Пример #1
0
    def test_remove_owner(self):
        # Add test_user1 and test_user2 as group owner
        graph_user1 = GraphUser(identifier=str(self.scim_user1.scim_id), display_name='Test User 1')
        graph_user2 = GraphUser(identifier=str(self.scim_user2.scim_id), display_name='Test User 2')
        self.scim_group1.owners = [graph_user1, graph_user2]
        self.app.scimapi_groupdb.save(self.scim_group1)

        # Check that test_user2 is an owner of scim_group1
        group = self.app.scimapi_groupdb.get_group_by_scim_id(str(self.scim_group1.scim_id))
        assert group.has_owner(self.scim_user2.scim_id) is True

        with self.session_cookie(self.browser, self.test_user.eppn) as client:
            with client.session_transaction() as sess:
                with self.app.test_request_context():
                    data = {
                        'group_identifier': str(self.scim_group1.scim_id),
                        'user_identifier': str(self.scim_user2.scim_id),
                        'role': 'owner',
                        'csrf_token': sess.get_csrf_token(),
                    }
                    response = client.post('/remove-user', data=json.dumps(data), content_type=self.content_type_json)
        self._check_success_response(response, type_='POST_GROUP_MANAGEMENT_REMOVE_USER_SUCCESS')
        payload = response.json.get('payload')
        assert 0 == len(payload['member_of'])
        assert 1 == len(payload['owner_of'])

        # Check that test_user2 is no longer a member of scim_group1
        group = self.app.scimapi_groupdb.get_group_by_scim_id(str(self.scim_group1.scim_id))
        assert group.has_owner(self.scim_user2.scim_id) is False
Пример #2
0
def accept_group_invitation(scim_user: ScimApiUser, scim_group: ScimApiGroup,
                            invite: GroupInviteState) -> None:
    graph_user = GraphUser(identifier=str(scim_user.scim_id),
                           display_name=invite.email_address)
    modified = False
    if invite.role == GroupRole.OWNER:
        if not is_owner(scim_user, scim_group.scim_id):
            scim_group.add_owner(graph_user)
            modified = True
    elif invite.role == GroupRole.MEMBER:
        if not is_member(scim_user, scim_group.scim_id):
            scim_group.add_member(graph_user)
            modified = True
    else:
        raise NotImplementedError(f'Unknown role: {invite.role}')

    if modified:
        if not current_app.scimapi_groupdb.save(scim_group):
            current_app.logger.error(
                f'Failed to save group with scim_id: {invite.group_scim_id}')
            raise EduIDDBError('Failed to save group')
        current_app.logger.info(
            f'Added user as {invite.role.value} to group with scim_id: {invite.group_scim_id}'
        )
    return None
Пример #3
0
    def test_invite_owner(self):
        # Add test user as group owner
        graph_user = GraphUser(
            identifier=str(self.scim_user1.scim_id), display_name=self.test_user.mail_addresses.primary.email
        )
        self.scim_group1.owners = [graph_user]
        self.app.scimapi_groupdb.save(self.scim_group1)

        # Invite test user 2 to the group as owner
        response = self._invite(
            group_scim_id=str(self.scim_group1.scim_id),
            inviter=self.test_user,
            invite_address=self.test_user2.mail_addresses.primary.email,
            role='owner',
        )
        payload = response.json.get('payload')
        outgoing = payload['outgoing']
        assert 1 == len(outgoing)
        for invite in outgoing:
            assert str(self.scim_group1.scim_id) == invite['group_identifier']
            assert 0 == len(invite['member_invites'])
            assert 1 == len(invite['owner_invites'])
        assert (
            self.app.invite_state_db.get_state(
                group_scim_id=str(self.scim_group1.scim_id),
                email_address=self.test_user2.mail_addresses.primary.email,
                role=GroupRole.OWNER,
            )
            is not None
        )
