List assignments with names
When a client calls list assignment API what is returned is the role id, user id or group id, and project id or domain id. Most users then call the api again for each of these entities to get their names, creating many api calls between the client and server. This can be reduced by having the server do all the work instead. This commit adds the functionality to include the user, role, group, project, and domain names with the response if the parameter 'include_names' is set to True. Change-Id: I0a1cc986b8a35aeafe567e5e7fee6eeb848ae113 Closes-Bug: #1479569 Implements: blueprint list-assignment-with-names
This commit is contained in:
parent
d78fcc361e
commit
dc212cd4d2
@ -514,8 +514,15 @@ class RoleAssignmentV3(controller.V3Controller):
|
||||
inherited_assignment = entity.get('inherited_to_projects')
|
||||
|
||||
if 'project_id' in entity:
|
||||
formatted_entity['scope'] = {
|
||||
'project': {'id': entity['project_id']}}
|
||||
if 'project_name' in entity:
|
||||
formatted_entity['scope'] = {'project': {
|
||||
'id': entity['project_id'],
|
||||
'name': entity['project_name'],
|
||||
'domain': {'id': entity['project_domain_id'],
|
||||
'name': entity['project_domain_name']}}}
|
||||
else:
|
||||
formatted_entity['scope'] = {
|
||||
'project': {'id': entity['project_id']}}
|
||||
|
||||
if 'domain_id' in entity.get('indirect', {}):
|
||||
inherited_assignment = True
|
||||
@ -528,12 +535,24 @@ class RoleAssignmentV3(controller.V3Controller):
|
||||
else:
|
||||
formatted_link = '/projects/%s' % entity['project_id']
|
||||
elif 'domain_id' in entity:
|
||||
formatted_entity['scope'] = {'domain': {'id': entity['domain_id']}}
|
||||
if 'domain_name' in entity:
|
||||
formatted_entity['scope'] = {
|
||||
'domain': {'id': entity['domain_id'],
|
||||
'name': entity['domain_name']}}
|
||||
else:
|
||||
formatted_entity['scope'] = {
|
||||
'domain': {'id': entity['domain_id']}}
|
||||
formatted_link = '/domains/%s' % entity['domain_id']
|
||||
|
||||
if 'user_id' in entity:
|
||||
formatted_entity['user'] = {'id': entity['user_id']}
|
||||
|
||||
if 'user_name' in entity:
|
||||
formatted_entity['user'] = {
|
||||
'id': entity['user_id'],
|
||||
'name': entity['user_name'],
|
||||
'domain': {'id': entity['user_domain_id'],
|
||||
'name': entity['user_domain_name']}}
|
||||
else:
|
||||
formatted_entity['user'] = {'id': entity['user_id']}
|
||||
if 'group_id' in entity.get('indirect', {}):
|
||||
membership_url = (
|
||||
self.base_url(context, '/groups/%s/users/%s' % (
|
||||
@ -543,10 +562,21 @@ class RoleAssignmentV3(controller.V3Controller):
|
||||
else:
|
||||
formatted_link += '/users/%s' % entity['user_id']
|
||||
elif 'group_id' in entity:
|
||||
formatted_entity['group'] = {'id': entity['group_id']}
|
||||
if 'group_name' in entity:
|
||||
formatted_entity['group'] = {
|
||||
'id': entity['group_id'],
|
||||
'name': entity['group_name'],
|
||||
'domain': {'id': entity['group_domain_id'],
|
||||
'name': entity['group_domain_name']}}
|
||||
else:
|
||||
formatted_entity['group'] = {'id': entity['group_id']}
|
||||
formatted_link += '/groups/%s' % entity['group_id']
|
||||
|
||||
formatted_entity['role'] = {'id': entity['role_id']}
|
||||
if 'role_name' in entity:
|
||||
formatted_entity['role'] = {'id': entity['role_id'],
|
||||
'name': entity['role_name']}
|
||||
else:
|
||||
formatted_entity['role'] = {'id': entity['role_id']}
|
||||
formatted_link += '/roles/%s' % entity['role_id']
|
||||
|
||||
if inherited_assignment:
|
||||
@ -616,6 +646,8 @@ class RoleAssignmentV3(controller.V3Controller):
|
||||
params = context['query_string']
|
||||
effective = 'effective' in params and (
|
||||
self.query_filter_is_true(params['effective']))
|
||||
include_names = ('include_names' in params and
|
||||
self.query_filter_is_true(params['include_names']))
|
||||
|
||||
if 'scope.OS-INHERIT:inherited_to' in params:
|
||||
inherited = (
|
||||
@ -642,7 +674,8 @@ class RoleAssignmentV3(controller.V3Controller):
|
||||
domain_id=params.get('scope.domain.id'),
|
||||
project_id=params.get('scope.project.id'),
|
||||
include_subtree=include_subtree,
|
||||
inherited=inherited, effective=effective)
|
||||
inherited=inherited, effective=effective,
|
||||
include_names=include_names)
|
||||
|
||||
formatted_refs = [self._format_entity(context, ref) for ref in refs]
|
||||
|
||||
|
@ -823,7 +823,7 @@ class Manager(manager.Manager):
|
||||
def list_role_assignments(self, role_id=None, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
include_subtree=False, inherited=None,
|
||||
effective=None):
|
||||
effective=None, include_names=False):
|
||||
"""List role assignments, honoring effective mode and provided filters.
|
||||
|
||||
Returns a list of role assignments, where their attributes match the
|
||||
@ -841,6 +841,9 @@ class Manager(manager.Manager):
|
||||
Think of effective mode as being the list of assignments that actually
|
||||
affect a user, for example the roles that would be placed in a token.
|
||||
|
||||
If include_names is set to true the entities' names are returned
|
||||
in addition to their id's
|
||||
|
||||
If OS-INHERIT extension is disabled or the used driver does not support
|
||||
inherited roles retrieval, inherited role assignments will be ignored.
|
||||
|
||||
@ -857,14 +860,59 @@ class Manager(manager.Manager):
|
||||
self.resource_api.list_projects_in_subtree(project_id)])
|
||||
|
||||
if effective:
|
||||
return self._list_effective_role_assignments(
|
||||
role_assignments = self._list_effective_role_assignments(
|
||||
role_id, user_id, group_id, domain_id, project_id,
|
||||
subtree_ids, inherited)
|
||||
else:
|
||||
return self._list_direct_role_assignments(
|
||||
role_assignments = self._list_direct_role_assignments(
|
||||
role_id, user_id, group_id, domain_id, project_id,
|
||||
subtree_ids, inherited)
|
||||
|
||||
if include_names:
|
||||
return self._get_names_from_role_assignments(role_assignments)
|
||||
return role_assignments
|
||||
|
||||
def _get_names_from_role_assignments(self, role_assignments):
|
||||
role_assign_list = []
|
||||
|
||||
for role_asgmt in role_assignments:
|
||||
new_assign = {}
|
||||
for id_type, id_ in role_asgmt.items():
|
||||
if id_type == 'domain_id':
|
||||
_domain = self.resource_api.get_domain(id_)
|
||||
new_assign['domain_id'] = _domain['id']
|
||||
new_assign['domain_name'] = _domain['name']
|
||||
elif id_type == 'user_id':
|
||||
_user = self.identity_api.get_user(id_)
|
||||
new_assign['user_id'] = _user['id']
|
||||
new_assign['user_name'] = _user['name']
|
||||
new_assign['user_domain_id'] = _user['domain_id']
|
||||
new_assign['user_domain_name'] = (
|
||||
self.resource_api.get_domain(_user['domain_id'])
|
||||
['name'])
|
||||
elif id_type == 'group_id':
|
||||
_group = self.identity_api.get_group(id_)
|
||||
new_assign['group_id'] = _group['id']
|
||||
new_assign['group_name'] = _group['name']
|
||||
new_assign['group_domain_id'] = _group['domain_id']
|
||||
new_assign['group_domain_name'] = (
|
||||
self.resource_api.get_domain(_group['domain_id'])
|
||||
['name'])
|
||||
elif id_type == 'project_id':
|
||||
_project = self.resource_api.get_project(id_)
|
||||
new_assign['project_id'] = _project['id']
|
||||
new_assign['project_name'] = _project['name']
|
||||
new_assign['project_domain_id'] = _project['domain_id']
|
||||
new_assign['project_domain_name'] = (
|
||||
self.resource_api.get_domain(_project['domain_id'])
|
||||
['name'])
|
||||
elif id_type == 'role_id':
|
||||
_role = self.role_api.get_role(id_)
|
||||
new_assign['role_id'] = _role['id']
|
||||
new_assign['role_name'] = _role['name']
|
||||
role_assign_list.append(new_assign)
|
||||
return role_assign_list
|
||||
|
||||
def delete_tokens_for_role_assignments(self, role_id):
|
||||
assignments = self.list_role_assignments(role_id=role_id)
|
||||
|
||||
|
@ -4127,6 +4127,80 @@ class IdentityTests(AssignmentTestHelperMixin):
|
||||
{'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'])
|
||||
|
||||
|
||||
class TokenTests(object):
|
||||
def _create_token_id(self):
|
||||
|
@ -329,6 +329,9 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
|
||||
def test_delete_group_with_user_project_domain_links(self):
|
||||
self.skipTest('N/A: LDAP does not support multiple domains')
|
||||
|
||||
def test_list_role_assignment_containing_names(self):
|
||||
self.skipTest('N/A: LDAP does not support multiple domains')
|
||||
|
||||
def test_list_projects_for_user(self):
|
||||
domain = self._get_domain_fixture()
|
||||
user1 = self.new_user_ref(domain_id=domain['id'])
|
||||
|
@ -1321,3 +1321,64 @@ class AssignmentTestMixin(object):
|
||||
entity['scope']['OS-INHERIT:inherited_to'] = 'projects'
|
||||
|
||||
return entity
|
||||
|
||||
def build_role_assignment_entity_include_names(self,
|
||||
domain_ref=None,
|
||||
role_ref=None,
|
||||
group_ref=None,
|
||||
user_ref=None,
|
||||
project_ref=None,
|
||||
inherited_assignment=None):
|
||||
"""Build and return a role assignment entity with provided attributes.
|
||||
|
||||
The expected attributes are: domain_ref or project_ref,
|
||||
user_ref or group_ref, role_ref and, optionally, inherited_to_projects.
|
||||
"""
|
||||
entity = {'links': {}}
|
||||
attributes_for_links = {}
|
||||
if project_ref:
|
||||
dmn_name = self.resource_api.get_domain(
|
||||
project_ref['domain_id'])['name']
|
||||
|
||||
entity['scope'] = {'project': {
|
||||
'id': project_ref['id'],
|
||||
'name': project_ref['name'],
|
||||
'domain': {
|
||||
'id': project_ref['domain_id'],
|
||||
'name': dmn_name}}}
|
||||
attributes_for_links['project_id'] = project_ref['id']
|
||||
else:
|
||||
entity['scope'] = {'domain': {'id': domain_ref['id'],
|
||||
'name': domain_ref['name']}}
|
||||
attributes_for_links['domain_id'] = domain_ref['id']
|
||||
if user_ref:
|
||||
dmn_name = self.resource_api.get_domain(
|
||||
user_ref['domain_id'])['name']
|
||||
entity['user'] = {'id': user_ref['id'],
|
||||
'name': user_ref['name'],
|
||||
'domain': {'id': user_ref['domain_id'],
|
||||
'name': dmn_name}}
|
||||
attributes_for_links['user_id'] = user_ref['id']
|
||||
else:
|
||||
dmn_name = self.resource_api.get_domain(
|
||||
group_ref['domain_id'])['name']
|
||||
entity['group'] = {'id': group_ref['id'],
|
||||
'name': group_ref['name'],
|
||||
'domain': {
|
||||
'id': group_ref['domain_id'],
|
||||
'name': dmn_name}}
|
||||
attributes_for_links['group_id'] = group_ref['id']
|
||||
|
||||
if role_ref:
|
||||
entity['role'] = {'id': role_ref['id'],
|
||||
'name': role_ref['name']}
|
||||
attributes_for_links['role_id'] = role_ref['id']
|
||||
|
||||
if inherited_assignment:
|
||||
entity['scope']['OS-INHERIT:inherited_to'] = 'projects'
|
||||
attributes_for_links['inherited_to_projects'] = True
|
||||
|
||||
entity['links']['assignment'] = self.build_role_assignment_link(
|
||||
**attributes_for_links)
|
||||
|
||||
return entity
|
||||
|
@ -1429,6 +1429,98 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase,
|
||||
inherited_to_projects=True)
|
||||
self.assertRoleAssignmentInListResponse(r, up_entity)
|
||||
|
||||
def test_list_role_assignments_include_names(self):
|
||||
"""Call ``GET /role_assignments with include names``.
|
||||
|
||||
Test Plan:
|
||||
|
||||
- Create a domain with a group and a user
|
||||
- Create a project with a group and a user
|
||||
|
||||
"""
|
||||
role1 = unit.new_role_ref()
|
||||
self.role_api.create_role(role1['id'], role1)
|
||||
user1 = unit.create_user(self.identity_api, domain_id=self.domain_id)
|
||||
group = unit.new_group_ref(domain_id=self.domain_id)
|
||||
group = self.identity_api.create_group(group)
|
||||
project1 = unit.new_project_ref(domain_id=self.domain_id)
|
||||
self.resource_api.create_project(project1['id'], project1)
|
||||
|
||||
expected_entity1 = self.build_role_assignment_entity_include_names(
|
||||
role_ref=role1,
|
||||
project_ref=project1,
|
||||
user_ref=user1)
|
||||
self.put(expected_entity1['links']['assignment'])
|
||||
expected_entity2 = self.build_role_assignment_entity_include_names(
|
||||
role_ref=role1,
|
||||
domain_ref=self.domain,
|
||||
group_ref=group)
|
||||
self.put(expected_entity2['links']['assignment'])
|
||||
expected_entity3 = self.build_role_assignment_entity_include_names(
|
||||
role_ref=role1,
|
||||
domain_ref=self.domain,
|
||||
user_ref=user1)
|
||||
self.put(expected_entity3['links']['assignment'])
|
||||
expected_entity4 = self.build_role_assignment_entity_include_names(
|
||||
role_ref=role1,
|
||||
project_ref=project1,
|
||||
group_ref=group)
|
||||
self.put(expected_entity4['links']['assignment'])
|
||||
|
||||
collection_url_domain = (
|
||||
'/role_assignments?include_names&scope.domain.id=%(domain_id)s' % {
|
||||
'domain_id': self.domain_id})
|
||||
rs_domain = self.get(collection_url_domain)
|
||||
collection_url_project = (
|
||||
'/role_assignments?include_names&'
|
||||
'scope.project.id=%(project_id)s' % {
|
||||
'project_id': project1['id']})
|
||||
rs_project = self.get(collection_url_project)
|
||||
collection_url_group = (
|
||||
'/role_assignments?include_names&group.id=%(group_id)s' % {
|
||||
'group_id': group['id']})
|
||||
rs_group = self.get(collection_url_group)
|
||||
collection_url_user = (
|
||||
'/role_assignments?include_names&user.id=%(user_id)s' % {
|
||||
'user_id': user1['id']})
|
||||
rs_user = self.get(collection_url_user)
|
||||
collection_url_role = (
|
||||
'/role_assignments?include_names&role.id=%(role_id)s' % {
|
||||
'role_id': role1['id']})
|
||||
rs_role = self.get(collection_url_role)
|
||||
# Make sure all entities were created successfully
|
||||
self.assertEqual(rs_domain.status_int, http_client.OK)
|
||||
self.assertEqual(rs_project.status_int, http_client.OK)
|
||||
self.assertEqual(rs_group.status_int, http_client.OK)
|
||||
self.assertEqual(rs_user.status_int, http_client.OK)
|
||||
# Make sure we can get back the correct number of entities
|
||||
self.assertValidRoleAssignmentListResponse(
|
||||
rs_domain,
|
||||
expected_length=2,
|
||||
resource_url=collection_url_domain)
|
||||
self.assertValidRoleAssignmentListResponse(
|
||||
rs_project,
|
||||
expected_length=2,
|
||||
resource_url=collection_url_project)
|
||||
self.assertValidRoleAssignmentListResponse(
|
||||
rs_group,
|
||||
expected_length=2,
|
||||
resource_url=collection_url_group)
|
||||
self.assertValidRoleAssignmentListResponse(
|
||||
rs_user,
|
||||
expected_length=2,
|
||||
resource_url=collection_url_user)
|
||||
self.assertValidRoleAssignmentListResponse(
|
||||
rs_role,
|
||||
expected_length=4,
|
||||
resource_url=collection_url_role)
|
||||
# Verify all types of entities have the correct format
|
||||
self.assertRoleAssignmentInListResponse(rs_domain, expected_entity2)
|
||||
self.assertRoleAssignmentInListResponse(rs_project, expected_entity1)
|
||||
self.assertRoleAssignmentInListResponse(rs_group, expected_entity4)
|
||||
self.assertRoleAssignmentInListResponse(rs_user, expected_entity3)
|
||||
self.assertRoleAssignmentInListResponse(rs_role, expected_entity1)
|
||||
|
||||
def test_list_role_assignments_for_disabled_inheritance_extension(self):
|
||||
"""Call ``GET /role_assignments with inherited domain grants``.
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- >
|
||||
[`bug 1479569 <https://bugs.launchpad.net/keystone/+bug/1479569>`_]
|
||||
Names have been added to list role assignments, rather than returning
|
||||
just the internal ids of the objects the names are also returned.
|
Loading…
Reference in New Issue
Block a user