def test_sync_success_no_existing_remote_assignments(self):
        syncer = RBACRemoteGroupToRoleSyncer()
        user_db = self.users['user_1']

        # Create mock mapping which maps CN=stormers,OU=groups,DC=stackstorm,DC=net
        # to "mock_remote_role_3" and "mock_remote_role_4"
        rbac_service.create_group_to_role_map(
            group='CN=stormers,OU=groups,DC=stackstorm,DC=net',
            roles=['mock_remote_role_3', 'mock_remote_role_4'],
            source='mappings/stormers.yaml')

        # Verify initial state
        role_dbs = rbac_service.get_roles_for_user(user_db=user_db,
                                                   include_remote=True)
        self.assertEqual(len(role_dbs), 2)
        self.assertEqual(role_dbs[0], self.roles['mock_local_role_1'])
        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=self.users['user_1'])

        groups = [
            'CN=stormers,OU=groups,DC=stackstorm,DC=net',
            'CN=testers,OU=groups,DC=stackstorm,DC=net',
            # We repeat the same group to validate that repated groups are correctly de-duplicated
            'CN=stormers,OU=groups,DC=stackstorm,DC=net',
        ]
        result = syncer.sync(user_db=self.users['user_1'], groups=groups)
        created_role_assignment_dbs = result[0]
        removed_role_assignment_dbs = result[1]
        self.assertEqual(len(created_role_assignment_dbs), 2)
        self.assertEqual(created_role_assignment_dbs[0].role,
                         'mock_remote_role_3')
        self.assertEqual(created_role_assignment_dbs[1].role,
                         'mock_remote_role_4')
        self.assertEqual(removed_role_assignment_dbs, [])

        # User should have two new roles assigned now
        role_dbs = rbac_service.get_roles_for_user(user_db=user_db,
                                                   include_remote=True)
        self.assertEqual(len(role_dbs), 4)
        self.assertEqual(role_dbs[0], self.roles['mock_local_role_1'])
        self.assertEqual(role_dbs[1], self.roles['mock_local_role_2'])
        self.assertEqual(role_dbs[2], self.roles['mock_remote_role_3'])
        self.assertEqual(role_dbs[3], self.roles['mock_remote_role_4'])

        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=self.users['user_1'])
        self.assertEqual(len(role_assignment_dbs), 4)
        self.assertEqual(role_assignment_dbs[2].source,
                         'mappings/stormers.yaml')
        self.assertEqual(role_assignment_dbs[3].source,
                         'mappings/stormers.yaml')
    def test_sync_user_assignments_multiple_sources_same_role_assignment(self):
        syncer = RBACDefinitionsDBSyncer()

        self._insert_mock_roles()

        # Initial state, no roles
        role_dbs = rbac_service.get_roles_for_user(
            user_db=self.users['user_2'])
        self.assertItemsEqual(role_dbs, [])

        # Do the sync with role defined in separate files
        assignment1 = UserRoleAssignmentFileFormatAPI(
            username='******',
            roles=['role_1'],
            file_path='assignments/user2a.yaml')

        assignment2 = UserRoleAssignmentFileFormatAPI(
            username='******',
            roles=['role_1'],
            file_path='assignments/user2b.yaml')

        syncer.sync_users_role_assignments(
            role_assignment_apis=[assignment1, assignment2])

        role_dbs = rbac_service.get_roles_for_user(
            user_db=self.users['user_2'])
        self.assertEqual(len(role_dbs), 1)
        self.assertEqual(role_dbs[0], self.roles['role_1'])

        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=self.users['user_2'])
        self.assertEqual(len(role_assignment_dbs), 2)

        sources = [r.source for r in role_assignment_dbs]
        self.assertIn('assignments/user2a.yaml', sources)
        self.assertIn('assignments/user2b.yaml', sources)

        # Do another sync with one assignment file removed - only one assignment should be left
        syncer.sync_users_role_assignments(role_assignment_apis=[assignment2])

        role_dbs = rbac_service.get_roles_for_user(
            user_db=self.users['user_2'])
        self.assertEqual(len(role_dbs), 1)
        self.assertEqual(role_dbs[0], self.roles['role_1'])

        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=self.users['user_2'])
        self.assertEqual(len(role_assignment_dbs), 1)

        sources = [r.source for r in role_assignment_dbs]
        self.assertIn('assignments/user2b.yaml', sources)
    def test_sync_user_assignments_multiple_custom_roles_assignments(self):
        syncer = RBACDefinitionsDBSyncer()

        self._insert_mock_roles()

        # Initial state, no roles
        role_dbs = rbac_service.get_roles_for_user(
            user_db=self.users['user_2'])
        self.assertItemsEqual(role_dbs, [])

        # Do the sync with two roles defined
        api = UserRoleAssignmentFileFormatAPI(
            username='******',
            roles=['role_1', 'role_2'],
            file_path='assignments/user2.yaml')
        syncer.sync_users_role_assignments(role_assignment_apis=[api])

        role_dbs = rbac_service.get_roles_for_user(
            user_db=self.users['user_2'])
        self.assertEqual(len(role_dbs), 2)
        self.assertEqual(role_dbs[0], self.roles['role_1'])
        self.assertEqual(role_dbs[1], self.roles['role_2'])
        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=self.users['user_2'])
        self.assertEqual(len(role_assignment_dbs), 2)
        self.assertEqual(role_assignment_dbs[0].source,
                         'assignments/user2.yaml')
        self.assertEqual(role_assignment_dbs[1].source,
                         'assignments/user2.yaml')
    def test_sync_user_assignments_assignments_without_is_remote_are_deleted(
            self):
        # Test case which verifies roles without "is_remote" field (pre v2.3) are removed from the
        # database
        syncer = RBACDefinitionsDBSyncer()

        self._insert_mock_roles()

        # Initial state, 4 roles
        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=self.users['user_5'])
        self.assertEqual(len(role_assignment_dbs), 4)

        # All 3 non remote roles should have been deleted
        syncer.sync_users_role_assignments(role_assignment_apis=[])
        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=self.users['user_5'])
        self.assertEqual(len(role_assignment_dbs), 1)
        self.assertEqual(role_assignment_dbs[0].role, 'role_4')
        self.assertEqual(role_assignment_dbs[0].is_remote, True)
    def test_get_role_assignments_for_user(self):
        # Test a case where a document doesn't exist is_remote field and when it
        # does
        user_db = self.users['user_5']
        role_assignment_dbs = rbac_service.get_role_assignments_for_user(user_db=user_db,
                                                                         include_remote=False)
        self.assertEqual(len(role_assignment_dbs), 3)
        self.assertEqual(role_assignment_dbs[0].role, 'role_1')
        self.assertEqual(role_assignment_dbs[1].role, 'role_2')
        self.assertEqual(role_assignment_dbs[2].role, 'role_3')
        self.assertEqual(role_assignment_dbs[0].is_remote, False)
        self.assertEqual(role_assignment_dbs[1].is_remote, False)
        self.assertEqual(role_assignment_dbs[2].is_remote, False)

        user_db = self.users['user_5']
        role_assignment_dbs = rbac_service.get_role_assignments_for_user(user_db=user_db,
                                                                         include_remote=True)
        self.assertEqual(len(role_assignment_dbs), 4)
        self.assertEqual(role_assignment_dbs[3].role, 'role_4')
        self.assertEqual(role_assignment_dbs[3].is_remote, True)
    def test_sync_user_same_role_granted_locally_and_remote_via_mapping(self):
        syncer = RBACRemoteGroupToRoleSyncer()
        user_db = self.users['user_6']

        # Insert 2 local assignments for mock_role_7
        role_db = rbac_service.create_role(name='mock_role_7')

        source = 'assignments/user_6_one.yaml'
        rbac_service.assign_role_to_user(role_db=role_db,
                                         user_db=user_db,
                                         source=source,
                                         is_remote=False)

        source = 'assignments/user_6_two.yaml'
        rbac_service.assign_role_to_user(role_db=role_db,
                                         user_db=user_db,
                                         source=source,
                                         is_remote=False)

        # Create mock mapping which maps CN=stormers,OU=groups,DC=stackstorm,DC=net
        # to "mock_role_7"
        rbac_service.create_group_to_role_map(
            group='CN=stormers,OU=groups,DC=stackstorm,DC=net',
            roles=['mock_role_7'],
            source='mappings/stormers.yaml')

        # Create mock mapping which maps CN=testers,OU=groups,DC=stackstorm,DC=net
        # to "mock_role_7"
        rbac_service.create_group_to_role_map(
            group='CN=testers,OU=groups,DC=stackstorm,DC=net',
            roles=['mock_role_7'],
            source='mappings/testers.yaml')

        groups = [
            'CN=stormers,OU=groups,DC=stackstorm,DC=net',
            'CN=testers,OU=groups,DC=stackstorm,DC=net'
        ]
        result = syncer.sync(user_db=self.users['user_6'], groups=groups)
        created_role_assignment_dbs = result[0]
        removed_role_assignment_dbs = result[1]
        self.assertEqual(len(created_role_assignment_dbs), 2)
        self.assertEqual(created_role_assignment_dbs[0].role, 'mock_role_7')
        self.assertEqual(created_role_assignment_dbs[1].role, 'mock_role_7')
        self.assertEqual(removed_role_assignment_dbs, [])

        # There should be one role and 4 assignments for the same role
        role_dbs = rbac_service.get_roles_for_user(user_db=user_db,
                                                   include_remote=True)
        self.assertEqual(len(role_dbs), 1)
        self.assertEqual(role_dbs[0].name, 'mock_role_7')

        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=self.users['user_6'])
        self.assertEqual(len(role_assignment_dbs), 4)
        self.assertEqual(role_assignment_dbs[0].source,
                         'assignments/user_6_one.yaml')
        self.assertEqual(role_assignment_dbs[1].source,
                         'assignments/user_6_two.yaml')
        self.assertEqual(role_assignment_dbs[2].source,
                         'mappings/stormers.yaml')
        self.assertEqual(role_assignment_dbs[3].source,
                         'mappings/testers.yaml')

        # Remove one remote group - should be 3 left
        groups = ['CN=stormers,OU=groups,DC=stackstorm,DC=net']
        result = syncer.sync(user_db=self.users['user_6'], groups=groups)
        role_dbs = rbac_service.get_roles_for_user(user_db=user_db,
                                                   include_remote=True)
        self.assertEqual(len(role_dbs), 1)
        self.assertEqual(role_dbs[0].name, 'mock_role_7')

        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=self.users['user_6'])
        self.assertEqual(len(role_assignment_dbs), 3)
    def test_sync_assignments_are_removed_user_doesnt_exist_in_db(self):
        # Make sure that the assignments for the users which don't exist in the db are correctly
        # removed
        syncer = RBACDefinitionsDBSyncer()

        self._insert_mock_roles()

        username = '******'

        # Initial state, no roles
        user_db = UserDB(name=username)
        self.assertEqual(len(User.query(name=username)), 0)
        role_dbs = rbac_service.get_roles_for_user(user_db=user_db)
        self.assertItemsEqual(role_dbs, [])

        # Do the sync with two roles defined
        api = UserRoleAssignmentFileFormatAPI(username=user_db.name,
                                              roles=['role_1', 'role_2'],
                                              file_path='assignments/%s.yaml' %
                                              user_db.name)

        syncer.sync_users_role_assignments(role_assignment_apis=[api])

        role_dbs = rbac_service.get_roles_for_user(user_db=user_db)
        self.assertEqual(len(role_dbs), 2)
        self.assertEqual(role_dbs[0], self.roles['role_1'])
        self.assertEqual(role_dbs[1], self.roles['role_2'])

        # Sync with no roles on disk - existing roles should be removed
        syncer.sync_users_role_assignments(role_assignment_apis=[])

        role_dbs = rbac_service.get_roles_for_user(user_db=user_db)

        self.assertEqual(len(role_dbs), 0)

        username = '******'

        # Initial state, no roles
        user_db = UserDB(name=username)
        self.assertEqual(len(User.query(name=username)), 0)
        role_dbs = rbac_service.get_roles_for_user(user_db=user_db)
        self.assertItemsEqual(role_dbs, [])

        # Do the sync with three roles defined
        api = UserRoleAssignmentFileFormatAPI(
            username=user_db.name,
            roles=['role_1', 'role_2', 'role_3'],
            file_path='assignments/%s.yaml' % user_db.name)

        syncer.sync_users_role_assignments(role_assignment_apis=[api])

        role_dbs = rbac_service.get_roles_for_user(user_db=user_db)
        self.assertEqual(len(role_dbs), 3)
        self.assertEqual(role_dbs[0], self.roles['role_1'])
        self.assertEqual(role_dbs[1], self.roles['role_2'])
        self.assertEqual(role_dbs[2], self.roles['role_3'])
        role_assignment_dbs = rbac_service.get_role_assignments_for_user(
            user_db=user_db)
        self.assertEqual(len(role_assignment_dbs), 3)
        self.assertEqual(role_assignment_dbs[0].source,
                         'assignments/%s.yaml' % user_db.name)

        # Sync with one role on disk - two roles should be removed
        api = UserRoleAssignmentFileFormatAPI(username=user_db.name,
                                              roles=['role_3'],
                                              file_path='assignments/%s.yaml' %
                                              user_db.name)

        syncer.sync_users_role_assignments(role_assignment_apis=[api])

        role_dbs = rbac_service.get_roles_for_user(user_db=user_db)
        self.assertEqual(len(role_dbs), 1)
        self.assertEqual(role_dbs[0], self.roles['role_3'])