Пример #4
0
    def test_remove_last_owner(self):
        # Add test_user1 as group owner
        graph_user1 = GraphUser(identifier=str(self.scim_user1.scim_id), display_name='Test User 1')
        self.scim_group1.owners = [graph_user1]
        self.app.scimapi_groupdb.save(self.scim_group1)

        # Check that test_user1 is an owner of scim_group1
        group = self.app.scimapi_groupdb.get_group_by_scim_id(str(self.scim_group1.scim_id))
        found_owners = [owner for owner in group.graph.owners if owner.identifier == str(self.scim_user1.scim_id)]
        assert 1 == len(found_owners)

        with self.session_cookie(self.browser, self.test_user.eppn) as client:
            with client.session_transaction() as sess:
                with self.app.test_request_context():
                    data = {
                        'group_identifier': str(self.scim_group1.scim_id),
                        'user_identifier': str(self.scim_user1.scim_id),
                        'role': 'owner',
                        'csrf_token': sess.get_csrf_token(),
                    }
                    response = client.post('/remove-user', data=json.dumps(data), content_type=self.content_type_json)
        self._check_error_response(response, type_='POST_GROUP_MANAGEMENT_REMOVE_USER_FAIL')

        # Check that test_user1 is still owner of scim_group1
        group = self.app.scimapi_groupdb.get_group_by_scim_id(str(self.scim_group1.scim_id))
        found_owners = [owner for owner in group.graph.owners if owner.identifier == str(self.scim_user1.scim_id)]
        assert 1 == len(found_owners)
Пример #5
0
 def add_member(self, group: ScimApiGroup, member: Union[ScimApiUser,
                                                         ScimApiGroup],
                display_name: str) -> ScimApiGroup:
     if isinstance(member, ScimApiUser):
         member = GraphUser(identifier=str(member.scim_id),
                            display_name=display_name)
     elif isinstance(member, ScimApiGroup):
         member = GraphGroup(identifier=str(member.scim_id),
                             display_name=display_name)
     group.add_member(member)
     assert self.groupdb  # mypy doesn't know setUp will be called
     self.groupdb.save(group)
     return group
Пример #6
0
def create_group(user: User, display_name: str) -> FluxData:
    scim_user = get_or_create_scim_user_by_eppn(user.eppn)
    graph_user = GraphUser(identifier=str(scim_user.scim_id),
                           display_name=user.mail_addresses.primary.email)
    group = ScimApiGroup(display_name=display_name)
    group.owners = [graph_user]
    group.members = [graph_user]

    if not current_app.scimapi_groupdb.save(group):
        current_app.logger.error(
            f'Failed to create ScimApiGroup with scim_id: {group.scim_id}')
        return error_response(message=CommonMsg.temp_problem)

    current_app.logger.info(
        f'Created ScimApiGroup with scim_id: {group.scim_id}')
    current_app.stats.count(name='group_created')
    return get_groups()
Пример #7
0
    def test_delete_group_not_owner(self):
        # Add test user as group member
        graph_user = GraphUser(
            identifier=str(self.scim_user1.scim_id), display_name=self.test_user.mail_addresses.primary.email
        )
        self.scim_group1.members = [graph_user]
        self.app.scimapi_groupdb.save(self.scim_group1)

        with self.session_cookie(self.browser, self.test_user.eppn) as client:
            with client.session_transaction() as sess:
                with self.app.test_request_context():
                    data = {'group_identifier': str(self.scim_group1.scim_id), 'csrf_token': sess.get_csrf_token()}
                    response = client.post('/delete', data=json.dumps(data), content_type=self.content_type_json)
        self._check_error_response(
            response, type_='POST_GROUP_MANAGEMENT_DELETE_FAIL', msg=GroupManagementMsg.user_not_owner
        )
        assert self.app.scimapi_groupdb.group_exists(str(self.scim_group1.scim_id)) is True
Пример #8
0
    def test_get_groups(self):
        # Add test user as group member and owner
        graph_user = GraphUser(
            identifier=str(self.scim_user1.scim_id), display_name=self.test_user.mail_addresses.primary.email
        )
        self.scim_group1.members = [graph_user]
        self.scim_group1.owners = [graph_user]
        self.app.scimapi_groupdb.save(self.scim_group1)

        response = self.browser.get('/groups')
        assert response.status_code == 302  # Redirect to token service
        with self.session_cookie(self.browser, self.test_user.eppn) as client:
            response = client.get('/groups')
        self._check_success_response(response, type_='GET_GROUP_MANAGEMENT_GROUPS_SUCCESS')
        payload = response.json.get('payload')
        assert 1 == len(payload['member_of'])
        assert 1 == len(payload['owner_of'])
        assert 'Test Group 1' == payload['member_of'][0]['display_name']
        assert 'Test Group 1' == payload['owner_of'][0]['display_name']
