diff options
author | 2016-06-09 09:11:50 +0200 | |
---|---|---|
committer | 2016-06-09 09:11:50 +0200 | |
commit | 2e7b4f2027a1147ca28301e4f88adf8274b39a1f (patch) | |
tree | 8b8d94001ebe6cc34106cf813b538911a8d66d9a /keystone-moon/keystone/tests/unit/assignment/test_backends.py | |
parent | a33bdcb627102a01244630a54cb4b5066b385a6a (diff) |
Update Keystone core to Mitaka.
Change-Id: Ia10d6add16f4a9d25d1f42d420661c46332e69db
Diffstat (limited to 'keystone-moon/keystone/tests/unit/assignment/test_backends.py')
-rw-r--r-- | keystone-moon/keystone/tests/unit/assignment/test_backends.py | 3755 |
1 files changed, 3755 insertions, 0 deletions
diff --git a/keystone-moon/keystone/tests/unit/assignment/test_backends.py b/keystone-moon/keystone/tests/unit/assignment/test_backends.py new file mode 100644 index 00000000..eb40e569 --- /dev/null +++ b/keystone-moon/keystone/tests/unit/assignment/test_backends.py @@ -0,0 +1,3755 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import uuid + +import mock +from oslo_config import cfg +from six.moves import range +from testtools import matchers + +from keystone import exception +from keystone.tests import unit + + +CONF = cfg.CONF + + +class AssignmentTestHelperMixin(object): + """Mixin class to aid testing of assignments. + + This class supports data driven test plans that enable: + + - Creation of initial entities, such as domains, users, groups, projects + and roles + - Creation of assignments referencing the above entities + - A set of input parameters and expected outputs to list_role_assignments + based on the above test data + + A test plan is a dict of the form: + + test_plan = { + entities: details and number of entities, + group_memberships: group-user entity memberships, + assignments: list of assignments to create, + tests: list of pairs of input params and expected outputs} + + An example test plan: + + test_plan = { + # First, create the entities required. Entities are specified by + # a dict with the key being the entity type and the value an + # entity specification which can be one of: + # + # - a simple number, e.g. {'users': 3} creates 3 users + # - a dict where more information regarding the contents of the entity + # is required, e.g. {'domains' : {'users : 3}} creates a domain + # with three users + # - a list of entity specifications if multiple are required + # + # The following creates a domain that contains a single user, group and + # project, as well as creating three roles. + + 'entities': {'domains': {'users': 1, 'groups': 1, 'projects': 1}, + 'roles': 3}, + + # If it is required that an existing domain be used for the new + # entities, then the id of that domain can be included in the + # domain dict. For example, if alternatively we wanted to add 3 users + # to the default domain, add a second domain containing 3 projects as + # well as 5 additional empty domains, the entities would be defined as: + # + # 'entities': {'domains': [{'id': DEFAULT_DOMAIN, 'users': 3}, + # {'projects': 3}, 5]}, + # + # A project hierarchy can be specified within the 'projects' section by + # nesting the 'project' key, for example to create a project with three + # sub-projects you would use: + + 'projects': {'project': 3} + + # A more complex hierarchy can also be defined, for example the + # following would define three projects each containing a + # sub-project, each of which contain a further three sub-projects. + + 'projects': [{'project': {'project': 3}}, + {'project': {'project': 3}}, + {'project': {'project': 3}}] + + # If the 'roles' entity count is defined as top level key in 'entities' + # dict then these are global roles. If it is placed within the + # 'domain' dict, then they will be domain specific roles. A mix of + # domain specific and global roles are allowed, with the role index + # being calculated in the order they are defined in the 'entities' + # dict. + + # A set of implied role specifications. In this case, prior role + # index 0 implies role index 1, and role 1 implies roles 2 and 3. + + 'roles': [{'role': 0, 'implied_roles': [1]}, + {'role': 1, 'implied_roles': [2, 3]}] + + # A list of groups and their members. In this case make users with + # index 0 and 1 members of group with index 0. Users and Groups are + # indexed in the order they appear in the 'entities' key above. + + 'group_memberships': [{'group': 0, 'users': [0, 1]}] + + # Next, create assignments between the entities, referencing the + # entities by index, i.e. 'user': 0 refers to user[0]. Entities are + # indexed in the order they appear in the 'entities' key above within + # their entity type. + + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'user': 0, 'role': 2, 'project': 0}], + + # Finally, define an array of tests where list_role_assignment() is + # called with the given input parameters and the results are then + # confirmed to be as given in 'results'. Again, all entities are + # referenced by index. + + 'tests': [ + {'params': {}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'user': 0, 'role': 2, 'project': 0}]}, + {'params': {'role': 2}, + 'results': [{'group': 0, 'role': 2, 'domain': 0}, + {'user': 0, 'role': 2, 'project': 0}]}] + + # The 'params' key also supports the 'effective', + # 'inherited_to_projects' and 'source_from_group_ids' options to + # list_role_assignments.} + + """ + + def _handle_project_spec(self, test_data, domain_id, project_spec, + parent_id=None): + """Handle the creation of a project or hierarchy of projects. + + project_spec may either be a count of the number of projects to + create, or it may be a list of the form: + + [{'project': project_spec}, {'project': project_spec}, ...] + + This method is called recursively to handle the creation of a + hierarchy of projects. + + """ + def _create_project(domain_id, parent_id): + new_project = unit.new_project_ref(domain_id=domain_id, + parent_id=parent_id) + new_project = self.resource_api.create_project(new_project['id'], + new_project) + return new_project + + if isinstance(project_spec, list): + for this_spec in project_spec: + self._handle_project_spec( + test_data, domain_id, this_spec, parent_id=parent_id) + elif isinstance(project_spec, dict): + new_proj = _create_project(domain_id, parent_id) + test_data['projects'].append(new_proj) + self._handle_project_spec( + test_data, domain_id, project_spec['project'], + parent_id=new_proj['id']) + else: + for _ in range(project_spec): + test_data['projects'].append( + _create_project(domain_id, parent_id)) + + def _create_role(self, domain_id=None): + new_role = unit.new_role_ref(domain_id=domain_id) + return self.role_api.create_role(new_role['id'], new_role) + + def _handle_domain_spec(self, test_data, domain_spec): + """Handle the creation of domains and their contents. + + domain_spec may either be a count of the number of empty domains to + create, a dict describing the domain contents, or a list of + domain_specs. + + In the case when a list is provided, this method calls itself + recursively to handle the list elements. + + This method will insert any entities created into test_data + + """ + def _create_domain(domain_id=None): + if domain_id is None: + new_domain = unit.new_domain_ref() + self.resource_api.create_domain(new_domain['id'], + new_domain) + return new_domain + else: + # The test plan specified an existing domain to use + return self.resource_api.get_domain(domain_id) + + def _create_entity_in_domain(entity_type, domain_id): + """Create a user or group entity in the domain.""" + if entity_type == 'users': + new_entity = unit.new_user_ref(domain_id=domain_id) + new_entity = self.identity_api.create_user(new_entity) + elif entity_type == 'groups': + new_entity = unit.new_group_ref(domain_id=domain_id) + new_entity = self.identity_api.create_group(new_entity) + elif entity_type == 'roles': + new_entity = self._create_role(domain_id=domain_id) + else: + # Must be a bad test plan + raise exception.NotImplemented() + return new_entity + + if isinstance(domain_spec, list): + for x in domain_spec: + self._handle_domain_spec(test_data, x) + elif isinstance(domain_spec, dict): + # If there is a domain ID specified, then use it + the_domain = _create_domain(domain_spec.get('id')) + test_data['domains'].append(the_domain) + for entity_type, value in domain_spec.items(): + if entity_type == 'id': + # We already used this above to determine whether to + # use and existing domain + continue + if entity_type == 'projects': + # If it's projects, we need to handle the potential + # specification of a project hierarchy + self._handle_project_spec( + test_data, the_domain['id'], value) + else: + # It's a count of number of entities + for _ in range(value): + test_data[entity_type].append( + _create_entity_in_domain( + entity_type, the_domain['id'])) + else: + for _ in range(domain_spec): + test_data['domains'].append(_create_domain()) + + def create_entities(self, entity_pattern): + """Create the entities specified in the test plan. + + Process the 'entities' key in the test plan, creating the requested + entities. Each created entity will be added to the array of entities + stored in the returned test_data object, e.g.: + + test_data['users'] = [user[0], user[1]....] + + """ + test_data = {} + for entity in ['users', 'groups', 'domains', 'projects', 'roles']: + test_data[entity] = [] + + # Create any domains requested and, if specified, any entities within + # those domains + if 'domains' in entity_pattern: + self._handle_domain_spec(test_data, entity_pattern['domains']) + + # Create any roles requested + if 'roles' in entity_pattern: + for _ in range(entity_pattern['roles']): + test_data['roles'].append(self._create_role()) + + return test_data + + def _convert_entity_shorthand(self, key, shorthand_data, reference_data): + """Convert a shorthand entity description into a full ID reference. + + In test plan definitions, we allow a shorthand for referencing to an + entity of the form: + + 'user': 0 + + which is actually shorthand for: + + 'user_id': reference_data['users'][0]['id'] + + This method converts the shorthand version into the full reference. + + """ + expanded_key = '%s_id' % key + reference_index = '%ss' % key + index_value = ( + reference_data[reference_index][shorthand_data[key]]['id']) + return expanded_key, index_value + + def create_implied_roles(self, implied_pattern, test_data): + """Create the implied roles specified in the test plan.""" + for implied_spec in implied_pattern: + # Each implied role specification is a dict of the form: + # + # {'role': 0, 'implied_roles': list of roles} + + prior_role = test_data['roles'][implied_spec['role']]['id'] + if isinstance(implied_spec['implied_roles'], list): + for this_role in implied_spec['implied_roles']: + implied_role = test_data['roles'][this_role]['id'] + self.role_api.create_implied_role(prior_role, implied_role) + else: + implied_role = ( + test_data['roles'][implied_spec['implied_roles']]['id']) + self.role_api.create_implied_role(prior_role, implied_role) + + def create_group_memberships(self, group_pattern, test_data): + """Create the group memberships specified in the test plan.""" + for group_spec in group_pattern: + # Each membership specification is a dict of the form: + # + # {'group': 0, 'users': [list of user indexes]} + # + # Add all users in the list to the specified group, first + # converting from index to full entity ID. + group_value = test_data['groups'][group_spec['group']]['id'] + for user_index in group_spec['users']: + user_value = test_data['users'][user_index]['id'] + self.identity_api.add_user_to_group(user_value, group_value) + return test_data + + def create_assignments(self, assignment_pattern, test_data): + """Create the assignments specified in the test plan.""" + # First store how many assignments are already in the system, + # so during the tests we can check the number of new assignments + # created. + test_data['initial_assignment_count'] = ( + len(self.assignment_api.list_role_assignments())) + + # Now create the new assignments in the test plan + for assignment in assignment_pattern: + # Each assignment is a dict of the form: + # + # { 'user': 0, 'project':1, 'role': 6} + # + # where the value of each item is the index into the array of + # entities created earlier. + # + # We process the assignment dict to create the args required to + # make the create_grant() call. + args = {} + for param in assignment: + if param == 'inherited_to_projects': + args[param] = assignment[param] + else: + # Turn 'entity : 0' into 'entity_id = ac6736ba873d' + # where entity in user, group, project or domain + key, value = self._convert_entity_shorthand( + param, assignment, test_data) + args[key] = value + self.assignment_api.create_grant(**args) + return test_data + + def execute_assignment_cases(self, test_plan, test_data): + """Execute the test plan, based on the created test_data.""" + def check_results(expected, actual, param_arg_count): + if param_arg_count == 0: + # It was an unfiltered call, so default fixture assignments + # might be polluting our answer - so we take into account + # how many assignments there were before the test. + self.assertEqual( + len(expected) + test_data['initial_assignment_count'], + len(actual)) + else: + self.assertThat(actual, matchers.HasLength(len(expected))) + + for each_expected in expected: + expected_assignment = {} + for param in each_expected: + if param == 'inherited_to_projects': + expected_assignment[param] = each_expected[param] + elif param == 'indirect': + # We're expecting the result to contain an indirect + # dict with the details how the role came to be placed + # on this entity - so convert the key/value pairs of + # that dict into real entity references. + indirect_term = {} + for indirect_param in each_expected[param]: + key, value = self._convert_entity_shorthand( + indirect_param, each_expected[param], + test_data) + indirect_term[key] = value + expected_assignment[param] = indirect_term + else: + # Convert a simple shorthand entry into a full + # entity reference + key, value = self._convert_entity_shorthand( + param, each_expected, test_data) + expected_assignment[key] = value + self.assertIn(expected_assignment, actual) + + def convert_group_ids_sourced_from_list(index_list, reference_data): + value_list = [] + for group_index in index_list: + value_list.append( + reference_data['groups'][group_index]['id']) + return value_list + + # Go through each test in the array, processing the input params, which + # we build into an args dict, and then call list_role_assignments. Then + # check the results against those specified in the test plan. + for test in test_plan.get('tests', []): + args = {} + for param in test['params']: + if param in ['effective', 'inherited', 'include_subtree']: + # Just pass the value into the args + args[param] = test['params'][param] + elif param == 'source_from_group_ids': + # Convert the list of indexes into a list of IDs + args[param] = convert_group_ids_sourced_from_list( + test['params']['source_from_group_ids'], test_data) + else: + # Turn 'entity : 0' into 'entity_id = ac6736ba873d' + # where entity in user, group, project or domain + key, value = self._convert_entity_shorthand( + param, test['params'], test_data) + args[key] = value + results = self.assignment_api.list_role_assignments(**args) + check_results(test['results'], results, len(args)) + + def execute_assignment_plan(self, test_plan): + """Create entities, assignments and execute the test plan. + + The standard method to call to create entities and assignments and + execute the tests as specified in the test_plan. The test_data + dict is returned so that, if required, the caller can execute + additional manual tests with the entities and assignments created. + + """ + test_data = self.create_entities(test_plan['entities']) + if 'implied_roles' in test_plan: + self.create_implied_roles(test_plan['implied_roles'], test_data) + if 'group_memberships' in test_plan: + self.create_group_memberships(test_plan['group_memberships'], + test_data) + if 'assignments' in test_plan: + test_data = self.create_assignments(test_plan['assignments'], + test_data) + self.execute_assignment_cases(test_plan, test_data) + return test_data + + +class AssignmentTests(AssignmentTestHelperMixin): + + def _get_domain_fixture(self): + domain = unit.new_domain_ref() + self.resource_api.create_domain(domain['id'], domain) + return domain + + def test_project_add_and_remove_user_role(self): + user_ids = self.assignment_api.list_user_ids_for_project( + self.tenant_bar['id']) + self.assertNotIn(self.user_two['id'], user_ids) + + self.assignment_api.add_role_to_user_and_project( + tenant_id=self.tenant_bar['id'], + user_id=self.user_two['id'], + role_id=self.role_other['id']) + user_ids = self.assignment_api.list_user_ids_for_project( + self.tenant_bar['id']) + self.assertIn(self.user_two['id'], user_ids) + + self.assignment_api.remove_role_from_user_and_project( + tenant_id=self.tenant_bar['id'], + user_id=self.user_two['id'], + role_id=self.role_other['id']) + + user_ids = self.assignment_api.list_user_ids_for_project( + self.tenant_bar['id']) + self.assertNotIn(self.user_two['id'], user_ids) + + def test_remove_user_role_not_assigned(self): + # Expect failure if attempt to remove a role that was never assigned to + # the user. + self.assertRaises(exception.RoleNotFound, + self.assignment_api. + remove_role_from_user_and_project, + tenant_id=self.tenant_bar['id'], + user_id=self.user_two['id'], + role_id=self.role_other['id']) + + def test_list_user_ids_for_project(self): + user_ids = self.assignment_api.list_user_ids_for_project( + self.tenant_baz['id']) + self.assertEqual(2, len(user_ids)) + self.assertIn(self.user_two['id'], user_ids) + self.assertIn(self.user_badguy['id'], user_ids) + + def test_list_user_ids_for_project_no_duplicates(self): + # Create user + user_ref = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user_ref = self.identity_api.create_user(user_ref) + # Create project + project_ref = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id) + self.resource_api.create_project( + project_ref['id'], project_ref) + # Create 2 roles and give user each role in project + for i in range(2): + role_ref = unit.new_role_ref() + self.role_api.create_role(role_ref['id'], role_ref) + self.assignment_api.add_role_to_user_and_project( + user_id=user_ref['id'], + tenant_id=project_ref['id'], + role_id=role_ref['id']) + # Get the list of user_ids in project + user_ids = self.assignment_api.list_user_ids_for_project( + project_ref['id']) + # Ensure the user is only returned once + self.assertEqual(1, len(user_ids)) + + def test_get_project_user_ids_returns_not_found(self): + self.assertRaises(exception.ProjectNotFound, + self.assignment_api.list_user_ids_for_project, + uuid.uuid4().hex) + + def test_list_role_assignments_unfiltered(self): + """Test unfiltered listing of role assignments.""" + test_plan = { + # Create a domain, with a user, group & project + 'entities': {'domains': {'users': 1, 'groups': 1, 'projects': 1}, + 'roles': 3}, + # Create a grant of each type (user/group on project/domain) + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}], + 'tests': [ + # Check that we get back the 4 assignments + {'params': {}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}]} + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_role_assignments_filtered_by_role(self): + """Test listing of role assignments filtered by role ID.""" + test_plan = { + # Create a user, group & project in the default domain + 'entities': {'domains': {'id': CONF.identity.default_domain_id, + 'users': 1, 'groups': 1, 'projects': 1}, + 'roles': 3}, + # Create a grant of each type (user/group on project/domain) + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}], + 'tests': [ + # Check that when filtering by role, we only get back those + # that match + {'params': {'role': 2}, + 'results': [{'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}]} + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_group_role_assignment(self): + # When a group role assignment is created and the role assignments are + # listed then the group role assignment is included in the list. + + test_plan = { + 'entities': {'domains': {'id': CONF.identity.default_domain_id, + 'groups': 1, 'projects': 1}, + 'roles': 1}, + 'assignments': [{'group': 0, 'role': 0, 'project': 0}], + 'tests': [ + {'params': {}, + 'results': [{'group': 0, 'role': 0, 'project': 0}]} + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_role_assignments_bad_role(self): + assignment_list = self.assignment_api.list_role_assignments( + role_id=uuid.uuid4().hex) + self.assertEqual([], assignment_list) + + def test_add_duplicate_role_grant(self): + roles_ref = self.assignment_api.get_roles_for_user_and_project( + self.user_foo['id'], self.tenant_bar['id']) + self.assertNotIn(self.role_admin['id'], roles_ref) + self.assignment_api.add_role_to_user_and_project( + self.user_foo['id'], self.tenant_bar['id'], self.role_admin['id']) + self.assertRaises(exception.Conflict, + self.assignment_api.add_role_to_user_and_project, + self.user_foo['id'], + self.tenant_bar['id'], + self.role_admin['id']) + + def test_get_role_by_user_and_project_with_user_in_group(self): + """Test for get role by user and project, user was added into a group. + + Test Plan: + + - Create a user, a project & a group, add this user to group + - Create roles and grant them to user and project + - Check the role list get by the user and project was as expected + + """ + user_ref = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user_ref = self.identity_api.create_user(user_ref) + + project_ref = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id) + self.resource_api.create_project(project_ref['id'], project_ref) + + group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + group_id = self.identity_api.create_group(group)['id'] + self.identity_api.add_user_to_group(user_ref['id'], group_id) + + role_ref_list = [] + for i in range(2): + role_ref = unit.new_role_ref() + self.role_api.create_role(role_ref['id'], role_ref) + role_ref_list.append(role_ref) + + self.assignment_api.add_role_to_user_and_project( + user_id=user_ref['id'], + tenant_id=project_ref['id'], + role_id=role_ref['id']) + + role_list = self.assignment_api.get_roles_for_user_and_project( + user_ref['id'], + project_ref['id']) + + self.assertEqual(set([r['id'] for r in role_ref_list]), + set(role_list)) + + def test_get_role_by_user_and_project(self): + roles_ref = self.assignment_api.get_roles_for_user_and_project( + self.user_foo['id'], self.tenant_bar['id']) + self.assertNotIn(self.role_admin['id'], roles_ref) + self.assignment_api.add_role_to_user_and_project( + self.user_foo['id'], self.tenant_bar['id'], self.role_admin['id']) + roles_ref = self.assignment_api.get_roles_for_user_and_project( + self.user_foo['id'], self.tenant_bar['id']) + self.assertIn(self.role_admin['id'], roles_ref) + self.assertNotIn('member', roles_ref) + + self.assignment_api.add_role_to_user_and_project( + self.user_foo['id'], self.tenant_bar['id'], 'member') + roles_ref = self.assignment_api.get_roles_for_user_and_project( + self.user_foo['id'], self.tenant_bar['id']) + self.assertIn(self.role_admin['id'], roles_ref) + self.assertIn('member', roles_ref) + + def test_get_roles_for_user_and_domain(self): + """Test for getting roles for user on a domain. + + Test Plan: + + - Create a domain, with 2 users + - Check no roles yet exit + - Give user1 two roles on the domain, user2 one role + - Get roles on user1 and the domain - maybe sure we only + get back the 2 roles on user1 + - Delete both roles from user1 + - Check we get no roles back for user1 on domain + + """ + new_domain = unit.new_domain_ref() + self.resource_api.create_domain(new_domain['id'], new_domain) + new_user1 = unit.new_user_ref(domain_id=new_domain['id']) + new_user1 = self.identity_api.create_user(new_user1) + new_user2 = unit.new_user_ref(domain_id=new_domain['id']) + new_user2 = self.identity_api.create_user(new_user2) + roles_ref = self.assignment_api.list_grants( + user_id=new_user1['id'], + domain_id=new_domain['id']) + self.assertEqual(0, len(roles_ref)) + # Now create the grants (roles are defined in default_fixtures) + self.assignment_api.create_grant(user_id=new_user1['id'], + domain_id=new_domain['id'], + role_id='member') + self.assignment_api.create_grant(user_id=new_user1['id'], + domain_id=new_domain['id'], + role_id='other') + self.assignment_api.create_grant(user_id=new_user2['id'], + domain_id=new_domain['id'], + role_id='admin') + # Read back the roles for user1 on domain + roles_ids = self.assignment_api.get_roles_for_user_and_domain( + new_user1['id'], new_domain['id']) + self.assertEqual(2, len(roles_ids)) + self.assertIn(self.role_member['id'], roles_ids) + self.assertIn(self.role_other['id'], roles_ids) + + # Now delete both grants for user1 + self.assignment_api.delete_grant(user_id=new_user1['id'], + domain_id=new_domain['id'], + role_id='member') + self.assignment_api.delete_grant(user_id=new_user1['id'], + domain_id=new_domain['id'], + role_id='other') + roles_ref = self.assignment_api.list_grants( + user_id=new_user1['id'], + domain_id=new_domain['id']) + self.assertEqual(0, len(roles_ref)) + + def test_get_roles_for_user_and_domain_returns_not_found(self): + """Test errors raised when getting roles for user on a domain. + + Test Plan: + + - Check non-existing user gives UserNotFound + - Check non-existing domain gives DomainNotFound + + """ + new_domain = self._get_domain_fixture() + new_user1 = unit.new_user_ref(domain_id=new_domain['id']) + new_user1 = self.identity_api.create_user(new_user1) + + self.assertRaises(exception.UserNotFound, + self.assignment_api.get_roles_for_user_and_domain, + uuid.uuid4().hex, + new_domain['id']) + + self.assertRaises(exception.DomainNotFound, + self.assignment_api.get_roles_for_user_and_domain, + new_user1['id'], + uuid.uuid4().hex) + + def test_get_roles_for_user_and_project_returns_not_found(self): + self.assertRaises(exception.UserNotFound, + self.assignment_api.get_roles_for_user_and_project, + uuid.uuid4().hex, + self.tenant_bar['id']) + + self.assertRaises(exception.ProjectNotFound, + self.assignment_api.get_roles_for_user_and_project, + self.user_foo['id'], + uuid.uuid4().hex) + + def test_add_role_to_user_and_project_returns_not_found(self): + self.assertRaises(exception.ProjectNotFound, + self.assignment_api.add_role_to_user_and_project, + self.user_foo['id'], + uuid.uuid4().hex, + self.role_admin['id']) + + self.assertRaises(exception.RoleNotFound, + self.assignment_api.add_role_to_user_and_project, + self.user_foo['id'], + self.tenant_bar['id'], + uuid.uuid4().hex) + + def test_add_role_to_user_and_project_no_user(self): + # If add_role_to_user_and_project and the user doesn't exist, then + # no error. + user_id_not_exist = uuid.uuid4().hex + self.assignment_api.add_role_to_user_and_project( + user_id_not_exist, self.tenant_bar['id'], self.role_admin['id']) + + def test_remove_role_from_user_and_project(self): + self.assignment_api.add_role_to_user_and_project( + self.user_foo['id'], self.tenant_bar['id'], 'member') + self.assignment_api.remove_role_from_user_and_project( + self.user_foo['id'], self.tenant_bar['id'], 'member') + roles_ref = self.assignment_api.get_roles_for_user_and_project( + self.user_foo['id'], self.tenant_bar['id']) + self.assertNotIn('member', roles_ref) + self.assertRaises(exception.NotFound, + self.assignment_api. + remove_role_from_user_and_project, + self.user_foo['id'], + self.tenant_bar['id'], + 'member') + + def test_get_role_grant_by_user_and_project(self): + roles_ref = self.assignment_api.list_grants( + user_id=self.user_foo['id'], + project_id=self.tenant_bar['id']) + self.assertEqual(1, len(roles_ref)) + self.assignment_api.create_grant(user_id=self.user_foo['id'], + project_id=self.tenant_bar['id'], + role_id=self.role_admin['id']) + roles_ref = self.assignment_api.list_grants( + user_id=self.user_foo['id'], + project_id=self.tenant_bar['id']) + self.assertIn(self.role_admin['id'], + [role_ref['id'] for role_ref in roles_ref]) + + self.assignment_api.create_grant(user_id=self.user_foo['id'], + project_id=self.tenant_bar['id'], + role_id='member') + roles_ref = self.assignment_api.list_grants( + user_id=self.user_foo['id'], + project_id=self.tenant_bar['id']) + + roles_ref_ids = [] + for ref in roles_ref: + roles_ref_ids.append(ref['id']) + self.assertIn(self.role_admin['id'], roles_ref_ids) + self.assertIn('member', roles_ref_ids) + + def test_remove_role_grant_from_user_and_project(self): + self.assignment_api.create_grant(user_id=self.user_foo['id'], + project_id=self.tenant_baz['id'], + role_id='member') + roles_ref = self.assignment_api.list_grants( + user_id=self.user_foo['id'], + project_id=self.tenant_baz['id']) + self.assertDictEqual(self.role_member, roles_ref[0]) + + self.assignment_api.delete_grant(user_id=self.user_foo['id'], + project_id=self.tenant_baz['id'], + role_id='member') + roles_ref = self.assignment_api.list_grants( + user_id=self.user_foo['id'], + project_id=self.tenant_baz['id']) + self.assertEqual(0, len(roles_ref)) + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + user_id=self.user_foo['id'], + project_id=self.tenant_baz['id'], + role_id='member') + + def test_get_role_assignment_by_project_not_found(self): + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.check_grant_role_id, + user_id=self.user_foo['id'], + project_id=self.tenant_baz['id'], + role_id='member') + + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.check_grant_role_id, + group_id=uuid.uuid4().hex, + project_id=self.tenant_baz['id'], + role_id='member') + + def test_get_role_assignment_by_domain_not_found(self): + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.check_grant_role_id, + user_id=self.user_foo['id'], + domain_id=self.domain_default['id'], + role_id='member') + + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.check_grant_role_id, + group_id=uuid.uuid4().hex, + domain_id=self.domain_default['id'], + role_id='member') + + def test_del_role_assignment_by_project_not_found(self): + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + user_id=self.user_foo['id'], + project_id=self.tenant_baz['id'], + role_id='member') + + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + group_id=uuid.uuid4().hex, + project_id=self.tenant_baz['id'], + role_id='member') + + def test_del_role_assignment_by_domain_not_found(self): + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + user_id=self.user_foo['id'], + domain_id=self.domain_default['id'], + role_id='member') + + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + group_id=uuid.uuid4().hex, + domain_id=self.domain_default['id'], + role_id='member') + + def test_get_and_remove_role_grant_by_group_and_project(self): + new_domain = unit.new_domain_ref() + self.resource_api.create_domain(new_domain['id'], new_domain) + new_group = unit.new_group_ref(domain_id=new_domain['id']) + new_group = self.identity_api.create_group(new_group) + new_user = unit.new_user_ref(domain_id=new_domain['id']) + new_user = self.identity_api.create_user(new_user) + self.identity_api.add_user_to_group(new_user['id'], + new_group['id']) + roles_ref = self.assignment_api.list_grants( + group_id=new_group['id'], + project_id=self.tenant_bar['id']) + self.assertEqual(0, len(roles_ref)) + self.assignment_api.create_grant(group_id=new_group['id'], + project_id=self.tenant_bar['id'], + role_id='member') + roles_ref = self.assignment_api.list_grants( + group_id=new_group['id'], + project_id=self.tenant_bar['id']) + self.assertDictEqual(self.role_member, roles_ref[0]) + + self.assignment_api.delete_grant(group_id=new_group['id'], + project_id=self.tenant_bar['id'], + role_id='member') + roles_ref = self.assignment_api.list_grants( + group_id=new_group['id'], + project_id=self.tenant_bar['id']) + self.assertEqual(0, len(roles_ref)) + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + group_id=new_group['id'], + project_id=self.tenant_bar['id'], + role_id='member') + + def test_get_and_remove_role_grant_by_group_and_domain(self): + new_domain = unit.new_domain_ref() + self.resource_api.create_domain(new_domain['id'], new_domain) + new_group = unit.new_group_ref(domain_id=new_domain['id']) + new_group = self.identity_api.create_group(new_group) + new_user = unit.new_user_ref(domain_id=new_domain['id']) + new_user = self.identity_api.create_user(new_user) + self.identity_api.add_user_to_group(new_user['id'], + new_group['id']) + + roles_ref = self.assignment_api.list_grants( + group_id=new_group['id'], + domain_id=new_domain['id']) + self.assertEqual(0, len(roles_ref)) + + self.assignment_api.create_grant(group_id=new_group['id'], + domain_id=new_domain['id'], + role_id='member') + + roles_ref = self.assignment_api.list_grants( + group_id=new_group['id'], + domain_id=new_domain['id']) + self.assertDictEqual(self.role_member, roles_ref[0]) + + self.assignment_api.delete_grant(group_id=new_group['id'], + domain_id=new_domain['id'], + role_id='member') + roles_ref = self.assignment_api.list_grants( + group_id=new_group['id'], + domain_id=new_domain['id']) + self.assertEqual(0, len(roles_ref)) + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + group_id=new_group['id'], + domain_id=new_domain['id'], + role_id='member') + + def test_get_and_remove_correct_role_grant_from_a_mix(self): + new_domain = unit.new_domain_ref() + self.resource_api.create_domain(new_domain['id'], new_domain) + new_project = unit.new_project_ref(domain_id=new_domain['id']) + self.resource_api.create_project(new_project['id'], new_project) + new_group = unit.new_group_ref(domain_id=new_domain['id']) + new_group = self.identity_api.create_group(new_group) + new_group2 = unit.new_group_ref(domain_id=new_domain['id']) + new_group2 = self.identity_api.create_group(new_group2) + new_user = unit.new_user_ref(domain_id=new_domain['id']) + new_user = self.identity_api.create_user(new_user) + new_user2 = unit.new_user_ref(domain_id=new_domain['id']) + new_user2 = self.identity_api.create_user(new_user2) + self.identity_api.add_user_to_group(new_user['id'], + new_group['id']) + # First check we have no grants + roles_ref = self.assignment_api.list_grants( + group_id=new_group['id'], + domain_id=new_domain['id']) + self.assertEqual(0, len(roles_ref)) + # Now add the grant we are going to test for, and some others as + # well just to make sure we get back the right one + self.assignment_api.create_grant(group_id=new_group['id'], + domain_id=new_domain['id'], + role_id='member') + + self.assignment_api.create_grant(group_id=new_group2['id'], + domain_id=new_domain['id'], + role_id=self.role_admin['id']) + self.assignment_api.create_grant(user_id=new_user2['id'], + domain_id=new_domain['id'], + role_id=self.role_admin['id']) + self.assignment_api.create_grant(group_id=new_group['id'], + project_id=new_project['id'], + role_id=self.role_admin['id']) + + roles_ref = self.assignment_api.list_grants( + group_id=new_group['id'], + domain_id=new_domain['id']) + self.assertDictEqual(self.role_member, roles_ref[0]) + + self.assignment_api.delete_grant(group_id=new_group['id'], + domain_id=new_domain['id'], + role_id='member') + roles_ref = self.assignment_api.list_grants( + group_id=new_group['id'], + domain_id=new_domain['id']) + self.assertEqual(0, len(roles_ref)) + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + group_id=new_group['id'], + domain_id=new_domain['id'], + role_id='member') + + def test_get_and_remove_role_grant_by_user_and_domain(self): + new_domain = unit.new_domain_ref() + self.resource_api.create_domain(new_domain['id'], new_domain) + new_user = unit.new_user_ref(domain_id=new_domain['id']) + new_user = self.identity_api.create_user(new_user) + roles_ref = self.assignment_api.list_grants( + user_id=new_user['id'], + domain_id=new_domain['id']) + self.assertEqual(0, len(roles_ref)) + self.assignment_api.create_grant(user_id=new_user['id'], + domain_id=new_domain['id'], + role_id='member') + roles_ref = self.assignment_api.list_grants( + user_id=new_user['id'], + domain_id=new_domain['id']) + self.assertDictEqual(self.role_member, roles_ref[0]) + + self.assignment_api.delete_grant(user_id=new_user['id'], + domain_id=new_domain['id'], + role_id='member') + roles_ref = self.assignment_api.list_grants( + user_id=new_user['id'], + domain_id=new_domain['id']) + self.assertEqual(0, len(roles_ref)) + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + user_id=new_user['id'], + domain_id=new_domain['id'], + role_id='member') + + def test_get_and_remove_role_grant_by_group_and_cross_domain(self): + group1_domain1_role = unit.new_role_ref() + self.role_api.create_role(group1_domain1_role['id'], + group1_domain1_role) + group1_domain2_role = unit.new_role_ref() + self.role_api.create_role(group1_domain2_role['id'], + group1_domain2_role) + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + domain2 = unit.new_domain_ref() + self.resource_api.create_domain(domain2['id'], domain2) + group1 = unit.new_group_ref(domain_id=domain1['id']) + group1 = self.identity_api.create_group(group1) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + domain_id=domain1['id']) + self.assertEqual(0, len(roles_ref)) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + domain_id=domain2['id']) + self.assertEqual(0, len(roles_ref)) + self.assignment_api.create_grant(group_id=group1['id'], + domain_id=domain1['id'], + role_id=group1_domain1_role['id']) + self.assignment_api.create_grant(group_id=group1['id'], + domain_id=domain2['id'], + role_id=group1_domain2_role['id']) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + domain_id=domain1['id']) + self.assertDictEqual(group1_domain1_role, roles_ref[0]) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + domain_id=domain2['id']) + self.assertDictEqual(group1_domain2_role, roles_ref[0]) + + self.assignment_api.delete_grant(group_id=group1['id'], + domain_id=domain2['id'], + role_id=group1_domain2_role['id']) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + domain_id=domain2['id']) + self.assertEqual(0, len(roles_ref)) + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + group_id=group1['id'], + domain_id=domain2['id'], + role_id=group1_domain2_role['id']) + + def test_get_and_remove_role_grant_by_user_and_cross_domain(self): + user1_domain1_role = unit.new_role_ref() + self.role_api.create_role(user1_domain1_role['id'], user1_domain1_role) + user1_domain2_role = unit.new_role_ref() + self.role_api.create_role(user1_domain2_role['id'], user1_domain2_role) + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + domain2 = unit.new_domain_ref() + self.resource_api.create_domain(domain2['id'], domain2) + user1 = unit.new_user_ref(domain_id=domain1['id']) + user1 = self.identity_api.create_user(user1) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + domain_id=domain1['id']) + self.assertEqual(0, len(roles_ref)) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + domain_id=domain2['id']) + self.assertEqual(0, len(roles_ref)) + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain1['id'], + role_id=user1_domain1_role['id']) + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain2['id'], + role_id=user1_domain2_role['id']) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + domain_id=domain1['id']) + self.assertDictEqual(user1_domain1_role, roles_ref[0]) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + domain_id=domain2['id']) + self.assertDictEqual(user1_domain2_role, roles_ref[0]) + + self.assignment_api.delete_grant(user_id=user1['id'], + domain_id=domain2['id'], + role_id=user1_domain2_role['id']) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + domain_id=domain2['id']) + self.assertEqual(0, len(roles_ref)) + self.assertRaises(exception.RoleAssignmentNotFound, + self.assignment_api.delete_grant, + user_id=user1['id'], + domain_id=domain2['id'], + role_id=user1_domain2_role['id']) + + def test_role_grant_by_group_and_cross_domain_project(self): + role1 = unit.new_role_ref() + self.role_api.create_role(role1['id'], role1) + role2 = unit.new_role_ref() + self.role_api.create_role(role2['id'], role2) + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + domain2 = unit.new_domain_ref() + self.resource_api.create_domain(domain2['id'], domain2) + group1 = unit.new_group_ref(domain_id=domain1['id']) + group1 = self.identity_api.create_group(group1) + project1 = unit.new_project_ref(domain_id=domain2['id']) + self.resource_api.create_project(project1['id'], project1) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + project_id=project1['id']) + self.assertEqual(0, len(roles_ref)) + self.assignment_api.create_grant(group_id=group1['id'], + project_id=project1['id'], + role_id=role1['id']) + self.assignment_api.create_grant(group_id=group1['id'], + project_id=project1['id'], + role_id=role2['id']) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + project_id=project1['id']) + + roles_ref_ids = [] + for ref in roles_ref: + roles_ref_ids.append(ref['id']) + self.assertIn(role1['id'], roles_ref_ids) + self.assertIn(role2['id'], roles_ref_ids) + + self.assignment_api.delete_grant(group_id=group1['id'], + project_id=project1['id'], + role_id=role1['id']) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + project_id=project1['id']) + self.assertEqual(1, len(roles_ref)) + self.assertDictEqual(role2, roles_ref[0]) + + def test_role_grant_by_user_and_cross_domain_project(self): + role1 = unit.new_role_ref() + self.role_api.create_role(role1['id'], role1) + role2 = unit.new_role_ref() + self.role_api.create_role(role2['id'], role2) + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + domain2 = unit.new_domain_ref() + self.resource_api.create_domain(domain2['id'], domain2) + user1 = unit.new_user_ref(domain_id=domain1['id']) + user1 = self.identity_api.create_user(user1) + project1 = unit.new_project_ref(domain_id=domain2['id']) + self.resource_api.create_project(project1['id'], project1) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + project_id=project1['id']) + self.assertEqual(0, len(roles_ref)) + self.assignment_api.create_grant(user_id=user1['id'], + project_id=project1['id'], + role_id=role1['id']) + self.assignment_api.create_grant(user_id=user1['id'], + project_id=project1['id'], + role_id=role2['id']) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + project_id=project1['id']) + + roles_ref_ids = [] + for ref in roles_ref: + roles_ref_ids.append(ref['id']) + self.assertIn(role1['id'], roles_ref_ids) + self.assertIn(role2['id'], roles_ref_ids) + + self.assignment_api.delete_grant(user_id=user1['id'], + project_id=project1['id'], + role_id=role1['id']) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + project_id=project1['id']) + self.assertEqual(1, len(roles_ref)) + self.assertDictEqual(role2, roles_ref[0]) + + def test_delete_user_grant_no_user(self): + # Can delete a grant where the user doesn't exist. + role = unit.new_role_ref() + role_id = role['id'] + self.role_api.create_role(role_id, role) + + user_id = uuid.uuid4().hex + + self.assignment_api.create_grant(role_id, user_id=user_id, + project_id=self.tenant_bar['id']) + + self.assignment_api.delete_grant(role_id, user_id=user_id, + project_id=self.tenant_bar['id']) + + def test_delete_group_grant_no_group(self): + # Can delete a grant where the group doesn't exist. + role = unit.new_role_ref() + role_id = role['id'] + self.role_api.create_role(role_id, role) + + group_id = uuid.uuid4().hex + + self.assignment_api.create_grant(role_id, group_id=group_id, + project_id=self.tenant_bar['id']) + + self.assignment_api.delete_grant(role_id, group_id=group_id, + project_id=self.tenant_bar['id']) + + def test_grant_crud_throws_exception_if_invalid_role(self): + """Ensure RoleNotFound thrown if role does not exist.""" + def assert_role_not_found_exception(f, **kwargs): + self.assertRaises(exception.RoleNotFound, f, + role_id=uuid.uuid4().hex, **kwargs) + + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user_resp = self.identity_api.create_user(user) + group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + group_resp = self.identity_api.create_group(group) + project = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id) + project_resp = self.resource_api.create_project(project['id'], project) + + for manager_call in [self.assignment_api.create_grant, + self.assignment_api.get_grant, + self.assignment_api.delete_grant]: + assert_role_not_found_exception( + manager_call, + user_id=user_resp['id'], project_id=project_resp['id']) + assert_role_not_found_exception( + manager_call, + group_id=group_resp['id'], project_id=project_resp['id']) + assert_role_not_found_exception( + manager_call, + user_id=user_resp['id'], + domain_id=CONF.identity.default_domain_id) + assert_role_not_found_exception( + manager_call, + group_id=group_resp['id'], + domain_id=CONF.identity.default_domain_id) + + def test_multi_role_grant_by_user_group_on_project_domain(self): + role_list = [] + for _ in range(10): + role = unit.new_role_ref() + self.role_api.create_role(role['id'], role) + role_list.append(role) + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + user1 = unit.new_user_ref(domain_id=domain1['id']) + user1 = self.identity_api.create_user(user1) + group1 = unit.new_group_ref(domain_id=domain1['id']) + group1 = self.identity_api.create_group(group1) + group2 = unit.new_group_ref(domain_id=domain1['id']) + group2 = self.identity_api.create_group(group2) + project1 = unit.new_project_ref(domain_id=domain1['id']) + self.resource_api.create_project(project1['id'], project1) + + self.identity_api.add_user_to_group(user1['id'], + group1['id']) + self.identity_api.add_user_to_group(user1['id'], + group2['id']) + + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + project_id=project1['id']) + self.assertEqual(0, len(roles_ref)) + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain1['id'], + role_id=role_list[0]['id']) + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain1['id'], + role_id=role_list[1]['id']) + self.assignment_api.create_grant(group_id=group1['id'], + domain_id=domain1['id'], + role_id=role_list[2]['id']) + self.assignment_api.create_grant(group_id=group1['id'], + domain_id=domain1['id'], + role_id=role_list[3]['id']) + self.assignment_api.create_grant(user_id=user1['id'], + project_id=project1['id'], + role_id=role_list[4]['id']) + self.assignment_api.create_grant(user_id=user1['id'], + project_id=project1['id'], + role_id=role_list[5]['id']) + self.assignment_api.create_grant(group_id=group1['id'], + project_id=project1['id'], + role_id=role_list[6]['id']) + self.assignment_api.create_grant(group_id=group1['id'], + project_id=project1['id'], + role_id=role_list[7]['id']) + roles_ref = self.assignment_api.list_grants(user_id=user1['id'], + domain_id=domain1['id']) + self.assertEqual(2, len(roles_ref)) + self.assertIn(role_list[0], roles_ref) + self.assertIn(role_list[1], roles_ref) + roles_ref = self.assignment_api.list_grants(group_id=group1['id'], + domain_id=domain1['id']) + self.assertEqual(2, len(roles_ref)) + self.assertIn(role_list[2], roles_ref) + self.assertIn(role_list[3], roles_ref) + roles_ref = self.assignment_api.list_grants(user_id=user1['id'], + project_id=project1['id']) + self.assertEqual(2, len(roles_ref)) + self.assertIn(role_list[4], roles_ref) + self.assertIn(role_list[5], roles_ref) + roles_ref = self.assignment_api.list_grants(group_id=group1['id'], + project_id=project1['id']) + self.assertEqual(2, len(roles_ref)) + self.assertIn(role_list[6], roles_ref) + self.assertIn(role_list[7], roles_ref) + + # Now test the alternate way of getting back lists of grants, + # where user and group roles are combined. These should match + # the above results. + combined_list = self.assignment_api.get_roles_for_user_and_project( + user1['id'], project1['id']) + self.assertEqual(4, len(combined_list)) + self.assertIn(role_list[4]['id'], combined_list) + self.assertIn(role_list[5]['id'], combined_list) + self.assertIn(role_list[6]['id'], combined_list) + self.assertIn(role_list[7]['id'], combined_list) + + combined_role_list = self.assignment_api.get_roles_for_user_and_domain( + user1['id'], domain1['id']) + self.assertEqual(4, len(combined_role_list)) + self.assertIn(role_list[0]['id'], combined_role_list) + self.assertIn(role_list[1]['id'], combined_role_list) + self.assertIn(role_list[2]['id'], combined_role_list) + self.assertIn(role_list[3]['id'], combined_role_list) + + def test_multi_group_grants_on_project_domain(self): + """Test multiple group roles for user on project and domain. + + Test Plan: + + - Create 6 roles + - Create a domain, with a project, user and two groups + - Make the user a member of both groups + - Check no roles yet exit + - Assign a role to each user and both groups on both the + project and domain + - Get a list of effective roles for the user on both the + project and domain, checking we get back the correct three + roles + + """ + role_list = [] + for _ in range(6): + role = unit.new_role_ref() + self.role_api.create_role(role['id'], role) + role_list.append(role) + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + user1 = unit.new_user_ref(domain_id=domain1['id']) + user1 = self.identity_api.create_user(user1) + group1 = unit.new_group_ref(domain_id=domain1['id']) + group1 = self.identity_api.create_group(group1) + group2 = unit.new_group_ref(domain_id=domain1['id']) + group2 = self.identity_api.create_group(group2) + project1 = unit.new_project_ref(domain_id=domain1['id']) + self.resource_api.create_project(project1['id'], project1) + + self.identity_api.add_user_to_group(user1['id'], + group1['id']) + self.identity_api.add_user_to_group(user1['id'], + group2['id']) + + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + project_id=project1['id']) + self.assertEqual(0, len(roles_ref)) + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain1['id'], + role_id=role_list[0]['id']) + self.assignment_api.create_grant(group_id=group1['id'], + domain_id=domain1['id'], + role_id=role_list[1]['id']) + self.assignment_api.create_grant(group_id=group2['id'], + domain_id=domain1['id'], + role_id=role_list[2]['id']) + self.assignment_api.create_grant(user_id=user1['id'], + project_id=project1['id'], + role_id=role_list[3]['id']) + self.assignment_api.create_grant(group_id=group1['id'], + project_id=project1['id'], + role_id=role_list[4]['id']) + self.assignment_api.create_grant(group_id=group2['id'], + project_id=project1['id'], + role_id=role_list[5]['id']) + + # Read by the roles, ensuring we get the correct 3 roles for + # both project and domain + combined_list = self.assignment_api.get_roles_for_user_and_project( + user1['id'], project1['id']) + self.assertEqual(3, len(combined_list)) + self.assertIn(role_list[3]['id'], combined_list) + self.assertIn(role_list[4]['id'], combined_list) + self.assertIn(role_list[5]['id'], combined_list) + + combined_role_list = self.assignment_api.get_roles_for_user_and_domain( + user1['id'], domain1['id']) + self.assertEqual(3, len(combined_role_list)) + self.assertIn(role_list[0]['id'], combined_role_list) + self.assertIn(role_list[1]['id'], combined_role_list) + self.assertIn(role_list[2]['id'], combined_role_list) + + def test_delete_role_with_user_and_group_grants(self): + role1 = unit.new_role_ref() + self.role_api.create_role(role1['id'], role1) + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + project1 = unit.new_project_ref(domain_id=domain1['id']) + self.resource_api.create_project(project1['id'], project1) + user1 = unit.new_user_ref(domain_id=domain1['id']) + user1 = self.identity_api.create_user(user1) + group1 = unit.new_group_ref(domain_id=domain1['id']) + group1 = self.identity_api.create_group(group1) + self.assignment_api.create_grant(user_id=user1['id'], + project_id=project1['id'], + role_id=role1['id']) + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain1['id'], + role_id=role1['id']) + self.assignment_api.create_grant(group_id=group1['id'], + project_id=project1['id'], + role_id=role1['id']) + self.assignment_api.create_grant(group_id=group1['id'], + domain_id=domain1['id'], + role_id=role1['id']) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + project_id=project1['id']) + self.assertEqual(1, len(roles_ref)) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + project_id=project1['id']) + self.assertEqual(1, len(roles_ref)) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + domain_id=domain1['id']) + self.assertEqual(1, len(roles_ref)) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + domain_id=domain1['id']) + self.assertEqual(1, len(roles_ref)) + self.role_api.delete_role(role1['id']) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + project_id=project1['id']) + self.assertEqual(0, len(roles_ref)) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + project_id=project1['id']) + self.assertEqual(0, len(roles_ref)) + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + domain_id=domain1['id']) + self.assertEqual(0, len(roles_ref)) + roles_ref = self.assignment_api.list_grants( + group_id=group1['id'], + domain_id=domain1['id']) + self.assertEqual(0, len(roles_ref)) + + def test_list_role_assignment_by_domain(self): + """Test listing of role assignment filtered by domain.""" + test_plan = { + # A domain with 3 users, 1 group, a spoiler domain and 2 roles. + 'entities': {'domains': [{'users': 3, 'groups': 1}, 1], + 'roles': 2}, + # Users 1 & 2 are in the group + 'group_memberships': [{'group': 0, 'users': [1, 2]}], + # Assign a role for user 0 and the group + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'group': 0, 'role': 1, 'domain': 0}], + 'tests': [ + # List all effective assignments for domain[0]. + # Should get one direct user role and user roles for each of + # the users in the group. + {'params': {'domain': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 1, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}}, + {'user': 2, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}} + ]}, + # Using domain[1] should return nothing + {'params': {'domain': 1, 'effective': True}, + 'results': []}, + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_role_assignment_by_user_with_domain_group_roles(self): + """Test listing assignments by user, with group roles on a domain.""" + test_plan = { + # A domain with 3 users, 3 groups, a spoiler domain + # plus 3 roles. + 'entities': {'domains': [{'users': 3, 'groups': 3}, 1], + 'roles': 3}, + # Users 1 & 2 are in the group 0, User 1 also in group 1 + 'group_memberships': [{'group': 0, 'users': [0, 1]}, + {'group': 1, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'group': 0, 'role': 1, 'domain': 0}, + {'group': 1, 'role': 2, 'domain': 0}, + # ...and two spoiler assignments + {'user': 1, 'role': 1, 'domain': 0}, + {'group': 2, 'role': 2, 'domain': 0}], + 'tests': [ + # List all effective assignments for user[0]. + # Should get one direct user role and a user roles for each of + # groups 0 and 1 + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 2, 'domain': 0, + 'indirect': {'group': 1}} + ]}, + # Adding domain[0] as a filter should return the same data + {'params': {'user': 0, 'domain': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 2, 'domain': 0, + 'indirect': {'group': 1}} + ]}, + # Using domain[1] should return nothing + {'params': {'user': 0, 'domain': 1, 'effective': True}, + 'results': []}, + # Using user[2] should return nothing + {'params': {'user': 2, 'domain': 0, 'effective': True}, + 'results': []}, + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_role_assignment_using_sourced_groups(self): + """Test listing assignments when restricted by source groups.""" + test_plan = { + # The default domain with 3 users, 3 groups, 3 projects, + # plus 3 roles. + 'entities': {'domains': {'id': CONF.identity.default_domain_id, + 'users': 3, 'groups': 3, 'projects': 3}, + 'roles': 3}, + # Users 0 & 1 are in the group 0, User 0 also in group 1 + 'group_memberships': [{'group': 0, 'users': [0, 1]}, + {'group': 1, 'users': [0]}], + # Spread the assignments around - we want to be able to show that + # if sourced by group, assignments from other sources are excluded + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'group': 0, 'role': 1, 'project': 1}, + {'group': 1, 'role': 2, 'project': 0}, + {'group': 1, 'role': 2, 'project': 1}, + {'user': 2, 'role': 1, 'project': 1}, + {'group': 2, 'role': 2, 'project': 2} + ], + 'tests': [ + # List all effective assignments sourced from groups 0 and 1 + {'params': {'source_from_group_ids': [0, 1], + 'effective': True}, + 'results': [{'group': 0, 'role': 1, 'project': 1}, + {'group': 1, 'role': 2, 'project': 0}, + {'group': 1, 'role': 2, 'project': 1} + ]}, + # Adding a role a filter should further restrict the entries + {'params': {'source_from_group_ids': [0, 1], 'role': 2, + 'effective': True}, + 'results': [{'group': 1, 'role': 2, 'project': 0}, + {'group': 1, 'role': 2, 'project': 1} + ]}, + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_role_assignment_using_sourced_groups_with_domains(self): + """Test listing domain assignments when restricted by source groups.""" + test_plan = { + # A domain with 3 users, 3 groups, 3 projects, a second domain, + # plus 3 roles. + 'entities': {'domains': [{'users': 3, 'groups': 3, 'projects': 3}, + 1], + 'roles': 3}, + # Users 0 & 1 are in the group 0, User 0 also in group 1 + 'group_memberships': [{'group': 0, 'users': [0, 1]}, + {'group': 1, 'users': [0]}], + # Spread the assignments around - we want to be able to show that + # if sourced by group, assignments from other sources are excluded + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'group': 0, 'role': 1, 'domain': 1}, + {'group': 1, 'role': 2, 'project': 0}, + {'group': 1, 'role': 2, 'project': 1}, + {'user': 2, 'role': 1, 'project': 1}, + {'group': 2, 'role': 2, 'project': 2} + ], + 'tests': [ + # List all effective assignments sourced from groups 0 and 1 + {'params': {'source_from_group_ids': [0, 1], + 'effective': True}, + 'results': [{'group': 0, 'role': 1, 'domain': 1}, + {'group': 1, 'role': 2, 'project': 0}, + {'group': 1, 'role': 2, 'project': 1} + ]}, + # Adding a role a filter should further restrict the entries + {'params': {'source_from_group_ids': [0, 1], 'role': 1, + 'effective': True}, + 'results': [{'group': 0, 'role': 1, 'domain': 1}, + ]}, + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_role_assignment_fails_with_userid_and_source_groups(self): + """Show we trap this unsupported internal combination of params.""" + group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + group = self.identity_api.create_group(group) + self.assertRaises(exception.UnexpectedError, + self.assignment_api.list_role_assignments, + effective=True, + user_id=self.user_foo['id'], + source_from_group_ids=[group['id']]) + + def test_add_user_to_project(self): + self.assignment_api.add_user_to_project(self.tenant_baz['id'], + self.user_foo['id']) + tenants = self.assignment_api.list_projects_for_user( + self.user_foo['id']) + self.assertIn(self.tenant_baz, tenants) + + def test_add_user_to_project_missing_default_role(self): + self.role_api.delete_role(CONF.member_role_id) + self.assertRaises(exception.RoleNotFound, + self.role_api.get_role, + CONF.member_role_id) + self.assignment_api.add_user_to_project(self.tenant_baz['id'], + self.user_foo['id']) + tenants = ( + self.assignment_api.list_projects_for_user(self.user_foo['id'])) + self.assertIn(self.tenant_baz, tenants) + default_role = self.role_api.get_role(CONF.member_role_id) + self.assertIsNotNone(default_role) + + def test_add_user_to_project_returns_not_found(self): + self.assertRaises(exception.ProjectNotFound, + self.assignment_api.add_user_to_project, + uuid.uuid4().hex, + self.user_foo['id']) + + def test_add_user_to_project_no_user(self): + # If add_user_to_project and the user doesn't exist, then + # no error. + user_id_not_exist = uuid.uuid4().hex + self.assignment_api.add_user_to_project(self.tenant_bar['id'], + user_id_not_exist) + + def test_remove_user_from_project(self): + self.assignment_api.add_user_to_project(self.tenant_baz['id'], + self.user_foo['id']) + self.assignment_api.remove_user_from_project(self.tenant_baz['id'], + self.user_foo['id']) + tenants = self.assignment_api.list_projects_for_user( + self.user_foo['id']) + self.assertNotIn(self.tenant_baz, tenants) + + def test_remove_user_from_project_race_delete_role(self): + self.assignment_api.add_user_to_project(self.tenant_baz['id'], + self.user_foo['id']) + self.assignment_api.add_role_to_user_and_project( + tenant_id=self.tenant_baz['id'], + user_id=self.user_foo['id'], + role_id=self.role_other['id']) + + # Mock a race condition, delete a role after + # get_roles_for_user_and_project() is called in + # remove_user_from_project(). + roles = self.assignment_api.get_roles_for_user_and_project( + self.user_foo['id'], self.tenant_baz['id']) + self.role_api.delete_role(self.role_other['id']) + self.assignment_api.get_roles_for_user_and_project = mock.Mock( + return_value=roles) + self.assignment_api.remove_user_from_project(self.tenant_baz['id'], + self.user_foo['id']) + tenants = self.assignment_api.list_projects_for_user( + self.user_foo['id']) + self.assertNotIn(self.tenant_baz, tenants) + + def test_remove_user_from_project_returns_not_found(self): + self.assertRaises(exception.ProjectNotFound, + self.assignment_api.remove_user_from_project, + uuid.uuid4().hex, + self.user_foo['id']) + + self.assertRaises(exception.UserNotFound, + self.assignment_api.remove_user_from_project, + self.tenant_bar['id'], + uuid.uuid4().hex) + + self.assertRaises(exception.NotFound, + self.assignment_api.remove_user_from_project, + self.tenant_baz['id'], + self.user_foo['id']) + + def test_list_user_project_ids_returns_not_found(self): + self.assertRaises(exception.UserNotFound, + self.assignment_api.list_projects_for_user, + uuid.uuid4().hex) + + def test_delete_user_with_project_association(self): + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user = self.identity_api.create_user(user) + self.assignment_api.add_user_to_project(self.tenant_bar['id'], + user['id']) + self.identity_api.delete_user(user['id']) + self.assertRaises(exception.UserNotFound, + self.assignment_api.list_projects_for_user, + user['id']) + + def test_delete_user_with_project_roles(self): + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user = self.identity_api.create_user(user) + self.assignment_api.add_role_to_user_and_project( + user['id'], + self.tenant_bar['id'], + self.role_member['id']) + self.identity_api.delete_user(user['id']) + self.assertRaises(exception.UserNotFound, + self.assignment_api.list_projects_for_user, + user['id']) + + def test_delete_role_returns_not_found(self): + self.assertRaises(exception.RoleNotFound, + self.role_api.delete_role, + uuid.uuid4().hex) + + def test_delete_project_with_role_assignments(self): + project = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id) + self.resource_api.create_project(project['id'], project) + self.assignment_api.add_role_to_user_and_project( + self.user_foo['id'], project['id'], 'member') + self.resource_api.delete_project(project['id']) + self.assertRaises(exception.ProjectNotFound, + self.assignment_api.list_user_ids_for_project, + project['id']) + + def test_delete_role_check_role_grant(self): + role = unit.new_role_ref() + alt_role = unit.new_role_ref() + self.role_api.create_role(role['id'], role) + self.role_api.create_role(alt_role['id'], alt_role) + self.assignment_api.add_role_to_user_and_project( + self.user_foo['id'], self.tenant_bar['id'], role['id']) + self.assignment_api.add_role_to_user_and_project( + self.user_foo['id'], self.tenant_bar['id'], alt_role['id']) + self.role_api.delete_role(role['id']) + roles_ref = self.assignment_api.get_roles_for_user_and_project( + self.user_foo['id'], self.tenant_bar['id']) + self.assertNotIn(role['id'], roles_ref) + self.assertIn(alt_role['id'], roles_ref) + + def test_list_projects_for_user(self): + domain = unit.new_domain_ref() + self.resource_api.create_domain(domain['id'], domain) + user1 = unit.new_user_ref(domain_id=domain['id']) + user1 = self.identity_api.create_user(user1) + user_projects = self.assignment_api.list_projects_for_user(user1['id']) + self.assertEqual(0, len(user_projects)) + self.assignment_api.create_grant(user_id=user1['id'], + project_id=self.tenant_bar['id'], + role_id=self.role_member['id']) + self.assignment_api.create_grant(user_id=user1['id'], + project_id=self.tenant_baz['id'], + role_id=self.role_member['id']) + user_projects = self.assignment_api.list_projects_for_user(user1['id']) + self.assertEqual(2, len(user_projects)) + + def test_list_projects_for_user_with_grants(self): + # Create two groups each with a role on a different project, and + # make user1 a member of both groups. Both these new projects + # should now be included, along with any direct user grants. + domain = unit.new_domain_ref() + self.resource_api.create_domain(domain['id'], domain) + user1 = unit.new_user_ref(domain_id=domain['id']) + user1 = self.identity_api.create_user(user1) + group1 = unit.new_group_ref(domain_id=domain['id']) + group1 = self.identity_api.create_group(group1) + group2 = unit.new_group_ref(domain_id=domain['id']) + group2 = self.identity_api.create_group(group2) + project1 = unit.new_project_ref(domain_id=domain['id']) + self.resource_api.create_project(project1['id'], project1) + project2 = unit.new_project_ref(domain_id=domain['id']) + self.resource_api.create_project(project2['id'], project2) + self.identity_api.add_user_to_group(user1['id'], group1['id']) + self.identity_api.add_user_to_group(user1['id'], group2['id']) + + # Create 3 grants, one user grant, the other two as group grants + self.assignment_api.create_grant(user_id=user1['id'], + project_id=self.tenant_bar['id'], + role_id=self.role_member['id']) + self.assignment_api.create_grant(group_id=group1['id'], + project_id=project1['id'], + role_id=self.role_admin['id']) + self.assignment_api.create_grant(group_id=group2['id'], + project_id=project2['id'], + role_id=self.role_admin['id']) + user_projects = self.assignment_api.list_projects_for_user(user1['id']) + self.assertEqual(3, len(user_projects)) + + def test_create_grant_no_user(self): + # If call create_grant with a user that doesn't exist, doesn't fail. + self.assignment_api.create_grant( + self.role_other['id'], + user_id=uuid.uuid4().hex, + project_id=self.tenant_bar['id']) + + def test_create_grant_no_group(self): + # If call create_grant with a group that doesn't exist, doesn't fail. + self.assignment_api.create_grant( + self.role_other['id'], + group_id=uuid.uuid4().hex, + project_id=self.tenant_bar['id']) + + def test_delete_group_removes_role_assignments(self): + # When a group is deleted any role assignments for the group are + # removed. + + MEMBER_ROLE_ID = 'member' + + def get_member_assignments(): + assignments = self.assignment_api.list_role_assignments() + return [x for x in assignments if x['role_id'] == MEMBER_ROLE_ID] + + orig_member_assignments = get_member_assignments() + + # Create a group. + new_group = unit.new_group_ref( + domain_id=CONF.identity.default_domain_id) + new_group = self.identity_api.create_group(new_group) + + # Create a project. + new_project = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id) + self.resource_api.create_project(new_project['id'], new_project) + + # Assign a role to the group. + self.assignment_api.create_grant( + group_id=new_group['id'], project_id=new_project['id'], + role_id=MEMBER_ROLE_ID) + + # Delete the group. + self.identity_api.delete_group(new_group['id']) + + # Check that the role assignment for the group is gone + member_assignments = get_member_assignments() + + self.assertThat(member_assignments, + matchers.Equals(orig_member_assignments)) + + def test_get_roles_for_groups_on_domain(self): + """Test retrieving group domain roles. + + Test Plan: + + - Create a domain, three groups and three roles + - Assign one an inherited and the others a non-inherited group role + to the domain + - Ensure that only the non-inherited roles are returned on the domain + + """ + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + group_list = [] + group_id_list = [] + role_list = [] + for _ in range(3): + group = unit.new_group_ref(domain_id=domain1['id']) + group = self.identity_api.create_group(group) + group_list.append(group) + group_id_list.append(group['id']) + + role = unit.new_role_ref() + self.role_api.create_role(role['id'], role) + role_list.append(role) + + # Assign the roles - one is inherited + self.assignment_api.create_grant(group_id=group_list[0]['id'], + domain_id=domain1['id'], + role_id=role_list[0]['id']) + self.assignment_api.create_grant(group_id=group_list[1]['id'], + domain_id=domain1['id'], + role_id=role_list[1]['id']) + self.assignment_api.create_grant(group_id=group_list[2]['id'], + domain_id=domain1['id'], + role_id=role_list[2]['id'], + inherited_to_projects=True) + + # Now get the effective roles for the groups on the domain project. We + # shouldn't get back the inherited role. + + role_refs = self.assignment_api.get_roles_for_groups( + group_id_list, domain_id=domain1['id']) + + self.assertThat(role_refs, matchers.HasLength(2)) + self.assertIn(role_list[0], role_refs) + self.assertIn(role_list[1], role_refs) + + def test_get_roles_for_groups_on_project(self): + """Test retrieving group project roles. + + Test Plan: + + - Create two domains, two projects, six groups and six roles + - Project1 is in Domain1, Project2 is in Domain2 + - Domain2/Project2 are spoilers + - Assign a different direct group role to each project as well + as both an inherited and non-inherited role to each domain + - Get the group roles for Project 1 - depending on whether we have + enabled inheritance, we should either get back just the direct role + or both the direct one plus the inherited domain role from Domain 1 + + """ + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + domain2 = unit.new_domain_ref() + self.resource_api.create_domain(domain2['id'], domain2) + project1 = unit.new_project_ref(domain_id=domain1['id']) + self.resource_api.create_project(project1['id'], project1) + project2 = unit.new_project_ref(domain_id=domain2['id']) + self.resource_api.create_project(project2['id'], project2) + group_list = [] + group_id_list = [] + role_list = [] + for _ in range(6): + group = unit.new_group_ref(domain_id=domain1['id']) + group = self.identity_api.create_group(group) + group_list.append(group) + group_id_list.append(group['id']) + + role = unit.new_role_ref() + self.role_api.create_role(role['id'], role) + role_list.append(role) + + # Assign the roles - one inherited and one non-inherited on Domain1, + # plus one on Project1 + self.assignment_api.create_grant(group_id=group_list[0]['id'], + domain_id=domain1['id'], + role_id=role_list[0]['id']) + self.assignment_api.create_grant(group_id=group_list[1]['id'], + domain_id=domain1['id'], + role_id=role_list[1]['id'], + inherited_to_projects=True) + self.assignment_api.create_grant(group_id=group_list[2]['id'], + project_id=project1['id'], + role_id=role_list[2]['id']) + + # ...and a duplicate set of spoiler assignments to Domain2/Project2 + self.assignment_api.create_grant(group_id=group_list[3]['id'], + domain_id=domain2['id'], + role_id=role_list[3]['id']) + self.assignment_api.create_grant(group_id=group_list[4]['id'], + domain_id=domain2['id'], + role_id=role_list[4]['id'], + inherited_to_projects=True) + self.assignment_api.create_grant(group_id=group_list[5]['id'], + project_id=project2['id'], + role_id=role_list[5]['id']) + + # Now get the effective roles for all groups on the Project1. With + # inheritance off, we should only get back the direct role. + + self.config_fixture.config(group='os_inherit', enabled=False) + role_refs = self.assignment_api.get_roles_for_groups( + group_id_list, project_id=project1['id']) + + self.assertThat(role_refs, matchers.HasLength(1)) + self.assertIn(role_list[2], role_refs) + + # With inheritance on, we should also get back the inherited role from + # its owning domain. + + self.config_fixture.config(group='os_inherit', enabled=True) + role_refs = self.assignment_api.get_roles_for_groups( + group_id_list, project_id=project1['id']) + + self.assertThat(role_refs, matchers.HasLength(2)) + self.assertIn(role_list[1], role_refs) + self.assertIn(role_list[2], role_refs) + + def test_list_domains_for_groups(self): + """Test retrieving domains for a list of groups. + + Test Plan: + + - Create three domains, three groups and one role + - Assign a non-inherited group role to two domains, and an inherited + group role to the third + - Ensure only the domains with non-inherited roles are returned + + """ + domain_list = [] + group_list = [] + group_id_list = [] + for _ in range(3): + domain = unit.new_domain_ref() + self.resource_api.create_domain(domain['id'], domain) + domain_list.append(domain) + + group = unit.new_group_ref(domain_id=domain['id']) + group = self.identity_api.create_group(group) + group_list.append(group) + group_id_list.append(group['id']) + + role1 = unit.new_role_ref() + self.role_api.create_role(role1['id'], role1) + + # Assign the roles - one is inherited + self.assignment_api.create_grant(group_id=group_list[0]['id'], + domain_id=domain_list[0]['id'], + role_id=role1['id']) + self.assignment_api.create_grant(group_id=group_list[1]['id'], + domain_id=domain_list[1]['id'], + role_id=role1['id']) + self.assignment_api.create_grant(group_id=group_list[2]['id'], + domain_id=domain_list[2]['id'], + role_id=role1['id'], + inherited_to_projects=True) + + # Now list the domains that have roles for any of the 3 groups + # We shouldn't get back domain[2] since that had an inherited role. + + domain_refs = ( + self.assignment_api.list_domains_for_groups(group_id_list)) + + self.assertThat(domain_refs, matchers.HasLength(2)) + self.assertIn(domain_list[0], domain_refs) + self.assertIn(domain_list[1], domain_refs) + + def test_list_projects_for_groups(self): + """Test retrieving projects for a list of groups. + + Test Plan: + + - Create two domains, four projects, seven groups and seven roles + - Project1-3 are in Domain1, Project4 is in Domain2 + - Domain2/Project4 are spoilers + - Project1 and 2 have direct group roles, Project3 has no direct + roles but should inherit a group role from Domain1 + - Get the projects for the group roles that are assigned to Project1 + Project2 and the inherited one on Domain1. Depending on whether we + have enabled inheritance, we should either get back just the projects + with direct roles (Project 1 and 2) or also Project3 due to its + inherited role from Domain1. + + """ + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + domain2 = unit.new_domain_ref() + self.resource_api.create_domain(domain2['id'], domain2) + project1 = unit.new_project_ref(domain_id=domain1['id']) + project1 = self.resource_api.create_project(project1['id'], project1) + project2 = unit.new_project_ref(domain_id=domain1['id']) + project2 = self.resource_api.create_project(project2['id'], project2) + project3 = unit.new_project_ref(domain_id=domain1['id']) + project3 = self.resource_api.create_project(project3['id'], project3) + project4 = unit.new_project_ref(domain_id=domain2['id']) + project4 = self.resource_api.create_project(project4['id'], project4) + group_list = [] + role_list = [] + for _ in range(7): + group = unit.new_group_ref(domain_id=domain1['id']) + group = self.identity_api.create_group(group) + group_list.append(group) + + role = unit.new_role_ref() + self.role_api.create_role(role['id'], role) + role_list.append(role) + + # Assign the roles - one inherited and one non-inherited on Domain1, + # plus one on Project1 and Project2 + self.assignment_api.create_grant(group_id=group_list[0]['id'], + domain_id=domain1['id'], + role_id=role_list[0]['id']) + self.assignment_api.create_grant(group_id=group_list[1]['id'], + domain_id=domain1['id'], + role_id=role_list[1]['id'], + inherited_to_projects=True) + self.assignment_api.create_grant(group_id=group_list[2]['id'], + project_id=project1['id'], + role_id=role_list[2]['id']) + self.assignment_api.create_grant(group_id=group_list[3]['id'], + project_id=project2['id'], + role_id=role_list[3]['id']) + + # ...and a few of spoiler assignments to Domain2/Project4 + self.assignment_api.create_grant(group_id=group_list[4]['id'], + domain_id=domain2['id'], + role_id=role_list[4]['id']) + self.assignment_api.create_grant(group_id=group_list[5]['id'], + domain_id=domain2['id'], + role_id=role_list[5]['id'], + inherited_to_projects=True) + self.assignment_api.create_grant(group_id=group_list[6]['id'], + project_id=project4['id'], + role_id=role_list[6]['id']) + + # Now get the projects for the groups that have roles on Project1, + # Project2 and the inherited role on Domain!. With inheritance off, + # we should only get back the projects with direct role. + + self.config_fixture.config(group='os_inherit', enabled=False) + group_id_list = [group_list[1]['id'], group_list[2]['id'], + group_list[3]['id']] + project_refs = ( + self.assignment_api.list_projects_for_groups(group_id_list)) + + self.assertThat(project_refs, matchers.HasLength(2)) + self.assertIn(project1, project_refs) + self.assertIn(project2, project_refs) + + # With inheritance on, we should also get back the Project3 due to the + # inherited role from its owning domain. + + self.config_fixture.config(group='os_inherit', enabled=True) + project_refs = ( + self.assignment_api.list_projects_for_groups(group_id_list)) + + self.assertThat(project_refs, matchers.HasLength(3)) + self.assertIn(project1, project_refs) + self.assertIn(project2, project_refs) + self.assertIn(project3, project_refs) + + def test_update_role_no_name(self): + # A user can update a role and not include the name. + + # description is picked just because it's not name. + self.role_api.update_role(self.role_member['id'], + {'description': uuid.uuid4().hex}) + # If the previous line didn't raise an exception then the test passes. + + def test_update_role_same_name(self): + # A user can update a role and set the name to be the same as it was. + + self.role_api.update_role(self.role_member['id'], + {'name': self.role_member['name']}) + # If the previous line didn't raise an exception then the test passes. + + def test_list_role_assignment_containing_names(self): + # Create Refs + new_role = unit.new_role_ref() + new_domain = self._get_domain_fixture() + new_user = unit.new_user_ref(domain_id=new_domain['id']) + new_project = unit.new_project_ref(domain_id=new_domain['id']) + new_group = unit.new_group_ref(domain_id=new_domain['id']) + # Create entities + new_role = self.role_api.create_role(new_role['id'], new_role) + new_user = self.identity_api.create_user(new_user) + new_group = self.identity_api.create_group(new_group) + self.resource_api.create_project(new_project['id'], new_project) + self.assignment_api.create_grant(user_id=new_user['id'], + project_id=new_project['id'], + role_id=new_role['id']) + self.assignment_api.create_grant(group_id=new_group['id'], + project_id=new_project['id'], + role_id=new_role['id']) + self.assignment_api.create_grant(domain_id=new_domain['id'], + user_id=new_user['id'], + role_id=new_role['id']) + # Get the created assignments with the include_names flag + _asgmt_prj = self.assignment_api.list_role_assignments( + user_id=new_user['id'], + project_id=new_project['id'], + include_names=True) + _asgmt_grp = self.assignment_api.list_role_assignments( + group_id=new_group['id'], + project_id=new_project['id'], + include_names=True) + _asgmt_dmn = self.assignment_api.list_role_assignments( + domain_id=new_domain['id'], + user_id=new_user['id'], + include_names=True) + # Make sure we can get back the correct number of assignments + self.assertThat(_asgmt_prj, matchers.HasLength(1)) + self.assertThat(_asgmt_grp, matchers.HasLength(1)) + self.assertThat(_asgmt_dmn, matchers.HasLength(1)) + # get the first assignment + first_asgmt_prj = _asgmt_prj[0] + first_asgmt_grp = _asgmt_grp[0] + first_asgmt_dmn = _asgmt_dmn[0] + # Assert the names are correct in the project response + self.assertEqual(new_project['name'], + first_asgmt_prj['project_name']) + self.assertEqual(new_project['domain_id'], + first_asgmt_prj['project_domain_id']) + self.assertEqual(new_user['name'], + first_asgmt_prj['user_name']) + self.assertEqual(new_user['domain_id'], + first_asgmt_prj['user_domain_id']) + self.assertEqual(new_role['name'], + first_asgmt_prj['role_name']) + # Assert the names are correct in the group response + self.assertEqual(new_group['name'], + first_asgmt_grp['group_name']) + self.assertEqual(new_group['domain_id'], + first_asgmt_grp['group_domain_id']) + self.assertEqual(new_project['name'], + first_asgmt_grp['project_name']) + self.assertEqual(new_project['domain_id'], + first_asgmt_grp['project_domain_id']) + self.assertEqual(new_role['name'], + first_asgmt_grp['role_name']) + # Assert the names are correct in the domain response + self.assertEqual(new_domain['name'], + first_asgmt_dmn['domain_name']) + self.assertEqual(new_user['name'], + first_asgmt_dmn['user_name']) + self.assertEqual(new_user['domain_id'], + first_asgmt_dmn['user_domain_id']) + self.assertEqual(new_role['name'], + first_asgmt_dmn['role_name']) + + def test_list_role_assignment_does_not_contain_names(self): + """Test names are not included with list role assignments. + + Scenario: + - names are NOT included by default + - names are NOT included when include_names=False + + """ + def assert_does_not_contain_names(assignment): + first_asgmt_prj = assignment[0] + self.assertNotIn('project_name', first_asgmt_prj) + self.assertNotIn('project_domain_id', first_asgmt_prj) + self.assertNotIn('user_name', first_asgmt_prj) + self.assertNotIn('user_domain_id', first_asgmt_prj) + self.assertNotIn('role_name', first_asgmt_prj) + + # Create Refs + new_role = unit.new_role_ref() + new_domain = self._get_domain_fixture() + new_user = unit.new_user_ref(domain_id=new_domain['id']) + new_project = unit.new_project_ref(domain_id=new_domain['id']) + # Create entities + new_role = self.role_api.create_role(new_role['id'], new_role) + new_user = self.identity_api.create_user(new_user) + self.resource_api.create_project(new_project['id'], new_project) + self.assignment_api.create_grant(user_id=new_user['id'], + project_id=new_project['id'], + role_id=new_role['id']) + # Get the created assignments with NO include_names flag + role_assign_without_names = self.assignment_api.list_role_assignments( + user_id=new_user['id'], + project_id=new_project['id']) + assert_does_not_contain_names(role_assign_without_names) + # Get the created assignments with include_names=False + role_assign_without_names = self.assignment_api.list_role_assignments( + user_id=new_user['id'], + project_id=new_project['id'], + include_names=False) + assert_does_not_contain_names(role_assign_without_names) + + def test_delete_user_assignments_user_same_id_as_group(self): + """Test deleting user assignments when user_id == group_id. + + In this scenario, only user assignments must be deleted (i.e. + USER_DOMAIN or USER_PROJECT). + + Test plan: + * Create a user and a group with the same ID; + * Create four roles and assign them to both user and group; + * Delete all user assignments; + * Group assignments must stay intact. + """ + # Create a common ID + common_id = uuid.uuid4().hex + # Create a project + project = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id) + project = self.resource_api.create_project(project['id'], project) + # Create a user + user = unit.new_user_ref(id=common_id, + domain_id=CONF.identity.default_domain_id) + user = self.identity_api.driver.create_user(common_id, user) + self.assertEqual(common_id, user['id']) + # Create a group + group = unit.new_group_ref(id=common_id, + domain_id=CONF.identity.default_domain_id) + group = self.identity_api.driver.create_group(common_id, group) + self.assertEqual(common_id, group['id']) + # Create four roles + roles = [] + for _ in range(4): + role = unit.new_role_ref() + roles.append(self.role_api.create_role(role['id'], role)) + # Assign roles for user + self.assignment_api.driver.create_grant( + user_id=user['id'], domain_id=CONF.identity.default_domain_id, + role_id=roles[0]['id']) + self.assignment_api.driver.create_grant(user_id=user['id'], + project_id=project['id'], + role_id=roles[1]['id']) + # Assign roles for group + self.assignment_api.driver.create_grant( + group_id=group['id'], domain_id=CONF.identity.default_domain_id, + role_id=roles[2]['id']) + self.assignment_api.driver.create_grant(group_id=group['id'], + project_id=project['id'], + role_id=roles[3]['id']) + # Make sure they were assigned + user_assignments = self.assignment_api.list_role_assignments( + user_id=user['id']) + self.assertThat(user_assignments, matchers.HasLength(2)) + group_assignments = self.assignment_api.list_role_assignments( + group_id=group['id']) + self.assertThat(group_assignments, matchers.HasLength(2)) + # Delete user assignments + self.assignment_api.delete_user_assignments(user_id=user['id']) + # Assert only user assignments were deleted + user_assignments = self.assignment_api.list_role_assignments( + user_id=user['id']) + self.assertThat(user_assignments, matchers.HasLength(0)) + group_assignments = self.assignment_api.list_role_assignments( + group_id=group['id']) + self.assertThat(group_assignments, matchers.HasLength(2)) + # Make sure these remaining assignments are group-related + for assignment in group_assignments: + self.assertThat(assignment.keys(), matchers.Contains('group_id')) + + def test_delete_group_assignments_group_same_id_as_user(self): + """Test deleting group assignments when group_id == user_id. + + In this scenario, only group assignments must be deleted (i.e. + GROUP_DOMAIN or GROUP_PROJECT). + + Test plan: + * Create a group and a user with the same ID; + * Create four roles and assign them to both group and user; + * Delete all group assignments; + * User assignments must stay intact. + """ + # Create a common ID + common_id = uuid.uuid4().hex + # Create a project + project = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id) + project = self.resource_api.create_project(project['id'], project) + # Create a user + user = unit.new_user_ref(id=common_id, + domain_id=CONF.identity.default_domain_id) + user = self.identity_api.driver.create_user(common_id, user) + self.assertEqual(common_id, user['id']) + # Create a group + group = unit.new_group_ref(id=common_id, + domain_id=CONF.identity.default_domain_id) + group = self.identity_api.driver.create_group(common_id, group) + self.assertEqual(common_id, group['id']) + # Create four roles + roles = [] + for _ in range(4): + role = unit.new_role_ref() + roles.append(self.role_api.create_role(role['id'], role)) + # Assign roles for user + self.assignment_api.driver.create_grant( + user_id=user['id'], domain_id=CONF.identity.default_domain_id, + role_id=roles[0]['id']) + self.assignment_api.driver.create_grant(user_id=user['id'], + project_id=project['id'], + role_id=roles[1]['id']) + # Assign roles for group + self.assignment_api.driver.create_grant( + group_id=group['id'], domain_id=CONF.identity.default_domain_id, + role_id=roles[2]['id']) + self.assignment_api.driver.create_grant(group_id=group['id'], + project_id=project['id'], + role_id=roles[3]['id']) + # Make sure they were assigned + user_assignments = self.assignment_api.list_role_assignments( + user_id=user['id']) + self.assertThat(user_assignments, matchers.HasLength(2)) + group_assignments = self.assignment_api.list_role_assignments( + group_id=group['id']) + self.assertThat(group_assignments, matchers.HasLength(2)) + # Delete group assignments + self.assignment_api.delete_group_assignments(group_id=group['id']) + # Assert only group assignments were deleted + group_assignments = self.assignment_api.list_role_assignments( + group_id=group['id']) + self.assertThat(group_assignments, matchers.HasLength(0)) + user_assignments = self.assignment_api.list_role_assignments( + user_id=user['id']) + self.assertThat(user_assignments, matchers.HasLength(2)) + # Make sure these remaining assignments are user-related + for assignment in group_assignments: + self.assertThat(assignment.keys(), matchers.Contains('user_id')) + + def test_remove_foreign_assignments_when_deleting_a_domain(self): + # A user and a group are in default domain and have assigned a role on + # two new domains. This test makes sure that when one of the new + # domains is deleted, the role assignments for the user and the group + # from the default domain are deleted only on that domain. + group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + group = self.identity_api.create_group(group) + + role = unit.new_role_ref() + role = self.role_api.create_role(role['id'], role) + + new_domains = [unit.new_domain_ref(), unit.new_domain_ref()] + for new_domain in new_domains: + self.resource_api.create_domain(new_domain['id'], new_domain) + + self.assignment_api.create_grant(group_id=group['id'], + domain_id=new_domain['id'], + role_id=role['id']) + self.assignment_api.create_grant(user_id=self.user_two['id'], + domain_id=new_domain['id'], + role_id=role['id']) + + # Check there are 4 role assignments for that role + role_assignments = self.assignment_api.list_role_assignments( + role_id=role['id']) + self.assertThat(role_assignments, matchers.HasLength(4)) + + # Delete first new domain and check only 2 assignments were left + self.resource_api.update_domain(new_domains[0]['id'], + {'enabled': False}) + self.resource_api.delete_domain(new_domains[0]['id']) + + role_assignments = self.assignment_api.list_role_assignments( + role_id=role['id']) + self.assertThat(role_assignments, matchers.HasLength(2)) + + # Delete second new domain and check no assignments were left + self.resource_api.update_domain(new_domains[1]['id'], + {'enabled': False}) + self.resource_api.delete_domain(new_domains[1]['id']) + + role_assignments = self.assignment_api.list_role_assignments( + role_id=role['id']) + self.assertEqual([], role_assignments) + + +class InheritanceTests(AssignmentTestHelperMixin): + + def test_role_assignments_user_domain_to_project_inheritance(self): + test_plan = { + 'entities': {'domains': {'users': 2, 'projects': 1}, + 'roles': 3}, + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'user': 1, 'role': 1, 'project': 0}], + 'tests': [ + # List all direct assignments for user[0] + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': 'projects'}]}, + # Now the effective ones - so the domain role should turn into + # a project role + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}]}, + # Narrow down to effective roles for user[0] and project[0] + {'params': {'user': 0, 'project': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + self.execute_assignment_plan(test_plan) + + def test_inherited_role_assignments_excluded_if_os_inherit_false(self): + test_plan = { + 'entities': {'domains': {'users': 2, 'groups': 1, 'projects': 1}, + 'roles': 4}, + 'group_memberships': [{'group': 0, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'user': 1, 'role': 1, 'project': 0}, + {'group': 0, 'role': 3, 'project': 0}], + 'tests': [ + # List all direct assignments for user[0], since os-inherit is + # disabled, we should not see the inherited role + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}]}, + # Same in effective mode - inherited roles should not be + # included or expanded...but the group role should now + # turn up as a user role, since group expansion is not + # part of os-inherit. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'group': 0}}]}, + ] + } + self.config_fixture.config(group='os_inherit', enabled=False) + self.execute_assignment_plan(test_plan) + + def _test_crud_inherited_and_direct_assignment(self, **kwargs): + """Tests inherited and direct assignments for the actor and target + + Ensure it is possible to create both inherited and direct role + assignments for the same actor on the same target. The actor and the + target are specified in the kwargs as ('user_id' or 'group_id') and + ('project_id' or 'domain_id'), respectively. + + """ + self.config_fixture.config(group='os_inherit', enabled=True) + # Create a new role to avoid assignments loaded from default fixtures + role = unit.new_role_ref() + role = self.role_api.create_role(role['id'], role) + + # Define the common assignment entity + assignment_entity = {'role_id': role['id']} + assignment_entity.update(kwargs) + + # Define assignments under test + direct_assignment_entity = assignment_entity.copy() + inherited_assignment_entity = assignment_entity.copy() + inherited_assignment_entity['inherited_to_projects'] = 'projects' + + # Create direct assignment and check grants + self.assignment_api.create_grant(inherited_to_projects=False, + **assignment_entity) + + grants = self.assignment_api.list_role_assignments(role_id=role['id']) + self.assertThat(grants, matchers.HasLength(1)) + self.assertIn(direct_assignment_entity, grants) + + # Now add inherited assignment and check grants + self.assignment_api.create_grant(inherited_to_projects=True, + **assignment_entity) + + grants = self.assignment_api.list_role_assignments(role_id=role['id']) + self.assertThat(grants, matchers.HasLength(2)) + self.assertIn(direct_assignment_entity, grants) + self.assertIn(inherited_assignment_entity, grants) + + # Delete both and check grants + self.assignment_api.delete_grant(inherited_to_projects=False, + **assignment_entity) + self.assignment_api.delete_grant(inherited_to_projects=True, + **assignment_entity) + + grants = self.assignment_api.list_role_assignments(role_id=role['id']) + self.assertEqual([], grants) + + def test_crud_inherited_and_direct_assignment_for_user_on_domain(self): + self._test_crud_inherited_and_direct_assignment( + user_id=self.user_foo['id'], + domain_id=CONF.identity.default_domain_id) + + def test_crud_inherited_and_direct_assignment_for_group_on_domain(self): + group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + group = self.identity_api.create_group(group) + + self._test_crud_inherited_and_direct_assignment( + group_id=group['id'], domain_id=CONF.identity.default_domain_id) + + def test_crud_inherited_and_direct_assignment_for_user_on_project(self): + self._test_crud_inherited_and_direct_assignment( + user_id=self.user_foo['id'], project_id=self.tenant_baz['id']) + + def test_crud_inherited_and_direct_assignment_for_group_on_project(self): + group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + group = self.identity_api.create_group(group) + + self._test_crud_inherited_and_direct_assignment( + group_id=group['id'], project_id=self.tenant_baz['id']) + + def test_inherited_role_grants_for_user(self): + """Test inherited user roles. + + Test Plan: + + - Enable OS-INHERIT extension + - Create 3 roles + - Create a domain, with a project and a user + - Check no roles yet exit + - Assign a direct user role to the project and a (non-inherited) + user role to the domain + - Get a list of effective roles - should only get the one direct role + - Now add an inherited user role to the domain + - Get a list of effective roles - should have two roles, one + direct and one by virtue of the inherited user role + - Also get effective roles for the domain - the role marked as + inherited should not show up + + """ + self.config_fixture.config(group='os_inherit', enabled=True) + role_list = [] + for _ in range(3): + role = unit.new_role_ref() + self.role_api.create_role(role['id'], role) + role_list.append(role) + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + user1 = unit.new_user_ref(domain_id=domain1['id']) + user1 = self.identity_api.create_user(user1) + project1 = unit.new_project_ref(domain_id=domain1['id']) + self.resource_api.create_project(project1['id'], project1) + + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + project_id=project1['id']) + self.assertEqual(0, len(roles_ref)) + + # Create the first two roles - the domain one is not inherited + self.assignment_api.create_grant(user_id=user1['id'], + project_id=project1['id'], + role_id=role_list[0]['id']) + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain1['id'], + role_id=role_list[1]['id']) + + # Now get the effective roles for the user and project, this + # should only include the direct role assignment on the project + combined_list = self.assignment_api.get_roles_for_user_and_project( + user1['id'], project1['id']) + self.assertEqual(1, len(combined_list)) + self.assertIn(role_list[0]['id'], combined_list) + + # Now add an inherited role on the domain + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain1['id'], + role_id=role_list[2]['id'], + inherited_to_projects=True) + + # Now get the effective roles for the user and project again, this + # should now include the inherited role on the domain + combined_list = self.assignment_api.get_roles_for_user_and_project( + user1['id'], project1['id']) + self.assertEqual(2, len(combined_list)) + self.assertIn(role_list[0]['id'], combined_list) + self.assertIn(role_list[2]['id'], combined_list) + + # Finally, check that the inherited role does not appear as a valid + # directly assigned role on the domain itself + combined_role_list = self.assignment_api.get_roles_for_user_and_domain( + user1['id'], domain1['id']) + self.assertEqual(1, len(combined_role_list)) + self.assertIn(role_list[1]['id'], combined_role_list) + + # TODO(henry-nash): The test above uses get_roles_for_user_and_project + # and get_roles_for_user_and_domain, which will, in a subsequent patch, + # be re-implemented to simply call list_role_assignments (see blueprint + # remove-role-metadata). + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once get_roles_for_user_and + # project/domain have been re-implemented then the manual tests above + # can be refactored to simply ensure it gives the same answers. + test_plan = { + # A domain with a user & project, plus 3 roles. + 'entities': {'domains': {'users': 1, 'projects': 1}, + 'roles': 3}, + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'domain': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] on project[0]. + # Should get one direct role and one inherited role. + {'params': {'user': 0, 'project': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}]}, + # Ensure effective mode on the domain does not list the + # inherited role on that domain + {'params': {'user': 0, 'domain': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 1, 'domain': 0}]}, + # Ensure non-inherited mode also only returns the non-inherited + # role on the domain + {'params': {'user': 0, 'domain': 0, 'inherited': False}, + 'results': [{'user': 0, 'role': 1, 'domain': 0}]}, + ] + } + self.execute_assignment_plan(test_plan) + + def test_inherited_role_grants_for_group(self): + """Test inherited group roles. + + Test Plan: + + - Enable OS-INHERIT extension + - Create 4 roles + - Create a domain, with a project, user and two groups + - Make the user a member of both groups + - Check no roles yet exit + - Assign a direct user role to the project and a (non-inherited) + group role on the domain + - Get a list of effective roles - should only get the one direct role + - Now add two inherited group roles to the domain + - Get a list of effective roles - should have three roles, one + direct and two by virtue of inherited group roles + + """ + self.config_fixture.config(group='os_inherit', enabled=True) + role_list = [] + for _ in range(4): + role = unit.new_role_ref() + self.role_api.create_role(role['id'], role) + role_list.append(role) + domain1 = unit.new_domain_ref() + self.resource_api.create_domain(domain1['id'], domain1) + user1 = unit.new_user_ref(domain_id=domain1['id']) + user1 = self.identity_api.create_user(user1) + group1 = unit.new_group_ref(domain_id=domain1['id']) + group1 = self.identity_api.create_group(group1) + group2 = unit.new_group_ref(domain_id=domain1['id']) + group2 = self.identity_api.create_group(group2) + project1 = unit.new_project_ref(domain_id=domain1['id']) + self.resource_api.create_project(project1['id'], project1) + + self.identity_api.add_user_to_group(user1['id'], + group1['id']) + self.identity_api.add_user_to_group(user1['id'], + group2['id']) + + roles_ref = self.assignment_api.list_grants( + user_id=user1['id'], + project_id=project1['id']) + self.assertEqual(0, len(roles_ref)) + + # Create two roles - the domain one is not inherited + self.assignment_api.create_grant(user_id=user1['id'], + project_id=project1['id'], + role_id=role_list[0]['id']) + self.assignment_api.create_grant(group_id=group1['id'], + domain_id=domain1['id'], + role_id=role_list[1]['id']) + + # Now get the effective roles for the user and project, this + # should only include the direct role assignment on the project + combined_list = self.assignment_api.get_roles_for_user_and_project( + user1['id'], project1['id']) + self.assertEqual(1, len(combined_list)) + self.assertIn(role_list[0]['id'], combined_list) + + # Now add to more group roles, both inherited, to the domain + self.assignment_api.create_grant(group_id=group2['id'], + domain_id=domain1['id'], + role_id=role_list[2]['id'], + inherited_to_projects=True) + self.assignment_api.create_grant(group_id=group2['id'], + domain_id=domain1['id'], + role_id=role_list[3]['id'], + inherited_to_projects=True) + + # Now get the effective roles for the user and project again, this + # should now include the inherited roles on the domain + combined_list = self.assignment_api.get_roles_for_user_and_project( + user1['id'], project1['id']) + self.assertEqual(3, len(combined_list)) + self.assertIn(role_list[0]['id'], combined_list) + self.assertIn(role_list[2]['id'], combined_list) + self.assertIn(role_list[3]['id'], combined_list) + + # TODO(henry-nash): The test above uses get_roles_for_user_and_project + # which will, in a subsequent patch, be re-implemented to simply call + # list_role_assignments (see blueprint remove-role-metadata). + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once + # get_roles_for_user_and_project has been re-implemented then the + # manual tests above can be refactored to simply ensure it gives + # the same answers. + test_plan = { + # A domain with a user and project, 2 groups, plus 4 roles. + 'entities': {'domains': {'users': 1, 'projects': 1, 'groups': 2}, + 'roles': 4}, + 'group_memberships': [{'group': 0, 'users': [0]}, + {'group': 1, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'group': 0, 'role': 1, 'domain': 0}, + {'group': 1, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'group': 1, 'role': 3, 'domain': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] on project[0]. + # Should get one direct role and both inherited roles, but + # not the direct one on domain[0], even though user[0] is + # in group[0]. + {'params': {'user': 0, 'project': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0, 'group': 1}}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'domain': 0, 'group': 1}}]} + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_projects_for_user_with_inherited_grants(self): + """Test inherited user roles. + + Test Plan: + + - Enable OS-INHERIT extension + - Create a domain, with two projects and a user + - Assign an inherited user role on the domain, as well as a direct + user role to a separate project in a different domain + - Get a list of projects for user, should return all three projects + + """ + self.config_fixture.config(group='os_inherit', enabled=True) + domain = unit.new_domain_ref() + self.resource_api.create_domain(domain['id'], domain) + user1 = unit.new_user_ref(domain_id=domain['id']) + user1 = self.identity_api.create_user(user1) + project1 = unit.new_project_ref(domain_id=domain['id']) + self.resource_api.create_project(project1['id'], project1) + project2 = unit.new_project_ref(domain_id=domain['id']) + self.resource_api.create_project(project2['id'], project2) + + # Create 2 grants, one on a project and one inherited grant + # on the domain + self.assignment_api.create_grant(user_id=user1['id'], + project_id=self.tenant_bar['id'], + role_id=self.role_member['id']) + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain['id'], + role_id=self.role_admin['id'], + inherited_to_projects=True) + # Should get back all three projects, one by virtue of the direct + # grant, plus both projects in the domain + user_projects = self.assignment_api.list_projects_for_user(user1['id']) + self.assertEqual(3, len(user_projects)) + + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemented to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with 1 project, plus a second domain with 2 projects, + # as well as a user. Also, create 2 roles. + 'entities': {'domains': [{'projects': 1}, + {'users': 1, 'projects': 2}], + 'roles': 2}, + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'domain': 1, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] + # Should get one direct role plus one inherited role for each + # project in domain + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'domain': 1}}, + {'user': 0, 'role': 1, 'project': 2, + 'indirect': {'domain': 1}}]} + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_projects_for_user_with_inherited_user_project_grants(self): + """Test inherited role assignments for users on nested projects. + + Test Plan: + + - Enable OS-INHERIT extension + - Create a hierarchy of projects with one root and one leaf project + - Assign an inherited user role on root project + - Assign a non-inherited user role on root project + - Get a list of projects for user, should return both projects + - Disable OS-INHERIT extension + - Get a list of projects for user, should return only root project + + """ + # Enable OS-INHERIT extension + self.config_fixture.config(group='os_inherit', enabled=True) + root_project = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id) + root_project = self.resource_api.create_project(root_project['id'], + root_project) + leaf_project = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id, + parent_id=root_project['id']) + leaf_project = self.resource_api.create_project(leaf_project['id'], + leaf_project) + + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user = self.identity_api.create_user(user) + + # Grant inherited user role + self.assignment_api.create_grant(user_id=user['id'], + project_id=root_project['id'], + role_id=self.role_admin['id'], + inherited_to_projects=True) + # Grant non-inherited user role + self.assignment_api.create_grant(user_id=user['id'], + project_id=root_project['id'], + role_id=self.role_member['id']) + # Should get back both projects: because the direct role assignment for + # the root project and inherited role assignment for leaf project + user_projects = self.assignment_api.list_projects_for_user(user['id']) + self.assertEqual(2, len(user_projects)) + self.assertIn(root_project, user_projects) + self.assertIn(leaf_project, user_projects) + + # Disable OS-INHERIT extension + self.config_fixture.config(group='os_inherit', enabled=False) + # Should get back just root project - due the direct role assignment + user_projects = self.assignment_api.list_projects_for_user(user['id']) + self.assertEqual(1, len(user_projects)) + self.assertIn(root_project, user_projects) + + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemented to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a project and sub-project, plus a user. + # Also, create 2 roles. + 'entities': { + 'domains': {'id': CONF.identity.default_domain_id, 'users': 1, + 'projects': {'project': 1}}, + 'roles': 2}, + # A direct role and an inherited role on the parent + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] - should get back + # one direct role plus one inherited role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'project': 0}}]} + ] + } + + test_plan_with_os_inherit_disabled = { + 'tests': [ + # List all effective assignments for user[0] - should only get + # back the one direct role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + test_data = self.execute_assignment_plan(test_plan) + self.config_fixture.config(group='os_inherit', enabled=False) + # Pass the existing test data in to allow execution of 2nd test plan + self.execute_assignment_cases( + test_plan_with_os_inherit_disabled, test_data) + + def test_list_projects_for_user_with_inherited_group_grants(self): + """Test inherited group roles. + + Test Plan: + + - Enable OS-INHERIT extension + - Create two domains, each with two projects + - Create a user and group + - Make the user a member of the group + - Assign a user role two projects, an inherited + group role to one domain and an inherited regular role on + the other domain + - Get a list of projects for user, should return both pairs of projects + from the domain, plus the one separate project + + """ + self.config_fixture.config(group='os_inherit', enabled=True) + domain = unit.new_domain_ref() + self.resource_api.create_domain(domain['id'], domain) + domain2 = unit.new_domain_ref() + self.resource_api.create_domain(domain2['id'], domain2) + project1 = unit.new_project_ref(domain_id=domain['id']) + self.resource_api.create_project(project1['id'], project1) + project2 = unit.new_project_ref(domain_id=domain['id']) + self.resource_api.create_project(project2['id'], project2) + project3 = unit.new_project_ref(domain_id=domain2['id']) + self.resource_api.create_project(project3['id'], project3) + project4 = unit.new_project_ref(domain_id=domain2['id']) + self.resource_api.create_project(project4['id'], project4) + user1 = unit.new_user_ref(domain_id=domain['id']) + user1 = self.identity_api.create_user(user1) + group1 = unit.new_group_ref(domain_id=domain['id']) + group1 = self.identity_api.create_group(group1) + self.identity_api.add_user_to_group(user1['id'], group1['id']) + + # Create 4 grants: + # - one user grant on a project in domain2 + # - one user grant on a project in the default domain + # - one inherited user grant on domain + # - one inherited group grant on domain2 + self.assignment_api.create_grant(user_id=user1['id'], + project_id=project3['id'], + role_id=self.role_member['id']) + self.assignment_api.create_grant(user_id=user1['id'], + project_id=self.tenant_bar['id'], + role_id=self.role_member['id']) + self.assignment_api.create_grant(user_id=user1['id'], + domain_id=domain['id'], + role_id=self.role_admin['id'], + inherited_to_projects=True) + self.assignment_api.create_grant(group_id=group1['id'], + domain_id=domain2['id'], + role_id=self.role_admin['id'], + inherited_to_projects=True) + # Should get back all five projects, but without a duplicate for + # project3 (since it has both a direct user role and an inherited role) + user_projects = self.assignment_api.list_projects_for_user(user1['id']) + self.assertEqual(5, len(user_projects)) + + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemented to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a 1 project, plus a second domain with 2 projects, + # as well as a user & group and a 3rd domain with 2 projects. + # Also, created 2 roles. + 'entities': {'domains': [{'projects': 1}, + {'users': 1, 'groups': 1, 'projects': 2}, + {'projects': 2}], + 'roles': 2}, + 'group_memberships': [{'group': 0, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 0, 'project': 3}, + {'user': 0, 'role': 1, 'domain': 1, + 'inherited_to_projects': True}, + {'user': 0, 'role': 1, 'domain': 2, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] + # Should get back both direct roles plus roles on both projects + # from each domain. Duplicates should not be filtered out. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 3}, + {'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'domain': 1}}, + {'user': 0, 'role': 1, 'project': 2, + 'indirect': {'domain': 1}}, + {'user': 0, 'role': 1, 'project': 3, + 'indirect': {'domain': 2}}, + {'user': 0, 'role': 1, 'project': 4, + 'indirect': {'domain': 2}}]} + ] + } + self.execute_assignment_plan(test_plan) + + def test_list_projects_for_user_with_inherited_group_project_grants(self): + """Test inherited role assignments for groups on nested projects. + + Test Plan: + + - Enable OS-INHERIT extension + - Create a hierarchy of projects with one root and one leaf project + - Assign an inherited group role on root project + - Assign a non-inherited group role on root project + - Get a list of projects for user, should return both projects + - Disable OS-INHERIT extension + - Get a list of projects for user, should return only root project + + """ + self.config_fixture.config(group='os_inherit', enabled=True) + root_project = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id) + root_project = self.resource_api.create_project(root_project['id'], + root_project) + leaf_project = unit.new_project_ref( + domain_id=CONF.identity.default_domain_id, + parent_id=root_project['id']) + leaf_project = self.resource_api.create_project(leaf_project['id'], + leaf_project) + + user = unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + user = self.identity_api.create_user(user) + + group = unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + group = self.identity_api.create_group(group) + self.identity_api.add_user_to_group(user['id'], group['id']) + + # Grant inherited group role + self.assignment_api.create_grant(group_id=group['id'], + project_id=root_project['id'], + role_id=self.role_admin['id'], + inherited_to_projects=True) + # Grant non-inherited group role + self.assignment_api.create_grant(group_id=group['id'], + project_id=root_project['id'], + role_id=self.role_member['id']) + # Should get back both projects: because the direct role assignment for + # the root project and inherited role assignment for leaf project + user_projects = self.assignment_api.list_projects_for_user(user['id']) + self.assertEqual(2, len(user_projects)) + self.assertIn(root_project, user_projects) + self.assertIn(leaf_project, user_projects) + + # Disable OS-INHERIT extension + self.config_fixture.config(group='os_inherit', enabled=False) + # Should get back just root project - due the direct role assignment + user_projects = self.assignment_api.list_projects_for_user(user['id']) + self.assertEqual(1, len(user_projects)) + self.assertIn(root_project, user_projects) + + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemented to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a project ans sub-project, plus a user. + # Also, create 2 roles. + 'entities': { + 'domains': {'id': CONF.identity.default_domain_id, 'users': 1, + 'groups': 1, + 'projects': {'project': 1}}, + 'roles': 2}, + 'group_memberships': [{'group': 0, 'users': [0]}], + # A direct role and an inherited role on the parent + 'assignments': [{'group': 0, 'role': 0, 'project': 0}, + {'group': 0, 'role': 1, 'project': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] - should get back + # one direct role plus one inherited role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'group': 0, 'project': 0}}]} + ] + } + + test_plan_with_os_inherit_disabled = { + 'tests': [ + # List all effective assignments for user[0] - should only get + # back the one direct role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0, + 'indirect': {'group': 0}}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + test_data = self.execute_assignment_plan(test_plan) + self.config_fixture.config(group='os_inherit', enabled=False) + # Pass the existing test data in to allow execution of 2nd test plan + self.execute_assignment_cases( + test_plan_with_os_inherit_disabled, test_data) + + def test_list_assignments_for_tree(self): + """Test we correctly list direct assignments for a tree""" + # Enable OS-INHERIT extension + self.config_fixture.config(group='os_inherit', enabled=True) + + test_plan = { + # Create a domain with a project hierarchy 3 levels deep: + # + # project 0 + # ____________|____________ + # | | + # project 1 project 4 + # ______|_____ ______|_____ + # | | | | + # project 2 project 3 project 5 project 6 + # + # Also, create 1 user and 4 roles. + 'entities': { + 'domains': { + 'projects': {'project': [{'project': 2}, + {'project': 2}]}, + 'users': 1}, + 'roles': 4}, + 'assignments': [ + # Direct assignment to projects 1 and 2 + {'user': 0, 'role': 0, 'project': 1}, + {'user': 0, 'role': 1, 'project': 2}, + # Also an inherited assignment on project 1 + {'user': 0, 'role': 2, 'project': 1, + 'inherited_to_projects': True}, + # ...and two spoiler assignments, one to the root and one + # to project 4 + {'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 3, 'project': 4}], + 'tests': [ + # List all assignments for project 1 and its subtree. + {'params': {'project': 1, 'include_subtree': True}, + 'results': [ + # Only the actual assignments should be returned, no + # expansion of inherited assignments + {'user': 0, 'role': 0, 'project': 1}, + {'user': 0, 'role': 1, 'project': 2}, + {'user': 0, 'role': 2, 'project': 1, + 'inherited_to_projects': 'projects'}]} + ] + } + + self.execute_assignment_plan(test_plan) + + def test_list_effective_assignments_for_tree(self): + """Test we correctly list effective assignments for a tree""" + # Enable OS-INHERIT extension + self.config_fixture.config(group='os_inherit', enabled=True) + + test_plan = { + # Create a domain with a project hierarchy 3 levels deep: + # + # project 0 + # ____________|____________ + # | | + # project 1 project 4 + # ______|_____ ______|_____ + # | | | | + # project 2 project 3 project 5 project 6 + # + # Also, create 1 user and 4 roles. + 'entities': { + 'domains': { + 'projects': {'project': [{'project': 2}, + {'project': 2}]}, + 'users': 1}, + 'roles': 4}, + 'assignments': [ + # An inherited assignment on project 1 + {'user': 0, 'role': 1, 'project': 1, + 'inherited_to_projects': True}, + # A direct assignment to project 2 + {'user': 0, 'role': 2, 'project': 2}, + # ...and two spoiler assignments, one to the root and one + # to project 4 + {'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 3, 'project': 4}], + 'tests': [ + # List all effective assignments for project 1 and its subtree. + {'params': {'project': 1, 'effective': True, + 'include_subtree': True}, + 'results': [ + # The inherited assignment on project 1 should appear only + # on its children + {'user': 0, 'role': 1, 'project': 2, + 'indirect': {'project': 1}}, + {'user': 0, 'role': 1, 'project': 3, + 'indirect': {'project': 1}}, + # And finally the direct assignment on project 2 + {'user': 0, 'role': 2, 'project': 2}]} + ] + } + + self.execute_assignment_plan(test_plan) + + def test_list_effective_assignments_for_tree_with_mixed_assignments(self): + """Test that we correctly combine assignments for a tree. + + In this test we want to ensure that when asking for a list of + assignments in a subtree, any assignments inherited from above the + subtree are correctly combined with any assignments within the subtree + itself. + + """ + # Enable OS-INHERIT extension + self.config_fixture.config(group='os_inherit', enabled=True) + + test_plan = { + # Create a domain with a project hierarchy 3 levels deep: + # + # project 0 + # ____________|____________ + # | | + # project 1 project 4 + # ______|_____ ______|_____ + # | | | | + # project 2 project 3 project 5 project 6 + # + # Also, create 2 users, 1 group and 4 roles. + 'entities': { + 'domains': { + 'projects': {'project': [{'project': 2}, + {'project': 2}]}, + 'users': 2, 'groups': 1}, + 'roles': 4}, + # Both users are part of the same group + 'group_memberships': [{'group': 0, 'users': [0, 1]}], + # We are going to ask for listing of assignment on project 1 and + # it's subtree. So first we'll add two inherited assignments above + # this (one user and one for a group that contains this user). + 'assignments': [{'user': 0, 'role': 0, 'project': 0, + 'inherited_to_projects': True}, + {'group': 0, 'role': 1, 'project': 0, + 'inherited_to_projects': True}, + # Now an inherited assignment on project 1 itself, + # which should ONLY show up on its children + {'user': 0, 'role': 2, 'project': 1, + 'inherited_to_projects': True}, + # ...and a direct assignment on one of those + # children + {'user': 0, 'role': 3, 'project': 2}, + # The rest are spoiler assignments + {'user': 0, 'role': 2, 'project': 5}, + {'user': 0, 'role': 3, 'project': 4}], + 'tests': [ + # List all effective assignments for project 1 and its subtree. + {'params': {'project': 1, 'user': 0, 'effective': True, + 'include_subtree': True}, + 'results': [ + # First, we should see the inherited user assignment from + # project 0 on all projects in the subtree + {'user': 0, 'role': 0, 'project': 1, + 'indirect': {'project': 0}}, + {'user': 0, 'role': 0, 'project': 2, + 'indirect': {'project': 0}}, + {'user': 0, 'role': 0, 'project': 3, + 'indirect': {'project': 0}}, + # Also the inherited group assignment from project 0 on + # the subtree + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'project': 0, 'group': 0}}, + {'user': 0, 'role': 1, 'project': 2, + 'indirect': {'project': 0, 'group': 0}}, + {'user': 0, 'role': 1, 'project': 3, + 'indirect': {'project': 0, 'group': 0}}, + # The inherited assignment on project 1 should appear only + # on its children + {'user': 0, 'role': 2, 'project': 2, + 'indirect': {'project': 1}}, + {'user': 0, 'role': 2, 'project': 3, + 'indirect': {'project': 1}}, + # And finally the direct assignment on project 2 + {'user': 0, 'role': 3, 'project': 2}]} + ] + } + + self.execute_assignment_plan(test_plan) + + def test_list_effective_assignments_for_tree_with_domain_assignments(self): + """Test we correctly honor domain inherited assignments on the tree""" + # Enable OS-INHERIT extension + self.config_fixture.config(group='os_inherit', enabled=True) + + test_plan = { + # Create a domain with a project hierarchy 3 levels deep: + # + # project 0 + # ____________|____________ + # | | + # project 1 project 4 + # ______|_____ ______|_____ + # | | | | + # project 2 project 3 project 5 project 6 + # + # Also, create 1 user and 4 roles. + 'entities': { + 'domains': { + 'projects': {'project': [{'project': 2}, + {'project': 2}]}, + 'users': 1}, + 'roles': 4}, + 'assignments': [ + # An inherited assignment on the domain (which should be + # applied to all the projects) + {'user': 0, 'role': 1, 'domain': 0, + 'inherited_to_projects': True}, + # A direct assignment to project 2 + {'user': 0, 'role': 2, 'project': 2}, + # ...and two spoiler assignments, one to the root and one + # to project 4 + {'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 3, 'project': 4}], + 'tests': [ + # List all effective assignments for project 1 and its subtree. + {'params': {'project': 1, 'effective': True, + 'include_subtree': True}, + 'results': [ + # The inherited assignment from the domain should appear + # only on the part of the subtree we are interested in + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'domain': 0}}, + {'user': 0, 'role': 1, 'project': 2, + 'indirect': {'domain': 0}}, + {'user': 0, 'role': 1, 'project': 3, + 'indirect': {'domain': 0}}, + # And finally the direct assignment on project 2 + {'user': 0, 'role': 2, 'project': 2}]} + ] + } + + self.execute_assignment_plan(test_plan) + + def test_list_user_ids_for_project_with_inheritance(self): + test_plan = { + # A domain with a project and sub-project, plus four users, + # two groups, as well as 4 roles. + 'entities': { + 'domains': {'id': CONF.identity.default_domain_id, 'users': 4, + 'groups': 2, + 'projects': {'project': 1}}, + 'roles': 4}, + # Each group has a unique user member + 'group_memberships': [{'group': 0, 'users': [1]}, + {'group': 1, 'users': [3]}], + # Set up assignments so that there should end up with four + # effective assignments on project 1 - one direct, one due to + # group membership and one user assignment inherited from the + # parent and one group assignment inhertied from the parent. + 'assignments': [{'user': 0, 'role': 0, 'project': 1}, + {'group': 0, 'role': 1, 'project': 1}, + {'user': 2, 'role': 2, 'project': 0, + 'inherited_to_projects': True}, + {'group': 1, 'role': 3, 'project': 0, + 'inherited_to_projects': True}], + } + # Use assignment plan helper to create all the entities and + # assignments - then we'll run our own tests using the data + test_data = self.execute_assignment_plan(test_plan) + self.config_fixture.config(group='os_inherit', enabled=True) + user_ids = self.assignment_api.list_user_ids_for_project( + test_data['projects'][1]['id']) + self.assertThat(user_ids, matchers.HasLength(4)) + for x in range(0, 4): + self.assertIn(test_data['users'][x]['id'], user_ids) + + def test_list_role_assignment_using_inherited_sourced_groups(self): + """Test listing inherited assignments when restricted by groups.""" + test_plan = { + # A domain with 3 users, 3 groups, 3 projects, a second domain, + # plus 3 roles. + 'entities': {'domains': [{'users': 3, 'groups': 3, 'projects': 3}, + 1], + 'roles': 3}, + # Users 0 & 1 are in the group 0, User 0 also in group 1 + 'group_memberships': [{'group': 0, 'users': [0, 1]}, + {'group': 1, 'users': [0]}], + # Spread the assignments around - we want to be able to show that + # if sourced by group, assignments from other sources are excluded + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'group': 0, 'role': 1, 'domain': 1}, + {'group': 1, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'group': 1, 'role': 2, 'project': 1}, + {'user': 2, 'role': 1, 'project': 1, + 'inherited_to_projects': True}, + {'group': 2, 'role': 2, 'project': 2} + ], + 'tests': [ + # List all effective assignments sourced from groups 0 and 1. + # We should see the inherited group assigned on the 3 projects + # from domain 0, as well as the direct assignments. + {'params': {'source_from_group_ids': [0, 1], + 'effective': True}, + 'results': [{'group': 0, 'role': 1, 'domain': 1}, + {'group': 1, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}, + {'group': 1, 'role': 2, 'project': 1, + 'indirect': {'domain': 0}}, + {'group': 1, 'role': 2, 'project': 2, + 'indirect': {'domain': 0}}, + {'group': 1, 'role': 2, 'project': 1} + ]}, + ] + } + self.execute_assignment_plan(test_plan) + + +class ImpliedRoleTests(AssignmentTestHelperMixin): + + def test_implied_role_crd(self): + prior_role_ref = unit.new_role_ref() + self.role_api.create_role(prior_role_ref['id'], prior_role_ref) + implied_role_ref = unit.new_role_ref() + self.role_api.create_role(implied_role_ref['id'], implied_role_ref) + + self.role_api.create_implied_role( + prior_role_ref['id'], + implied_role_ref['id']) + implied_role = self.role_api.get_implied_role( + prior_role_ref['id'], + implied_role_ref['id']) + expected_implied_role_ref = { + 'prior_role_id': prior_role_ref['id'], + 'implied_role_id': implied_role_ref['id']} + self.assertDictContainsSubset( + expected_implied_role_ref, + implied_role) + + self.role_api.delete_implied_role( + prior_role_ref['id'], + implied_role_ref['id']) + self.assertRaises(exception.ImpliedRoleNotFound, + self.role_api.get_implied_role, + uuid.uuid4().hex, + uuid.uuid4().hex) + + def test_delete_implied_role_returns_not_found(self): + self.assertRaises(exception.ImpliedRoleNotFound, + self.role_api.delete_implied_role, + uuid.uuid4().hex, + uuid.uuid4().hex) + + def test_role_assignments_simple_tree_of_implied_roles(self): + """Test that implied roles are expanded out.""" + test_plan = { + 'entities': {'domains': {'users': 1, 'projects': 1}, + 'roles': 4}, + # Three level tree of implied roles + 'implied_roles': [{'role': 0, 'implied_roles': 1}, + {'role': 1, 'implied_roles': [2, 3]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}], + 'tests': [ + # List all direct assignments for user[0], this should just + # show the one top level role assignment + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'project': 0}]}, + # Listing in effective mode should show the implied roles + # expanded out + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 0, + 'indirect': {'role': 0}}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'role': 1}}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'role': 1}}]}, + ] + } + self.execute_assignment_plan(test_plan) + + def test_circular_inferences(self): + """Test that implied roles are expanded out.""" + test_plan = { + 'entities': {'domains': {'users': 1, 'projects': 1}, + 'roles': 4}, + # Three level tree of implied roles + 'implied_roles': [{'role': 0, 'implied_roles': [1]}, + {'role': 1, 'implied_roles': [2, 3]}, + {'role': 3, 'implied_roles': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}], + 'tests': [ + # List all direct assignments for user[0], this should just + # show the one top level role assignment + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'project': 0}]}, + # Listing in effective mode should show the implied roles + # expanded out + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 0, 'project': 0, + 'indirect': {'role': 3}}, + {'user': 0, 'role': 1, 'project': 0, + 'indirect': {'role': 0}}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'role': 1}}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'role': 1}}]}, + ] + } + self.execute_assignment_plan(test_plan) + + def test_role_assignments_directed_graph_of_implied_roles(self): + """Test that a role can have multiple, different prior roles.""" + test_plan = { + 'entities': {'domains': {'users': 1, 'projects': 1}, + 'roles': 6}, + # Three level tree of implied roles, where one of the roles at the + # bottom is implied by more than one top level role + 'implied_roles': [{'role': 0, 'implied_roles': [1, 2]}, + {'role': 1, 'implied_roles': [3, 4]}, + {'role': 5, 'implied_roles': 4}], + # The user gets both top level roles + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 5, 'project': 0}], + 'tests': [ + # The implied roles should be expanded out and there should be + # two entries for the role that had two different prior roles. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 5, 'project': 0}, + {'user': 0, 'role': 1, 'project': 0, + 'indirect': {'role': 0}}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'role': 0}}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'role': 1}}, + {'user': 0, 'role': 4, 'project': 0, + 'indirect': {'role': 1}}, + {'user': 0, 'role': 4, 'project': 0, + 'indirect': {'role': 5}}]}, + ] + } + test_data = self.execute_assignment_plan(test_plan) + + # We should also be able to get a similar (yet summarized) answer to + # the above by calling get_roles_for_user_and_project(), which should + # list the role_ids, yet remove any duplicates + role_ids = self.assignment_api.get_roles_for_user_and_project( + test_data['users'][0]['id'], test_data['projects'][0]['id']) + # We should see 6 entries, not 7, since role index 5 appeared twice in + # the answer from list_role_assignments + self.assertThat(role_ids, matchers.HasLength(6)) + for x in range(0, 5): + self.assertIn(test_data['roles'][x]['id'], role_ids) + + def test_role_assignments_implied_roles_filtered_by_role(self): + """Test that you can filter by role even if roles are implied.""" + test_plan = { + 'entities': {'domains': {'users': 1, 'projects': 2}, + 'roles': 4}, + # Three level tree of implied roles + 'implied_roles': [{'role': 0, 'implied_roles': 1}, + {'role': 1, 'implied_roles': [2, 3]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 3, 'project': 1}], + 'tests': [ + # List effective roles filtering by one of the implied roles, + # showing that the filter was implied post expansion of + # implied roles (and that non impled roles are included in + # the filter + {'params': {'role': 3, 'effective': True}, + 'results': [{'user': 0, 'role': 3, 'project': 0, + 'indirect': {'role': 1}}, + {'user': 0, 'role': 3, 'project': 1}]}, + ] + } + self.execute_assignment_plan(test_plan) + + def test_role_assignments_simple_tree_of_implied_roles_on_domain(self): + """Test that implied roles are expanded out when placed on a domain.""" + test_plan = { + 'entities': {'domains': {'users': 1}, + 'roles': 4}, + # Three level tree of implied roles + 'implied_roles': [{'role': 0, 'implied_roles': 1}, + {'role': 1, 'implied_roles': [2, 3]}], + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}], + 'tests': [ + # List all direct assignments for user[0], this should just + # show the one top level role assignment + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}]}, + # Listing in effective mode should how the implied roles + # expanded out + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'domain': 0, + 'indirect': {'role': 0}}, + {'user': 0, 'role': 2, 'domain': 0, + 'indirect': {'role': 1}}, + {'user': 0, 'role': 3, 'domain': 0, + 'indirect': {'role': 1}}]}, + ] + } + self.execute_assignment_plan(test_plan) + + def test_role_assignments_inherited_implied_roles(self): + """Test that you can intermix inherited and implied roles.""" + test_plan = { + 'entities': {'domains': {'users': 1, 'projects': 1}, + 'roles': 4}, + # Simply one level of implied roles + 'implied_roles': [{'role': 0, 'implied_roles': 1}], + # Assign to top level role as an inherited assignment to the + # domain + 'assignments': [{'user': 0, 'role': 0, 'domain': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all direct assignments for user[0], this should just + # show the one top level role assignment + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'domain': 0, + 'inherited_to_projects': 'projects'}]}, + # List in effective mode - we should only see the initial and + # implied role on the project (since inherited roles are not + # active on their anchor point). + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0, + 'indirect': {'domain': 0}}, + {'user': 0, 'role': 1, 'project': 0, + 'indirect': {'domain': 0, 'role': 0}}]}, + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + self.execute_assignment_plan(test_plan) + + def test_role_assignments_domain_specific_with_implied_roles(self): + test_plan = { + 'entities': {'domains': {'users': 1, 'projects': 1, 'roles': 2}, + 'roles': 2}, + # Two level tree of implied roles, with the top and 1st level being + # domain specific roles, and the bottom level being infered global + # roles. + 'implied_roles': [{'role': 0, 'implied_roles': [1]}, + {'role': 1, 'implied_roles': [2, 3]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}], + 'tests': [ + # List all direct assignments for user[0], this should just + # show the one top level role assignment, even though this is a + # domain specific role (since we are in non-effective mode and + # we show any direct role assignment in that mode). + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'project': 0}]}, + # Now the effective ones - so the implied roles should be + # expanded out, as well as any domain specific roles should be + # removed. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 2, 'project': 0, + 'indirect': {'role': 1}}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'role': 1}}]}, + ] + } + self.execute_assignment_plan(test_plan) |