diff --git a/.checkov.baseline b/.checkov.baseline index 19875f2e3..796f224f6 100644 --- a/.checkov.baseline +++ b/.checkov.baseline @@ -613,6 +613,25 @@ } ] }, + { + "file": "/checkov_pipeline_synth.json", + "findings": [ + { + "resource": "AWS::IAM::Role.PipelineRoleDCFDBB91", + "check_ids": [ + "CKV_AWS_107", + "CKV_AWS_108", + "CKV_AWS_111" + ] + }, + { + "resource": "AWS::S3::Bucket.thistableartifactsbucketDB1C8C64", + "check_ids": [ + "CKV_AWS_18" + ] + } + ] + }, { "file": "/frontend/docker/prod/Dockerfile", "findings": [ diff --git a/backend/dataall/core/environment/api/resolvers.py b/backend/dataall/core/environment/api/resolvers.py index 2f3301c74..01a3c1bde 100644 --- a/backend/dataall/core/environment/api/resolvers.py +++ b/backend/dataall/core/environment/api/resolvers.py @@ -14,7 +14,6 @@ from dataall.core.organizations.api.resolvers import Context, exceptions, get_organization_simplified - log = logging.getLogger() @@ -223,6 +222,7 @@ def generate_environment_access_token(context, source, environmentUri: str = Non def get_environment_stack(context: Context, source: Environment, **kwargs): return StackService.resolve_parent_obj_stack( targetUri=source.environmentUri, + targetType='environment', environmentUri=source.environmentUri, ) @@ -275,8 +275,7 @@ def resolve_environment(context, source, **kwargs): """Resolves the environment for a environmental resource""" if not source: return None - with context.engine.scoped_session() as session: - return EnvironmentService.get_environment_by_uri(session, source.environmentUri) + return EnvironmentService.find_environment_by_uri(uri=source.environmentUri) def resolve_parameters(context, source: Environment, **kwargs): diff --git a/backend/dataall/core/organizations/services/organization_service.py b/backend/dataall/core/organizations/services/organization_service.py index 696ae0881..cbc81a1ce 100644 --- a/backend/dataall/core/organizations/services/organization_service.py +++ b/backend/dataall/core/organizations/services/organization_service.py @@ -305,7 +305,7 @@ def resolve_organization_by_env(uri): context = get_context() with context.db_engine.scoped_session() as session: env = EnvironmentRepository.get_environment_by_uri(session, uri) - return OrganizationRepository.find_organization_by_uri(session, env.organizationUri) + return OrganizationService.get_organization(uri=env.organizationUri) @staticmethod @ResourcePolicyService.has_resource_permission(GET_ORGANIZATION) diff --git a/backend/dataall/core/stacks/services/stack_service.py b/backend/dataall/core/stacks/services/stack_service.py index d02d9ba48..46bacd555 100644 --- a/backend/dataall/core/stacks/services/stack_service.py +++ b/backend/dataall/core/stacks/services/stack_service.py @@ -65,9 +65,16 @@ def map_target_type_to_log_config_path(**kwargs): class StackService: @staticmethod - def resolve_parent_obj_stack(targetUri: str, environmentUri: str): + def resolve_parent_obj_stack(targetUri: str, targetType: str, environmentUri: str): context = get_context() with context.db_engine.scoped_session() as session: + ResourcePolicyService.check_user_resource_permission( + session=session, + username=context.username, + groups=context.groups, + resource_uri=targetUri, + permission_name=TargetType.get_resource_read_permission_name(targetType), + ) env: Environment = EnvironmentRepository.get_environment_by_uri(session, environmentUri) stack: Stack = StackRepository.find_stack_by_target_uri(session, target_uri=targetUri) if not stack: diff --git a/backend/dataall/core/vpc/services/vpc_service.py b/backend/dataall/core/vpc/services/vpc_service.py index 48c9c726c..fbdf8f092 100644 --- a/backend/dataall/core/vpc/services/vpc_service.py +++ b/backend/dataall/core/vpc/services/vpc_service.py @@ -1,5 +1,8 @@ +import logging + from dataall.base.context import get_context from dataall.base.db import exceptions +from dataall.base.db.exceptions import ResourceUnauthorized from dataall.core.permissions.services.group_policy_service import GroupPolicyService from dataall.core.environment.db.environment_repositories import EnvironmentRepository from dataall.core.activity.db.activity_models import Activity @@ -7,10 +10,12 @@ from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService from dataall.core.vpc.db.vpc_repositories import VpcRepository from dataall.core.vpc.db.vpc_models import Vpc -from dataall.core.permissions.services.network_permissions import NETWORK_ALL, DELETE_NETWORK +from dataall.core.permissions.services.network_permissions import NETWORK_ALL, DELETE_NETWORK, GET_NETWORK from dataall.core.permissions.services.environment_permissions import CREATE_NETWORK from dataall.core.permissions.services.tenant_permissions import MANAGE_ENVIRONMENTS +log = logging.getLogger(__name__) + def _session(): return get_context().db_engine.scoped_session() @@ -90,4 +95,19 @@ def delete_network(uri): @staticmethod def get_environment_networks(environment_uri): with _session() as session: - return VpcRepository.get_environment_networks(session=session, environment_uri=environment_uri) + nets = [] + all_nets = VpcRepository.get_environment_networks(session=session, environment_uri=environment_uri) + for net in all_nets: + try: + ResourcePolicyService.check_user_resource_permission( + session=session, + username=get_context().username, + groups=get_context().groups, + resource_uri=net.vpcUri, + permission_name=GET_NETWORK, + ) + except ResourceUnauthorized as exc: + log.info(exc) + else: + nets += net + return nets diff --git a/backend/dataall/modules/catalog/services/glossaries_service.py b/backend/dataall/modules/catalog/services/glossaries_service.py index d98f85bce..8a7db7d82 100644 --- a/backend/dataall/modules/catalog/services/glossaries_service.py +++ b/backend/dataall/modules/catalog/services/glossaries_service.py @@ -1,14 +1,13 @@ -from functools import wraps import logging +from functools import wraps from dataall.base.context import get_context from dataall.base.db import exceptions from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService - -from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository from dataall.modules.catalog.db.glossary_models import GlossaryNode -from dataall.modules.catalog.services.glossaries_permissions import MANAGE_GLOSSARIES +from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository from dataall.modules.catalog.indexers.registry import GlossaryRegistry +from dataall.modules.catalog.services.glossaries_permissions import MANAGE_GLOSSARIES logger = logging.getLogger(__name__) @@ -26,23 +25,29 @@ def wrapper(*args, **kwargs): uri = kwargs.get('uri') if not uri: raise KeyError(f"{f.__name__} doesn't have parameter uri.") - context = get_context() - with context.db_engine.scoped_session() as session: - node = GlossaryRepository.get_node(session=session, uri=uri) - while node.nodeType != 'G': - node = GlossaryRepository.get_node(session=session, uri=node.parentUri) - if node and (node.admin in context.groups): - return f(*args, **kwargs) - else: - raise exceptions.UnauthorizedOperation( - action='GLOSSARY MUTATION', - message=f'User {context.username} is not the admin of the glossary {node.label}.', - ) + GlossariesResourceAccess.check_owner(uri) + return f(*args, **kwargs) return wrapper return decorator + @staticmethod + def check_owner(uri): + context = get_context() + with context.db_engine.scoped_session() as session: + node = GlossaryRepository.get_node(session=session, uri=uri) + MAX_GLOSSARY_DEPTH = 10 + depth = 0 + while node.nodeType != 'G' and depth <= MAX_GLOSSARY_DEPTH: + node = GlossaryRepository.get_node(session=session, uri=node.parentUri) + depth += 1 + if not node or node.admin not in context.groups: + raise exceptions.UnauthorizedOperation( + action='GLOSSARY MUTATION', + message=f'User {context.username} is not the admin of the glossary {node.label}.', + ) + class GlossariesService: @staticmethod diff --git a/backend/dataall/modules/dashboards/__init__.py b/backend/dataall/modules/dashboards/__init__.py index ffbc8e92d..4d62dbb18 100644 --- a/backend/dataall/modules/dashboards/__init__.py +++ b/backend/dataall/modules/dashboards/__init__.py @@ -5,7 +5,6 @@ from dataall.base.loader import ImportMode, ModuleInterface - log = logging.getLogger(__name__) @@ -33,8 +32,9 @@ def __init__(self): from dataall.modules.catalog.indexers.registry import GlossaryRegistry, GlossaryDefinition from dataall.modules.vote.services.vote_service import add_vote_type from dataall.modules.dashboards.indexers.dashboard_indexer import DashboardIndexer + from dataall.modules.dashboards.services.dashboard_permissions import GET_DASHBOARD - FeedRegistry.register(FeedDefinition('Dashboard', Dashboard)) + FeedRegistry.register(FeedDefinition('Dashboard', Dashboard, GET_DASHBOARD)) GlossaryRegistry.register( GlossaryDefinition( @@ -42,7 +42,7 @@ def __init__(self): ) ) - add_vote_type('dashboard', DashboardIndexer) + add_vote_type('dashboard', DashboardIndexer, GET_DASHBOARD) EnvironmentResourceManager.register(DashboardRepository()) log.info('Dashboard API has been loaded') diff --git a/backend/dataall/modules/dashboards/api/resolvers.py b/backend/dataall/modules/dashboards/api/resolvers.py index 7a9bbe133..8f703419d 100644 --- a/backend/dataall/modules/dashboards/api/resolvers.py +++ b/backend/dataall/modules/dashboards/api/resolvers.py @@ -48,6 +48,12 @@ def get_dashboard(context: Context, source, dashboardUri: str = None): return DashboardService.get_dashboard(uri=dashboardUri) +def get_dashboard_restricted_information(context: Context, source: Dashboard): + if not source: + return None + return DashboardService.get_dashboard_restricted_information(uri=source.dashboardUri, dashboard=source) + + def resolve_user_role(context: Context, source: Dashboard): if context.username and source.owner == context.username: return DashboardRole.Creator.value diff --git a/backend/dataall/modules/dashboards/api/types.py b/backend/dataall/modules/dashboards/api/types.py index 857cf9333..9e5405891 100644 --- a/backend/dataall/modules/dashboards/api/types.py +++ b/backend/dataall/modules/dashboards/api/types.py @@ -1,7 +1,7 @@ from dataall.base.api import gql from dataall.modules.dashboards.api.resolvers import ( DashboardRole, - get_dashboard_organization, + get_dashboard_restricted_information, resolve_glossary_terms, resolve_upvotes, resolve_user_role, @@ -9,6 +9,11 @@ from dataall.core.environment.api.resolvers import resolve_environment +DashboardRestrictedInformation = gql.ObjectType( + name='DashboardRestrictedInformation', + fields=[gql.Field('AwsAccountId', type=gql.String), gql.Field('region', type=gql.String)], +) + Dashboard = gql.ObjectType( name='Dashboard', fields=[ @@ -19,10 +24,14 @@ gql.Field('DashboardId', type=gql.String), gql.Field('tags', type=gql.ArrayType(gql.String)), gql.Field('created', type=gql.String), - gql.Field('AwsAccountId', type=gql.String), gql.Field('updated', type=gql.String), gql.Field('owner', type=gql.String), gql.Field('SamlGroupName', type=gql.String), + gql.Field( + 'restricted', + type=DashboardRestrictedInformation, + resolver=get_dashboard_restricted_information, + ), gql.Field( 'environment', type=gql.Ref('EnvironmentSimplified'), diff --git a/backend/dataall/modules/dashboards/services/dashboard_quicksight_service.py b/backend/dataall/modules/dashboards/services/dashboard_quicksight_service.py index 67edc6a19..73a27e9f7 100644 --- a/backend/dataall/modules/dashboards/services/dashboard_quicksight_service.py +++ b/backend/dataall/modules/dashboards/services/dashboard_quicksight_service.py @@ -3,17 +3,16 @@ from dataall.base.aws.parameter_store import ParameterStoreManager from dataall.base.aws.sts import SessionHelper from dataall.base.context import get_context -from dataall.core.environment.services.environment_service import EnvironmentService -from dataall.core.permissions.db.tenant.tenant_policy_repositories import TenantPolicyRepository from dataall.base.db.exceptions import UnauthorizedOperation, TenantUnauthorized, AWSResourceNotFound -from dataall.core.permissions.services.tenant_permissions import TENANT_ALL +from dataall.base.utils import Parameter +from dataall.core.environment.services.environment_service import EnvironmentService from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService -from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService -from dataall.modules.dashboards.db.dashboard_repositories import DashboardRepository -from dataall.modules.dashboards.db.dashboard_models import Dashboard +from dataall.core.permissions.services.tenant_permissions import TENANT_ALL +from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService, TenantPolicyValidationService from dataall.modules.dashboards.aws.dashboard_quicksight_client import DashboardQuicksightClient +from dataall.modules.dashboards.db.dashboard_models import Dashboard +from dataall.modules.dashboards.db.dashboard_repositories import DashboardRepository from dataall.modules.dashboards.services.dashboard_permissions import GET_DASHBOARD, CREATE_DASHBOARD, MANAGE_DASHBOARDS -from dataall.base.utils import Parameter class DashboardQuicksightService: @@ -128,7 +127,7 @@ def get_quicksight_reader_session(cls, dashboard_uri): @staticmethod def _check_user_must_be_admin(): context = get_context() - admin = TenantPolicyRepository.is_tenant_admin(context.groups) + admin = TenantPolicyValidationService.is_tenant_admin(context.groups) if not admin: raise TenantUnauthorized( diff --git a/backend/dataall/modules/dashboards/services/dashboard_service.py b/backend/dataall/modules/dashboards/services/dashboard_service.py index 34d6c3a34..30c205e0f 100644 --- a/backend/dataall/modules/dashboards/services/dashboard_service.py +++ b/backend/dataall/modules/dashboards/services/dashboard_service.py @@ -25,11 +25,15 @@ class DashboardService: """Service that serves request related to dashboard""" @staticmethod - @ResourcePolicyService.has_resource_permission(GET_DASHBOARD) def get_dashboard(uri: str) -> Dashboard: with get_context().db_engine.scoped_session() as session: return DashboardRepository.get_dashboard_by_uri(session, uri) + @staticmethod + @ResourcePolicyService.has_resource_permission(GET_DASHBOARD) + def get_dashboard_restricted_information(uri: str, dashboard: Dashboard): + return dashboard + @staticmethod @TenantPolicyService.has_tenant_permission(MANAGE_DASHBOARDS) @ResourcePolicyService.has_resource_permission(CREATE_DASHBOARD) diff --git a/backend/dataall/modules/datapipelines/__init__.py b/backend/dataall/modules/datapipelines/__init__.py index 7b1a56334..171a6a311 100644 --- a/backend/dataall/modules/datapipelines/__init__.py +++ b/backend/dataall/modules/datapipelines/__init__.py @@ -35,7 +35,7 @@ def __init__(self): ) import dataall.modules.datapipelines.api - FeedRegistry.register(FeedDefinition('DataPipeline', DataPipeline)) + FeedRegistry.register(FeedDefinition('DataPipeline', DataPipeline, GET_PIPELINE)) TargetType('pipeline', GET_PIPELINE, UPDATE_PIPELINE, MANAGE_PIPELINES) TargetType('cdkpipeline', GET_PIPELINE, UPDATE_PIPELINE, MANAGE_PIPELINES) diff --git a/backend/dataall/modules/datapipelines/api/resolvers.py b/backend/dataall/modules/datapipelines/api/resolvers.py index 3c2bde886..47e676816 100644 --- a/backend/dataall/modules/datapipelines/api/resolvers.py +++ b/backend/dataall/modules/datapipelines/api/resolvers.py @@ -105,5 +105,6 @@ def resolve_stack(context, source: DataPipeline, **kwargs): return None return StackService.resolve_parent_obj_stack( targetUri=source.DataPipelineUri, + targetType='pipeline', environmentUri=source.environmentUri, ) diff --git a/backend/dataall/modules/datapipelines/services/datapipelines_service.py b/backend/dataall/modules/datapipelines/services/datapipelines_service.py index de277d20d..ed010ef9e 100644 --- a/backend/dataall/modules/datapipelines/services/datapipelines_service.py +++ b/backend/dataall/modules/datapipelines/services/datapipelines_service.py @@ -3,8 +3,9 @@ from dataall.base.aws.sts import SessionHelper from dataall.base.context import get_context -from dataall.core.permissions.services.group_policy_service import GroupPolicyService +from dataall.base.db import exceptions from dataall.core.environment.services.environment_service import EnvironmentService +from dataall.core.permissions.services.group_policy_service import GroupPolicyService from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService from dataall.core.permissions.services.tenant_policy_service import TenantPolicyService from dataall.core.stacks.db.keyvaluetag_repositories import KeyValueTagRepository @@ -12,7 +13,6 @@ from dataall.core.stacks.services.stack_service import StackService from dataall.core.tasks.db.task_models import Task from dataall.core.tasks.service_handlers import Worker -from dataall.base.db import exceptions from dataall.modules.datapipelines.db.datapipelines_models import DataPipeline, DataPipelineEnvironment from dataall.modules.datapipelines.db.datapipelines_repositories import DatapipelinesRepository from dataall.modules.datapipelines.services.datapipelines_permissions import ( @@ -25,7 +25,6 @@ UPDATE_PIPELINE, ) - logger = logging.getLogger(__name__) @@ -34,6 +33,10 @@ def _session(): class DataPipelineService: + @staticmethod + def _get_pipeline_uri_from_env_uri(session, envPipelineUri): + return DatapipelinesRepository.get_pipeline_environment_by_uri(session, envPipelineUri).pipelineUri + @staticmethod @TenantPolicyService.has_tenant_permission(MANAGE_PIPELINES) @ResourcePolicyService.has_resource_permission(CREATE_PIPELINE) @@ -255,6 +258,9 @@ def _delete_repository(target_uri, accountid, cdk_role_arn, region, repo_name): @staticmethod @TenantPolicyService.has_tenant_permission(MANAGE_PIPELINES) + @ResourcePolicyService.has_resource_permission( + UPDATE_PIPELINE, param_name='envPipelineUri', parent_resource=_get_pipeline_uri_from_env_uri + ) def delete_pipeline_environment(envPipelineUri: str): with _session() as session: DatapipelinesRepository.delete_pipeline_environment(session=session, envPipelineUri=envPipelineUri) diff --git a/backend/dataall/modules/datasets_base/api/resolvers.py b/backend/dataall/modules/datasets_base/api/resolvers.py index 73f6539c7..017256ae6 100644 --- a/backend/dataall/modules/datasets_base/api/resolvers.py +++ b/backend/dataall/modules/datasets_base/api/resolvers.py @@ -58,8 +58,7 @@ def get_dataset_organization(context, source: DatasetBase, **kwargs): def get_dataset_environment(context, source: DatasetBase, **kwargs): if not source: return None - with context.engine.scoped_session() as session: - return EnvironmentService.get_environment_by_uri(session, source.environmentUri) + return EnvironmentService.find_environment_by_uri(uri=source.environmentUri) def get_dataset_owners_group(context, source: DatasetBase, **kwargs): @@ -79,5 +78,6 @@ def resolve_dataset_stack(context: Context, source: DatasetBase, **kwargs): return None return StackService.resolve_parent_obj_stack( targetUri=source.datasetUri, + targetType='dataset', environmentUri=source.environmentUri, ) diff --git a/backend/dataall/modules/feed/api/registry.py b/backend/dataall/modules/feed/api/registry.py index 3fe72f245..8db914263 100644 --- a/backend/dataall/modules/feed/api/registry.py +++ b/backend/dataall/modules/feed/api/registry.py @@ -10,6 +10,7 @@ class FeedDefinition: target_type: str model: Type[Resource] + permission: str class FeedRegistry(UnionTypeRegistry): @@ -25,6 +26,10 @@ def register(cls, definition: FeedDefinition): def find_model(cls, target_type: str): return cls._DEFINITIONS[target_type].model + @classmethod + def find_permission(cls, target_type: str): + return cls._DEFINITIONS[target_type].permission + @classmethod def find_target(cls, obj: Resource): for target_type, definition in cls._DEFINITIONS.items(): diff --git a/backend/dataall/modules/feed/api/resolvers.py b/backend/dataall/modules/feed/api/resolvers.py index a3bcca622..e971d90bf 100644 --- a/backend/dataall/modules/feed/api/resolvers.py +++ b/backend/dataall/modules/feed/api/resolvers.py @@ -43,4 +43,4 @@ def resolve_feed_messages(context: Context, source: Feed, filter: dict = None): _required_uri(source.targetUri) if not filter: filter = {} - return FeedService.list_feed_messages(targetUri=source.targetUri, filter=filter) + return FeedService.list_feed_messages(targetUri=source.targetUri, targetType=source.targetType, filter=filter) diff --git a/backend/dataall/modules/feed/services/feed_service.py b/backend/dataall/modules/feed/services/feed_service.py index 364b2a575..69d271186 100644 --- a/backend/dataall/modules/feed/services/feed_service.py +++ b/backend/dataall/modules/feed/services/feed_service.py @@ -6,8 +6,10 @@ import logging from dataall.base.context import get_context +from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService from dataall.modules.feed.db.feed_models import FeedMessage from dataall.modules.feed.db.feed_repository import FeedRepository +from dataall.modules.feed.api.registry import FeedRegistry logger = logging.getLogger(__name__) @@ -27,10 +29,6 @@ def targetType(self): return self._targetType -def _session(): - return get_context().db_engine.scoped_session() - - class FeedService: """ Encapsulate the logic of interactions with Feeds. @@ -41,6 +39,15 @@ def get_feed( targetUri: str = None, targetType: str = None, ) -> Feed: + context = get_context() + with context.db_engine.scoped_session() as session: + ResourcePolicyService.check_user_resource_permission( + session=session, + username=context.username, + groups=context.groups, + resource_uri=targetUri, + permission_name=FeedRegistry.find_permission(target_type=targetType), + ) return Feed(targetUri=targetUri, targetType=targetType) @staticmethod @@ -49,17 +56,33 @@ def post_feed_message( targetType: str = None, content: str = None, ): - with _session() as session: + context = get_context() + with context.db_engine.scoped_session() as session: + ResourcePolicyService.check_user_resource_permission( + session=session, + username=context.username, + groups=context.groups, + resource_uri=targetUri, + permission_name=FeedRegistry.find_permission(target_type=targetType), + ) m = FeedMessage( targetUri=targetUri, targetType=targetType, - creator=get_context().username, + creator=context.username, content=content, ) session.add(m) return m @staticmethod - def list_feed_messages(targetUri: str, filter: dict = None): - with _session() as session: + def list_feed_messages(targetUri: str, targetType: str, filter: dict = None): + context = get_context() + with context.db_engine.scoped_session() as session: + ResourcePolicyService.check_user_resource_permission( + session=session, + username=context.username, + groups=context.groups, + resource_uri=targetUri, + permission_name=FeedRegistry.find_permission(target_type=targetType), + ) return FeedRepository(session).paginated_feed_messages(uri=targetUri, filter=filter) diff --git a/backend/dataall/modules/mlstudio/api/resolvers.py b/backend/dataall/modules/mlstudio/api/resolvers.py index e38d72ae6..6d4e07f8b 100644 --- a/backend/dataall/modules/mlstudio/api/resolvers.py +++ b/backend/dataall/modules/mlstudio/api/resolvers.py @@ -122,6 +122,7 @@ def resolve_sagemaker_studio_user_stack(context: Context, source: SagemakerStudi return None return StackService.resolve_parent_obj_stack( targetUri=source.sagemakerStudioUserUri, + targetType='mlstudio', environmentUri=source.environmentUri, ) diff --git a/backend/dataall/modules/notebooks/api/resolvers.py b/backend/dataall/modules/notebooks/api/resolvers.py index de6235305..e7e111fc3 100644 --- a/backend/dataall/modules/notebooks/api/resolvers.py +++ b/backend/dataall/modules/notebooks/api/resolvers.py @@ -90,6 +90,7 @@ def resolve_notebook_stack(context: Context, source: SagemakerNotebook, **kwargs return None return StackService.resolve_parent_obj_stack( targetUri=source.notebookUri, + targetType='notebook', environmentUri=source.environmentUri, ) diff --git a/backend/dataall/modules/redshift_datasets/__init__.py b/backend/dataall/modules/redshift_datasets/__init__.py index cd9e73f68..ff92c0d74 100644 --- a/backend/dataall/modules/redshift_datasets/__init__.py +++ b/backend/dataall/modules/redshift_datasets/__init__.py @@ -51,11 +51,16 @@ def __init__(self): FEED_REDSHIFT_DATASET_TABLE_NAME, VOTE_REDSHIFT_DATASET_NAME, ) - + from dataall.modules.redshift_datasets.services.redshift_dataset_permissions import ( + GET_REDSHIFT_DATASET, + GET_REDSHIFT_DATASET_TABLE, + ) import dataall.modules.redshift_datasets.api - FeedRegistry.register(FeedDefinition(FEED_REDSHIFT_DATASET_TABLE_NAME, RedshiftTable)) - FeedRegistry.register(FeedDefinition(FEED_REDSHIFT_DATASET_NAME, RedshiftDataset)) + FeedRegistry.register( + FeedDefinition(FEED_REDSHIFT_DATASET_TABLE_NAME, RedshiftTable, GET_REDSHIFT_DATASET_TABLE) + ) + FeedRegistry.register(FeedDefinition(FEED_REDSHIFT_DATASET_NAME, RedshiftDataset, GET_REDSHIFT_DATASET)) GlossaryRegistry.register( GlossaryDefinition( @@ -75,7 +80,7 @@ def __init__(self): ) ) - add_vote_type(VOTE_REDSHIFT_DATASET_NAME, DatasetIndexer) + add_vote_type(VOTE_REDSHIFT_DATASET_NAME, DatasetIndexer, GET_REDSHIFT_DATASET) EnvironmentResourceManager.register(RedshiftDatasetEnvironmentResource()) EnvironmentResourceManager.register(RedshiftConnectionEnvironmentResource()) diff --git a/backend/dataall/modules/redshift_datasets/api/datasets/resolvers.py b/backend/dataall/modules/redshift_datasets/api/datasets/resolvers.py index e95480351..aca1b2e79 100644 --- a/backend/dataall/modules/redshift_datasets/api/datasets/resolvers.py +++ b/backend/dataall/modules/redshift_datasets/api/datasets/resolvers.py @@ -87,8 +87,7 @@ def resolve_dataset_environment( ): # TODO- duplicated with S3 datasets - follow-up PR if not source: return None - with context.engine.scoped_session() as session: - return EnvironmentService.get_environment_by_uri(session, source.environmentUri) + return EnvironmentService.find_environment_by_uri(uri=source.environmentUri) def resolve_dataset_owners_group( diff --git a/backend/dataall/modules/s3_datasets/__init__.py b/backend/dataall/modules/s3_datasets/__init__.py index dbd4f458c..e8150e1b7 100644 --- a/backend/dataall/modules/s3_datasets/__init__.py +++ b/backend/dataall/modules/s3_datasets/__init__.py @@ -44,14 +44,16 @@ def __init__(self): from dataall.modules.s3_datasets.services.dataset_permissions import ( GET_DATASET, UPDATE_DATASET, + GET_DATASET_TABLE, + GET_DATASET_FOLDER, MANAGE_DATASETS, ) from dataall.modules.s3_datasets.db.dataset_repositories import DatasetRepository from dataall.modules.s3_datasets.db.dataset_models import DatasetStorageLocation, DatasetTable, S3Dataset - FeedRegistry.register(FeedDefinition('DatasetStorageLocation', DatasetStorageLocation)) - FeedRegistry.register(FeedDefinition('DatasetTable', DatasetTable)) - FeedRegistry.register(FeedDefinition('Dataset', S3Dataset)) + FeedRegistry.register(FeedDefinition('DatasetStorageLocation', DatasetStorageLocation, GET_DATASET_FOLDER)) + FeedRegistry.register(FeedDefinition('DatasetTable', DatasetTable, GET_DATASET_TABLE)) + FeedRegistry.register(FeedDefinition('Dataset', S3Dataset, GET_DATASET)) GlossaryRegistry.register( GlossaryDefinition( @@ -75,7 +77,7 @@ def __init__(self): ) ) - add_vote_type('dataset', DatasetIndexer) + add_vote_type('dataset', DatasetIndexer, GET_DATASET) TargetType('dataset', GET_DATASET, UPDATE_DATASET, MANAGE_DATASETS) diff --git a/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py b/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py index 56cb1c633..db2fad2df 100644 --- a/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py +++ b/backend/dataall/modules/s3_datasets/api/dataset/resolvers.py @@ -85,8 +85,7 @@ def get_dataset_organization(context, source: S3Dataset, **kwargs): def get_dataset_environment(context, source: S3Dataset, **kwargs): if not source: return None - with context.engine.scoped_session() as session: - return EnvironmentService.get_environment_by_uri(session, source.environmentUri) + return EnvironmentService.find_environment_by_uri(uri=source.environmentUri) def get_dataset_owners_group(context, source: S3Dataset, **kwargs): @@ -139,6 +138,7 @@ def resolve_dataset_stack(context: Context, source: S3Dataset, **kwargs): return None return StackService.resolve_parent_obj_stack( targetUri=source.datasetUri, + targetType='dataset', environmentUri=source.environmentUri, ) diff --git a/backend/dataall/modules/s3_datasets/api/storage_location/resolvers.py b/backend/dataall/modules/s3_datasets/api/storage_location/resolvers.py index 4fa38c918..928d0d4f8 100644 --- a/backend/dataall/modules/s3_datasets/api/storage_location/resolvers.py +++ b/backend/dataall/modules/s3_datasets/api/storage_location/resolvers.py @@ -1,9 +1,10 @@ from dataall.base.api.context import Context -from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository from dataall.base.db.exceptions import RequiredParameter from dataall.base.feature_toggle_checker import is_feature_enabled +from dataall.modules.catalog.db.glossary_repositories import GlossaryRepository +from dataall.modules.s3_datasets.db.dataset_models import DatasetStorageLocation from dataall.modules.s3_datasets.services.dataset_location_service import DatasetLocationService -from dataall.modules.s3_datasets.db.dataset_models import DatasetStorageLocation, S3Dataset +from dataall.modules.s3_datasets.services.dataset_service import DatasetService def _validate_input(input: dict): @@ -46,9 +47,7 @@ def remove_storage_location(context, source, locationUri: str = None): def resolve_dataset(context, source: DatasetStorageLocation, **kwargs): if not source: return None - with context.engine.scoped_session() as session: - d = session.query(S3Dataset).get(source.datasetUri) - return d + return DatasetService.find_dataset(uri=source.datasetUri) def get_folder_restricted_information(context: Context, source: DatasetStorageLocation, **kwargs): diff --git a/backend/dataall/modules/s3_datasets/services/dataset_service.py b/backend/dataall/modules/s3_datasets/services/dataset_service.py index 5efb1aa9d..67bef9581 100644 --- a/backend/dataall/modules/s3_datasets/services/dataset_service.py +++ b/backend/dataall/modules/s3_datasets/services/dataset_service.py @@ -38,6 +38,7 @@ DATASET_ALL, DATASET_READ, IMPORT_DATASET, + GET_DATASET, DATASET_TABLE_ALL, GET_DATASET, ) @@ -243,6 +244,11 @@ def get_dataset(uri): dataset.userRoleForDataset = DatasetRole.Admin.value return dataset + @classmethod + @ResourcePolicyService.has_resource_permission(GET_DATASET) + def find_dataset(cls, uri): + return DatasetService.get_dataset(uri) + @staticmethod @TenantPolicyService.has_tenant_permission(MANAGE_DATASETS) @ResourcePolicyService.has_resource_permission(CREDENTIALS_DATASET) diff --git a/backend/dataall/modules/s3_datasets_shares/api/resolvers.py b/backend/dataall/modules/s3_datasets_shares/api/resolvers.py index e2525202a..737b61fd6 100644 --- a/backend/dataall/modules/s3_datasets_shares/api/resolvers.py +++ b/backend/dataall/modules/s3_datasets_shares/api/resolvers.py @@ -5,7 +5,6 @@ from dataall.base.feature_toggle_checker import is_feature_enabled from dataall.modules.s3_datasets_shares.services.s3_share_service import S3ShareService - log = logging.getLogger(__name__) @@ -41,7 +40,7 @@ def validate_dataset_share_selector_input(data): def list_shared_tables_by_env_dataset(context: Context, source, datasetUri: str, envUri: str): - return S3ShareService.list_shared_tables_by_env_dataset(dataset_uri=datasetUri, uri=envUri) + return S3ShareService.list_shared_tables_by_env_dataset(uri=envUri, dataset_uri=datasetUri) @is_feature_enabled('modules.s3_datasets.features.aws_actions') diff --git a/backend/dataall/modules/vote/api/resolvers.py b/backend/dataall/modules/vote/api/resolvers.py index a35533159..609f3064c 100644 --- a/backend/dataall/modules/vote/api/resolvers.py +++ b/backend/dataall/modules/vote/api/resolvers.py @@ -1,9 +1,5 @@ -from typing import Dict, Type from dataall.base.db import exceptions from dataall.modules.vote.services.vote_service import VoteService -from dataall.modules.catalog.indexers.base_indexer import BaseIndexer - -_VOTE_TYPES: Dict[str, Type[BaseIndexer]] = {} def _required_param(param, name): diff --git a/backend/dataall/modules/vote/services/vote_service.py b/backend/dataall/modules/vote/services/vote_service.py index 380d9728d..e373c3b76 100644 --- a/backend/dataall/modules/vote/services/vote_service.py +++ b/backend/dataall/modules/vote/services/vote_service.py @@ -4,15 +4,21 @@ """ from typing import Dict, Type + from dataall.base.context import get_context +from dataall.core.permissions.services.resource_policy_service import ResourcePolicyService from dataall.modules.catalog.indexers.base_indexer import BaseIndexer from dataall.modules.vote.db.vote_repositories import VoteRepository -_VOTE_TYPES: Dict[str, Type[BaseIndexer]] = {} +_VOTE_TYPES: Dict[str, Dict[Type[BaseIndexer], str]] = {} + +def add_vote_type(target_type: str, indexer: Type[BaseIndexer], permission: str): + _VOTE_TYPES[target_type] = {'indexer': indexer, 'permission': permission} -def add_vote_type(target_type: str, indexer: Type[BaseIndexer]): - _VOTE_TYPES[target_type] = indexer + +def get_vote_type(target_type: str) -> dict[Type[BaseIndexer], str]: + return _VOTE_TYPES[target_type] def _session(): @@ -26,9 +32,18 @@ class VoteService: @staticmethod def upvote(targetUri: str, targetType: str, upvote: bool): - with _session() as session: + context = get_context() + target_type = get_vote_type(targetType) + with context.db_engine.scoped_session() as session: + ResourcePolicyService.check_user_resource_permission( + session=session, + username=context.username, + groups=context.groups, + resource_uri=targetUri, + permission_name=target_type.get('permission'), + ) vote = VoteRepository.upvote(session=session, targetUri=targetUri, targetType=targetType, upvote=upvote) - _VOTE_TYPES[vote.targetType].upsert(session, vote.targetUri) + target_type.get('indexer').upsert(session, vote.targetUri) return vote @staticmethod diff --git a/frontend/src/design/components/UpVoteButton.js b/frontend/src/design/components/UpVoteButton.js index 9b99e3913..e4ca2dd13 100644 --- a/frontend/src/design/components/UpVoteButton.js +++ b/frontend/src/design/components/UpVoteButton.js @@ -4,10 +4,11 @@ import * as PropTypes from 'prop-types'; import React from 'react'; export const UpVoteButton = (props) => { - const { upVoted, onClick, upVotes } = props; + const { upVoted, onClick, upVotes, disabled } = props; return ( + {isAdmin && ( + + )} + {isAdmin && ( + + )}