Пример #9
0
    def test_delete_group_and_invites(self):
        # Add test user as group owner
        graph_user = GraphUser(
            identifier=str(self.scim_user1.scim_id), display_name=self.test_user.mail_addresses.primary.email
        )
        self.scim_group1.owners = [graph_user]
        self.app.scimapi_groupdb.save(self.scim_group1)

        self._invite_setup()
        assert 3 == len(self.app.invite_state_db.get_states_by_group_scim_id(str(self.scim_group1.scim_id)))

        with self.session_cookie(self.browser, self.test_user.eppn) as client:
            with client.session_transaction() as sess:
                with self.app.test_request_context():
                    data = {'group_identifier': str(self.scim_group1.scim_id), 'csrf_token': sess.get_csrf_token()}
                    response = client.post('/delete', data=json.dumps(data), content_type=self.content_type_json)
        self._check_success_response(response, type_='POST_GROUP_MANAGEMENT_DELETE_SUCCESS')

        assert self.app.scimapi_groupdb.group_exists(str(self.scim_group1.scim_id)) is False
        with self.assertRaises(DocumentDoesNotExist):
            self.app.invite_state_db.get_states_by_group_scim_id(str(self.scim_group1.scim_id))
Пример #10
0
    def test_delete_group(self):
        # Add test user as group owner of two groups
        graph_user = GraphUser(
            identifier=str(self.scim_user1.scim_id), display_name=self.test_user.mail_addresses.primary.email
        )
        self.scim_group1.owners = [graph_user]
        self.app.scimapi_groupdb.save(self.scim_group1)
        self.scim_group2.owners = [graph_user]
        self.app.scimapi_groupdb.save(self.scim_group2)

        with self.session_cookie(self.browser, self.test_user.eppn) as client:
            with client.session_transaction() as sess:
                with self.app.test_request_context():
                    data = {'group_identifier': str(self.scim_group1.scim_id), 'csrf_token': sess.get_csrf_token()}
                    response = client.post('/delete', data=json.dumps(data), content_type=self.content_type_json)
        self._check_success_response(response, type_='POST_GROUP_MANAGEMENT_DELETE_SUCCESS')
        payload = response.json.get('payload')
        assert 0 == len(payload['member_of'])
        assert 1 == len(payload['owner_of'])

        assert self.app.scimapi_groupdb.group_exists(str(self.scim_group2.scim_id)) is True
        assert self.app.scimapi_groupdb.group_exists(str(self.scim_group1.scim_id)) is False
Пример #11
0
    def _invite_setup(self):
        # Add test user as group owner of two groups
        graph_user = GraphUser(
            identifier=str(self.scim_user1.scim_id), display_name=self.test_user.mail_addresses.primary.email
        )
        self.scim_group1.owners = [graph_user]
        self.app.scimapi_groupdb.save(self.scim_group1)

        self.scim_group2.owners = [graph_user]
        self.app.scimapi_groupdb.save(self.scim_group2)

        # Invite test_user2 as owner and member of Test Group 1
        self._invite(
            group_scim_id=str(self.scim_group1.scim_id),
            inviter=self.test_user,
            invite_address=self.test_user2.mail_addresses.primary.email,
            role='member',
        )
        self._invite(
            group_scim_id=str(self.scim_group1.scim_id),
            inviter=self.test_user,
            invite_address=self.test_user2.mail_addresses.primary.email,
            role='owner',
        )
        # Invite test_user3 as member of Test Group 1
        self._invite(
            group_scim_id=str(self.scim_group1.scim_id),
            inviter=self.test_user,
            invite_address=self.test_user3.mail_addresses.primary.email,
            role='member',
        )
        # Invite test_user3 as member of Test Group 2
        self._invite(
            group_scim_id=str(self.scim_group2.scim_id),
            inviter=self.test_user,
            invite_address=self.test_user3.mail_addresses.primary.email,
            role='member',
        )
