diff --git a/backend/dataall/api/Objects/Environment/queries.py b/backend/dataall/api/Objects/Environment/queries.py index acc8fefd7..9143cae1b 100644 --- a/backend/dataall/api/Objects/Environment/queries.py +++ b/backend/dataall/api/Objects/Environment/queries.py @@ -116,16 +116,6 @@ resolver=list_environment_invited_groups, ) -listEnvironmentNotInvitedGroups = gql.QueryField( - name='listEnvironmentNotInvitedGroups', - type=gql.Ref('GroupSearchResult'), - args=[ - gql.Argument(name='environmentUri', type=gql.NonNullableType(gql.String)), - gql.Argument(name='filter', type=gql.Ref('GroupFilter')), - ], - resolver=list_environment_not_invited_groups, -) - listEnvironmentGroups = gql.QueryField( name='listEnvironmentGroups', type=gql.Ref('GroupSearchResult'), diff --git a/backend/dataall/api/Objects/Environment/resolvers.py b/backend/dataall/api/Objects/Environment/resolvers.py index 22c5ec72d..c046fcf38 100644 --- a/backend/dataall/api/Objects/Environment/resolvers.py +++ b/backend/dataall/api/Objects/Environment/resolvers.py @@ -168,22 +168,6 @@ def list_environment_invited_groups( ) -def list_environment_not_invited_groups( - context: Context, source, environmentUri=None, filter=None -): - if filter is None: - filter = {} - with context.engine.scoped_session() as session: - return db.api.Environment.not_environment_groups( - session=session, - username=context.username, - groups=context.groups, - uri=environmentUri, - data=filter, - check_perm=True, - ) - - def list_environment_groups(context: Context, source, environmentUri=None, filter=None): if filter is None: filter = {} diff --git a/backend/dataall/api/Objects/Group/input_types.py b/backend/dataall/api/Objects/Group/input_types.py index 6ba08c2f0..9cccb014c 100644 --- a/backend/dataall/api/Objects/Group/input_types.py +++ b/backend/dataall/api/Objects/Group/input_types.py @@ -8,3 +8,11 @@ gql.Argument(name='pageSize', type=gql.Integer), ], ) + +CognitoGroupFilter = gql.InputType( + name='CognitoGroupFilter', + arguments=[ + gql.Argument(name='type', type=gql.String), + gql.Argument(name='uri', type=gql.String), + ], +) diff --git a/backend/dataall/api/Objects/Group/queries.py b/backend/dataall/api/Objects/Group/queries.py index 4d85f0fb4..5cbf484ff 100644 --- a/backend/dataall/api/Objects/Group/queries.py +++ b/backend/dataall/api/Objects/Group/queries.py @@ -1,5 +1,5 @@ from ... import gql -from .resolvers import get_group, list_datasets_owned_by_env_group, list_data_items_shared_with_env_group +from .resolvers import get_group, list_datasets_owned_by_env_group, list_data_items_shared_with_env_group, list_cognito_groups getGroup = gql.QueryField( name='getGroup', @@ -33,3 +33,12 @@ type=gql.Ref('EnvironmentPublishedItemSearchResults'), test_scope='Dataset', ) + +listCognitoGroups = gql.QueryField( + name='listCognitoGroups', + args=[ + gql.Argument(name='filter', type=gql.Ref('CognitoGroupFilter')), + ], + type=gql.ArrayType(gql.Ref('CognitoGroup')), + resolver=list_cognito_groups +) diff --git a/backend/dataall/api/Objects/Group/resolvers.py b/backend/dataall/api/Objects/Group/resolvers.py index bc9c97815..c17b052f7 100644 --- a/backend/dataall/api/Objects/Group/resolvers.py +++ b/backend/dataall/api/Objects/Group/resolvers.py @@ -1,7 +1,12 @@ +import os +import logging from .... import db from ....db import exceptions from ....db.models import Group -from ...constants import * +from ....aws.handlers.cognito import Cognito + + +log = logging.getLogger() def resolve_group_environment_permissions(context, source, environmentUri): @@ -70,3 +75,36 @@ def list_data_items_shared_with_env_group( data=filter, check_perm=True, ) + + +def list_cognito_groups(context, source, filter: dict = None): + envname = os.getenv('envname', 'local') + if envname in ['dkrcompose']: + return [{"groupName": 'Docker'}] + current_region = os.getenv('AWS_REGION', 'eu-west-1') + groups = Cognito.list_cognito_groups(envname=envname, region=current_region) + category, category_uri = filter.get("type"), filter.get("uri") + if category and category_uri: + if category == 'environment': + with context.engine.scoped_session() as session: + invited_groups = db.api.Environment.query_all_environment_groups( + session=session, + username=context.username, + groups=context.groups, + uri=category_uri, + filter=None, + ).all() + if category == 'organization': + with context.engine.scoped_session() as session: + organization = db.api.Organization.get_organization_by_uri(session, category_uri) + invited_groups = db.api.Organization.query_organization_groups( + session=session, + uri=organization.organizationUri, + filter=None, + ).all() + invited_group_uris = [item.groupUri for item in invited_groups] + res = [] + for group in groups: + if group['GroupName'] not in invited_group_uris: + res.append({"groupName": group['GroupName']}) + return res diff --git a/backend/dataall/api/Objects/Group/schema.py b/backend/dataall/api/Objects/Group/schema.py index 624f81db8..75f5350a5 100644 --- a/backend/dataall/api/Objects/Group/schema.py +++ b/backend/dataall/api/Objects/Group/schema.py @@ -46,3 +46,10 @@ gql.Field(name='nodes', type=gql.ArrayType(Group)), ], ) + +CognitoGroup = gql.ObjectType( + name='CognitoGroup', + fields=[ + gql.Field(name='groupName', type=gql.String), + ], +) diff --git a/backend/dataall/api/Objects/Organization/queries.py b/backend/dataall/api/Objects/Organization/queries.py index a9a2453c9..3f47e88b0 100644 --- a/backend/dataall/api/Objects/Organization/queries.py +++ b/backend/dataall/api/Objects/Organization/queries.py @@ -33,16 +33,6 @@ resolver=list_organization_invited_groups, ) -listOrganizationNotInvitedGroups = gql.QueryField( - name='listOrganizationNotInvitedGroups', - type=gql.Ref('GroupSearchResult'), - args=[ - gql.Argument(name='organizationUri', type=gql.NonNullableType(gql.String)), - gql.Argument(name='filter', type=gql.Ref('GroupFilter')), - ], - resolver=list_organization_not_invited_groups, -) - listOrganizationGroups = gql.QueryField( name='listOrganizationGroups', type=gql.Ref('GroupSearchResult'), diff --git a/backend/dataall/api/Objects/Organization/resolvers.py b/backend/dataall/api/Objects/Organization/resolvers.py index c7b55e699..f97f2849c 100644 --- a/backend/dataall/api/Objects/Organization/resolvers.py +++ b/backend/dataall/api/Objects/Organization/resolvers.py @@ -161,22 +161,6 @@ def list_organization_invited_groups( ) -def list_organization_not_invited_groups( - context: Context, source, organizationUri=None, filter=None -): - if filter is None: - filter = {} - with context.engine.scoped_session() as session: - return db.api.Organization.not_organization_groups( - session=session, - username=context.username, - groups=context.groups, - uri=organizationUri, - data=filter, - check_perm=True, - ) - - def list_organization_groups( context: Context, source, organizationUri=None, filter=None ): diff --git a/backend/dataall/aws/handlers/cognito.py b/backend/dataall/aws/handlers/cognito.py new file mode 100644 index 000000000..e3c9ea7c2 --- /dev/null +++ b/backend/dataall/aws/handlers/cognito.py @@ -0,0 +1,29 @@ +import logging +import boto3 + +from .sts import SessionHelper + + +log = logging.getLogger(__name__) + + +class Cognito: + @staticmethod + def client(account_id: str, region_name: str, client_type: str): + session = SessionHelper.remote_session(account_id) + return session.client(client_type, region_name=region_name) + + @staticmethod + def list_cognito_groups(envname: str, region: str): + try: + parameter_path = f'/dataall/{envname}/cognito/userpool' + ssm = boto3.client('ssm', region_name=region) + user_pool_id = ssm.get_parameter(Name=parameter_path)['Parameter']['Value'] + cognito = boto3.client('cognito-idp', region_name=region) + groups = cognito.list_groups(UserPoolId=user_pool_id)['Groups'] + except Exception as e: + log.error( + f'Failed to list groups of user pool {user_pool_id} due to {e}' + ) + else: + return groups diff --git a/backend/dataall/db/api/environment.py b/backend/dataall/db/api/environment.py index 79954c862..1e7fff7b3 100644 --- a/backend/dataall/db/api/environment.py +++ b/backend/dataall/db/api/environment.py @@ -630,25 +630,6 @@ def list_environment_invited_groups( session, username, groups, uri, data ).all() - @staticmethod - @has_resource_perm(permissions.LIST_ENVIRONMENT_GROUPS) - def not_environment_groups( - session, username, groups, uri, data=None, check_perm=None - ) -> dict: - environment_groups: [] = ( - session.query(models.EnvironmentGroup).filter( - and_( - models.EnvironmentGroup.groupUri.in_(groups), - models.EnvironmentGroup.environmentUri == uri, - ), - ) - ).all() - environment_groups = [g.groupUri for g in environment_groups] - not_invited_groups = [ - {'groupUri': group} for group in groups if group not in environment_groups - ] - return Page(not_invited_groups, 1, 1000, len(not_invited_groups)).to_dict() - @staticmethod def query_environment_datasets(session, username, groups, uri, filter) -> Query: query = session.query(models.Dataset).filter( diff --git a/backend/dataall/db/api/organization.py b/backend/dataall/db/api/organization.py index b095b08e9..979dd1095 100644 --- a/backend/dataall/db/api/organization.py +++ b/backend/dataall/db/api/organization.py @@ -336,22 +336,6 @@ def paginated_organization_invited_groups(session, username, groups, uri, data=N page_size=data.get('pageSize', 10), ).to_dict() - @staticmethod - @has_tenant_perm(permissions.MANAGE_ORGANIZATIONS) - @has_resource_perm(permissions.GET_ORGANIZATION) - def not_organization_groups(session, username, groups, uri, data=None, check_perm=False) -> dict: - org_groups: [] = ( - session.query(models.OrganizationGroup).filter( - and_( - models.OrganizationGroup.groupUri.in_(groups), - models.OrganizationGroup.organizationUri == uri, - ), - ) - ).all() - org_groups = [g.groupUri for g in org_groups] - not_invited_groups = [{'groupUri': group} for group in groups if group not in org_groups] - return Page(not_invited_groups, 1, 1000, len(not_invited_groups)).to_dict() - @staticmethod def count_organization_invited_groups(session, uri, group) -> int: groups = ( diff --git a/deploy/stacks/lambda_api.py b/deploy/stacks/lambda_api.py index e562ee376..300397446 100644 --- a/deploy/stacks/lambda_api.py +++ b/deploy/stacks/lambda_api.py @@ -240,6 +240,7 @@ def create_function_role(self, envname, resource_prefix, fn_name): 'xray:GetSamplingRules', 'xray:GetSamplingTargets', 'xray:GetSamplingStatisticSummaries', + 'cognito-idp:ListGroups' ], resources=['*'], ), diff --git a/frontend/src/api/Environment/listNotInvitedGroups.js b/frontend/src/api/Environment/listNotInvitedGroups.js deleted file mode 100644 index 88a33c245..000000000 --- a/frontend/src/api/Environment/listNotInvitedGroups.js +++ /dev/null @@ -1,30 +0,0 @@ -import { gql } from 'apollo-boost'; - -const listEnvironmentNotInvitedGroups = ({ filter, environmentUri }) => ({ - variables: { - environmentUri, - filter - }, - query: gql` - query listEnvironmentNotInvitedGroups( - $filter: GroupFilter - $environmentUri: String - ) { - listEnvironmentNotInvitedGroups( - environmentUri: $environmentUri - filter: $filter - ) { - count - page - pages - hasNext - hasPrevious - nodes { - groupUri - } - } - } - ` -}); - -export default listEnvironmentNotInvitedGroups; diff --git a/frontend/src/api/Groups/listCognitoGroups.js b/frontend/src/api/Groups/listCognitoGroups.js new file mode 100644 index 000000000..49d473a50 --- /dev/null +++ b/frontend/src/api/Groups/listCognitoGroups.js @@ -0,0 +1,20 @@ +import { gql } from 'apollo-boost'; + +const listCognitoGroups = ({ filter }) => ({ + variables: { + filter + }, + query: gql` + query listCognitoGroups ( + $filter: CognitoGroupFilter + ) { + listCognitoGroups ( + filter: $filter + ){ + groupName + } + } + ` +}); + +export default listCognitoGroups; diff --git a/frontend/src/api/Organization/listNotInvitedGroups.js b/frontend/src/api/Organization/listNotInvitedGroups.js deleted file mode 100644 index 72138643e..000000000 --- a/frontend/src/api/Organization/listNotInvitedGroups.js +++ /dev/null @@ -1,30 +0,0 @@ -import { gql } from 'apollo-boost'; - -const listOrganizationNotInvitedGroups = ({ filter, organizationUri }) => ({ - variables: { - organizationUri, - filter - }, - query: gql` - query listOrganizationNotInvitedGroups( - $filter: GroupFilter - $organizationUri: String - ) { - listOrganizationNotInvitedGroups( - organizationUri: $organizationUri - filter: $filter - ) { - count - page - pages - hasNext - hasPrevious - nodes { - groupUri - } - } - } - ` -}); - -export default listOrganizationNotInvitedGroups; diff --git a/frontend/src/views/Environments/EnvironmentTeamInviteForm.js b/frontend/src/views/Environments/EnvironmentTeamInviteForm.js index a9082d583..843351082 100644 --- a/frontend/src/views/Environments/EnvironmentTeamInviteForm.js +++ b/frontend/src/views/Environments/EnvironmentTeamInviteForm.js @@ -27,7 +27,7 @@ import { useDispatch } from '../../store'; import useClient from '../../hooks/useClient'; import listEnvironmentGroupInvitationPermissions from '../../api/Environment/listEnvironmentPermissions'; import inviteGroupOnEnvironment from '../../api/Environment/inviteGroup'; -import listEnvironmentNotInvitedGroups from '../../api/Environment/listNotInvitedGroups'; +import listCognitoGroups from '../../api/Groups/listCognitoGroups'; const EnvironmentTeamInviteForm = (props) => { const { environment, onClose, open, reloadTeams, ...other } = props; @@ -41,20 +41,21 @@ const EnvironmentTeamInviteForm = (props) => { const [groupOptions, setGroupOptions] = useState([]); const [permissionsError, setPermissionsError] = useState(null); + const filter = { + type: "environment", + uri: environment.environmentUri + } + const fetchGroups = useCallback(async () => { try { setLoadingGroups(true); - const response = await client.query( - listEnvironmentNotInvitedGroups({ - environmentUri: environment.environmentUri - }) - ); + const response = await client.query(listCognitoGroups({ filter })); if (!response.errors) { setGroupOptions( - response.data.listEnvironmentNotInvitedGroups.nodes.map((g) => ({ + response.data.listCognitoGroups.map((g) => ({ ...g, - value: g.groupUri, - label: g.groupUri + value: g.groupName, + label: g.groupName })) ); } else { diff --git a/frontend/src/views/Organizations/OrganizationTeamInviteForm.js b/frontend/src/views/Organizations/OrganizationTeamInviteForm.js index 3b93f822a..d0ea4b11b 100644 --- a/frontend/src/views/Organizations/OrganizationTeamInviteForm.js +++ b/frontend/src/views/Organizations/OrganizationTeamInviteForm.js @@ -25,7 +25,7 @@ import { SET_ERROR } from '../../store/errorReducer'; import { useDispatch } from '../../store'; import useClient from '../../hooks/useClient'; import inviteGroupToOrganization from '../../api/Organization/inviteGroup'; -import listOrganizationNotInvitedGroups from '../../api/Organization/listNotInvitedGroups'; +import listCognitoGroups from '../../api/Groups/listCognitoGroups'; const OrganizationTeamInviteForm = (props) => { const { organization, onClose, open, reloadTeams, ...other } = props; @@ -37,20 +37,21 @@ const OrganizationTeamInviteForm = (props) => { const [loadingGroups, setLoadingGroups] = useState(true); const [groupOptions, setGroupOptions] = useState([]); + const filter = { + type: "organization", + uri: organization.organizationUri + } + const fetchGroups = useCallback(async () => { try { setLoadingGroups(true); - const response = await client.query( - listOrganizationNotInvitedGroups({ - organizationUri: organization.organizationUri - }) - ); + const response = await client.query(listCognitoGroups({ filter })); if (!response.errors) { setGroupOptions( - response.data.listOrganizationNotInvitedGroups.nodes.map((g) => ({ + response.data.listCognitoGroups.map((g) => ({ ...g, - value: g.groupUri, - label: g.groupUri + value: g.groupName, + label: g.groupName })) ); } else { diff --git a/tests/api/test_environment.py b/tests/api/test_environment.py index c4dc64433..e961a445c 100644 --- a/tests/api/test_environment.py +++ b/tests/api/test_environment.py @@ -472,26 +472,6 @@ def test_group_invitation(db, client, env1, org1, group2, user, group3, group, d assert response.data.listEnvironmentInvitedGroups.count == 1 - response = client.query( - """ - query listEnvironmentNotInvitedGroups($environmentUri: String!, $filter:GroupFilter){ - listEnvironmentNotInvitedGroups(environmentUri:$environmentUri, filter:$filter){ - count - nodes{ - groupUri - name - } - } - } - """, - username=user.userName, - groups=[group.name, group2.name, group3.name], - environmentUri=env1.environmentUri, - filter={}, - ) - - assert response.data.listEnvironmentNotInvitedGroups.count == 1 - response = client.query( """ query listEnvironmentGroups($environmentUri: String!, $filter:GroupFilter){ @@ -618,26 +598,6 @@ def test_group_invitation(db, client, env1, org1, group2, user, group3, group, d assert response.data.listEnvironmentInvitedGroups.count == 0 - response = client.query( - """ - query listEnvironmentNotInvitedGroups($environmentUri: String!, $filter:GroupFilter){ - listEnvironmentNotInvitedGroups(environmentUri:$environmentUri, filter:$filter){ - count - nodes{ - groupUri - name - } - } - } - """, - username=user.userName, - groups=[group.name, group2.name, group3.name], - environmentUri=env1.environmentUri, - filter={}, - ) - - assert response.data.listEnvironmentNotInvitedGroups.count == 2 - response = client.query( """ query listEnvironmentGroups($environmentUri: String!, $filter:GroupFilter){ diff --git a/tests/api/test_group.py b/tests/api/test_group.py new file mode 100644 index 000000000..7cab78314 --- /dev/null +++ b/tests/api/test_group.py @@ -0,0 +1,45 @@ +import pytest + +import dataall +from dataall.db import permissions + + +@pytest.fixture(scope='module', autouse=True) +def org1(org, user, group, tenant): + org1 = org('testorg', user.userName, group.name) + yield org1 + + +@pytest.fixture(scope='module', autouse=True) +def env1(env, org1, user, group, tenant, module_mocker): + module_mocker.patch('requests.post', return_value=True) + module_mocker.patch( + 'dataall.api.Objects.Environment.resolvers.check_environment', return_value=True + ) + env1 = env(org1, 'dev', user.userName, group.name, '111111111111', 'eu-west-1') + yield env1 + + +def test_list_cognito_groups_env(client, env1, group, module_mocker): + module_mocker.patch( + 'dataall.aws.handlers.cognito.Cognito.list_cognito_groups', + return_value=[{"GroupName": 'cognitos'}, {"GroupName": 'testadmins'}], + ) + response = client.query( + """ + query listCognitoGroups ( + $filter: CognitoGroupFilter + ) { + listCognitoGroups ( + filter: $filter + ){ + groupName + } + } + """, + username='alice', + filter={'type': 'environment', 'uri': env1.environmentUri}, + ) + assert response.data.listCognitoGroups[0].groupName == 'cognitos' + + diff --git a/tests/api/test_organization.py b/tests/api/test_organization.py index 5a602037a..47a78bcda 100644 --- a/tests/api/test_organization.py +++ b/tests/api/test_organization.py @@ -246,26 +246,6 @@ def test_group_invitation(db, client, org1, group2, user, group3, group, dataset assert response.data.listOrganizationInvitedGroups.count == 1 - response = client.query( - """ - query listOrganizationNotInvitedGroups($organizationUri: String!, $filter:GroupFilter){ - listOrganizationNotInvitedGroups(organizationUri:$organizationUri, filter:$filter){ - count - nodes{ - groupUri - name - } - } - } - """, - username=user.userName, - groups=[group.name, group2.name, group3.name], - organizationUri=org1.organizationUri, - filter={}, - ) - - assert response.data.listOrganizationNotInvitedGroups.count == 1 - response = client.query( """ query listOrganizationGroups($organizationUri: String!, $filter:GroupFilter){ @@ -348,26 +328,6 @@ def test_group_invitation(db, client, org1, group2, user, group3, group, dataset assert response.data.listOrganizationInvitedGroups.count == 0 - response = client.query( - """ - query listOrganizationNotInvitedGroups($organizationUri: String!, $filter:GroupFilter){ - listOrganizationNotInvitedGroups(organizationUri:$organizationUri, filter:$filter){ - count - nodes{ - groupUri - name - } - } - } - """, - username=user.userName, - groups=[group.name, group2.name, group3.name], - organizationUri=org1.organizationUri, - filter={}, - ) - - assert response.data.listOrganizationNotInvitedGroups.count == 2 - response = client.query( """ query listOrganizationGroups($organizationUri: String!, $filter:GroupFilter){