Пример #12
0
    def test_decline_invite_owner(self):
        # Add test user as group owner
        graph_user = GraphUser(
            identifier=str(self.scim_user1.scim_id), display_name=self.test_user.mail_addresses.primary.email
        )
        self.scim_group1.owners = [graph_user]
        self.app.scimapi_groupdb.save(self.scim_group1)

        # Invite test user 2 to the group as member
        self._invite(
            group_scim_id=str(self.scim_group1.scim_id),
            inviter=self.test_user,
            invite_address=self.test_user2.mail_addresses.primary.email,
            role='owner',
        )
        # Decline invite as test user 2
        response = self._decline_invite(
            group_scim_id=str(self.scim_group1.scim_id),
            invitee=self.test_user2,
            invite_address=self.test_user2.mail_addresses.primary.email,
            role='owner',
        )
        payload = response.json.get('payload')
        incoming = payload['incoming']
        assert 0 == len(incoming)

        with self.assertRaises(DocumentDoesNotExist):
            self.app.invite_state_db.get_state(
                group_scim_id=str(self.scim_group1.scim_id),
                email_address=self.test_user2.mail_addresses.primary.email,
                role=GroupRole.MEMBER,
            )
        scim_group = self.app.scimapi_groupdb.get_group_by_scim_id(str(self.scim_group1.scim_id))
        scim_user = self.app.scimapi_userdb.get_user_by_external_id(
            f'{self.test_user2.eppn}@{self.app.config.scim_external_id_scope}'
        )
        assert scim_group.has_owner(scim_user.scim_id) is False
Пример #13
0
    def test_get_all_data_privacy(self):
        # Add test user as group member and owner, add test user 2 as member
        graph_user1 = GraphUser(
            identifier=str(self.scim_user1.scim_id), display_name=self.test_user.mail_addresses.primary.email
        )
        graph_user2 = GraphUser(
            identifier=str(self.scim_user2.scim_id), display_name=self.test_user2.mail_addresses.primary.email
        )
        self.scim_group1.members = [graph_user1, graph_user2]
        self.scim_group1.owners = [graph_user1]
        self.app.scimapi_groupdb.save(self.scim_group1)

        # Invite test user 2 as owner
        self._invite(
            group_scim_id=str(self.scim_group1.scim_id),
            inviter=self.test_user,
            invite_address=self.test_user2.mail_addresses.primary.email,
            role='owner',
        )

        # Get all data as test user 1
        with self.session_cookie(self.browser, self.test_user.eppn) as client:
            response = client.get('/all-data')
        self._check_success_response(response, type_='GET_GROUP_MANAGEMENT_ALL_DATA_SUCCESS')
        payload = response.json.get('payload')
        # As member the user only see owners for a group
        assert normalised_data(
            [
                {
                    'display_name': 'Test Group 1',
                    'identifier': '00000000-0000-0000-0000-000000000002',
                    'members': [],
                    'owners': [
                        {'display_name': '*****@*****.**', 'identifier': '00000000-0000-0000-0000-000000000000'}
                    ],
                }
            ]
        ) == normalised_data(payload['member_of'])
        # As owner the user see both members and owners
        assert normalised_data(
            [
                {
                    'display_name': 'Test Group 1',
                    'identifier': '00000000-0000-0000-0000-000000000002',
                    'members': [
                        {'display_name': '*****@*****.**', 'identifier': '00000000-0000-0000-0000-000000000000'},
                        {
                            'display_name': '*****@*****.**',
                            'identifier': '00000000-0000-0000-0000-000000000001',
                        },
                    ],
                    'owners': [
                        {'display_name': '*****@*****.**', 'identifier': '00000000-0000-0000-0000-000000000000'}
                    ],
                }
            ]
        ) == normalised_data(payload['owner_of'])
        # As owner the user see your outgoing invites
        assert normalised_data(
            [
                {
                    'group_identifier': '00000000-0000-0000-0000-000000000002',
                    'member_invites': [],
                    'owner_invites': [{'email_address': '*****@*****.**'}],
                }
            ]
        ) == normalised_data(payload['outgoing'])
        # test user 1 does not have any incoming invites
        assert [] == normalised_data(payload['incoming'])

        # Get all data as test user 2
        with self.session_cookie(self.browser, self.test_user2.eppn) as client:
            response = client.get('/all-data')
        self._check_success_response(response, type_='GET_GROUP_MANAGEMENT_ALL_DATA_SUCCESS')
        payload = response.json.get('payload')
        # As member the user only see owners for a group
        assert normalised_data(
            [
                {
                    'display_name': 'Test Group 1',
                    'identifier': '00000000-0000-0000-0000-000000000002',
                    'members': [],
                    'owners': [
                        {'display_name': '*****@*****.**', 'identifier': '00000000-0000-0000-0000-000000000000'}
                    ],
                }
            ]
        ) == normalised_data(payload['member_of'])
        # test user 2 is not an owner of a group
        assert [] == payload['owner_of']
        # test user 2 does not have any outgoing invites
        assert [] == payload['outgoing']
        # as an invitee the user see incoming invites
        assert normalised_data(
            [
                {
                    'display_name': 'Test Group 1',
                    'email_address': '*****@*****.**',
                    'group_identifier': '00000000-0000-0000-0000-000000000002',
                    'owners': [
                        {'display_name': '*****@*****.**', 'identifier': '00000000-0000-0000-0000-000000000000'}
                    ],
                    'role': 'owner',
                }
            ]
        ) == normalised_data(payload['incoming'])
Пример #14
0
    def update_group(self, update_request: GroupUpdateRequest, db_group: ScimApiGroup) -> Tuple[ScimApiGroup, bool]:
        changed = False
        updated_members = set()
        logger.info(f'Updating group {str(db_group.scim_id)}')
        for this in update_request.members:
            if this.is_user:
                _member = db_group.graph.get_member_user(identifier=str(this.value))
                _new_member = None if _member else GraphUser(identifier=str(this.value), display_name=this.display)
            elif this.is_group:
                _member = db_group.graph.get_member_group(identifier=str(this.value))
                _new_member = None if _member else GraphGroup(identifier=str(this.value), display_name=this.display)
            else:
                raise ValueError(f"Don't recognise member {this}")

            # Add a new member
            if _new_member:
                updated_members.add(_new_member)
                logger.debug(f'Added new member: {_new_member}')
            # Update member attributes if they changed
            elif _member.display_name != this.display:
                logger.debug(f'Changed display name for existing member: {_member.display_name} -> {this.display}')
                _member = replace(_member, display_name=this.display)
                updated_members.add(_member)
            else:
                # no change, retain member as-is
                updated_members.add(_member)

        if db_group.graph.display_name != update_request.display_name:
            changed = True
            db_group.graph = replace(db_group.graph, display_name=update_request.display_name)
            logger.debug(
                f'Changed display name for group: {db_group.graph.display_name} -> {update_request.display_name}'
            )

        if db_group.external_id != update_request.external_id:
            changed = True
            db_group.external_id = update_request.external_id
            logger.debug(f'Changed external id for group: {db_group.external_id} -> {update_request.external_id}')

        # Check if there where new, changed or removed members
        if db_group.graph.members != updated_members:
            changed = True
            db_group.graph = replace(db_group.graph, members=updated_members)
            logger.debug(f'Old members: {db_group.graph.members}')
            logger.debug(f'New members: {updated_members}')

        _sg_ext = GroupExtensions(data=update_request.nutid_group_v1.data)
        if db_group.extensions != _sg_ext:
            changed = True
            db_group.extensions = _sg_ext
            logger.debug(f'Old extensions: {db_group.extensions}')
            logger.debug(f'New extensions: {_sg_ext}')

        if changed:
            logger.info(f'Group {str(db_group.scim_id)} changed. Saving.')
            if self.save(db_group):
                logger.info(f'Group {str(db_group.scim_id)} saved.')
            else:
                logger.warning(f'Update of group {db_group} probably failed')

        return db_group, changed