Add system role functionality

This commit adds the necessary bits to be able to use system
role assignments from python-keystoneclient.

bp system-scope

Change-Id: Iecbcbf020a15f2bec777334c648d4477f89f3b2c
This commit is contained in:
Lance Bragstad 2018-01-22 18:09:25 +00:00
parent e5d44e5cea
commit 1e435e6dcd
3 changed files with 177 additions and 43 deletions

View File

@ -23,6 +23,32 @@ class RoleAssignmentsTests(utils.ClientTestCase, utils.CrudTests):
self.collection_key = 'role_assignments' self.collection_key = 'role_assignments'
self.model = role_assignments.RoleAssignment self.model = role_assignments.RoleAssignment
self.manager = self.client.role_assignments self.manager = self.client.role_assignments
self.TEST_USER_SYSTEM_LIST = [{
'role': {
'id': self.TEST_ROLE_ID
},
'scope': {
'system': {
'all': True
}
},
'user': {
'id': self.TEST_USER_ID
}
}]
self.TEST_GROUP_SYSTEM_LIST = [{
'role': {
'id': self.TEST_ROLE_ID
},
'scope': {
'system': {
'all': True
}
},
'group': {
'id': self.TEST_GROUP_ID
}
}]
self.TEST_USER_DOMAIN_LIST = [{ self.TEST_USER_DOMAIN_LIST = [{
'role': { 'role': {
'id': self.TEST_ROLE_ID 'id': self.TEST_ROLE_ID
@ -65,7 +91,9 @@ class RoleAssignmentsTests(utils.ClientTestCase, utils.CrudTests):
self.TEST_ALL_RESPONSE_LIST = (self.TEST_USER_PROJECT_LIST + self.TEST_ALL_RESPONSE_LIST = (self.TEST_USER_PROJECT_LIST +
self.TEST_GROUP_PROJECT_LIST + self.TEST_GROUP_PROJECT_LIST +
self.TEST_USER_DOMAIN_LIST) self.TEST_USER_DOMAIN_LIST +
self.TEST_USER_SYSTEM_LIST +
self.TEST_GROUP_SYSTEM_LIST)
def _assert_returned_list(self, ref_list, returned_list): def _assert_returned_list(self, ref_list, returned_list):
self.assertEqual(len(ref_list), len(returned_list)) self.assertEqual(len(ref_list), len(returned_list))
@ -150,6 +178,50 @@ class RoleAssignmentsTests(utils.ClientTestCase, utils.CrudTests):
kwargs = {'scope.domain.id': self.TEST_DOMAIN_ID} kwargs = {'scope.domain.id': self.TEST_DOMAIN_ID}
self.assertQueryStringContains(**kwargs) self.assertQueryStringContains(**kwargs)
def test_system_assignment_list(self):
ref_list = self.TEST_USER_SYSTEM_LIST + self.TEST_GROUP_SYSTEM_LIST
self.stub_entity('GET',
[self.collection_key, '?scope.system=all'],
entity=ref_list)
returned_list = self.manager.list(system='all')
self._assert_returned_list(ref_list, returned_list)
kwargs = {'scope.system': 'all'}
self.assertQueryStringContains(**kwargs)
def test_system_assignment_list_for_user(self):
ref_list = self.TEST_USER_SYSTEM_LIST
self.stub_entity('GET',
[self.collection_key,
'?user.id=%s&scope.system=all' % self.TEST_USER_ID],
entity=ref_list)
returned_list = self.manager.list(system='all', user=self.TEST_USER_ID)
self._assert_returned_list(ref_list, returned_list)
kwargs = {'scope.system': 'all', 'user.id': self.TEST_USER_ID}
self.assertQueryStringContains(**kwargs)
def test_system_assignment_list_for_group(self):
ref_list = self.TEST_GROUP_SYSTEM_LIST
self.stub_entity(
'GET',
[self.collection_key,
'?group.id=%s&scope.system=all' % self.TEST_GROUP_ID],
entity=ref_list)
returned_list = self.manager.list(
system='all', group=self.TEST_GROUP_ID
)
self._assert_returned_list(ref_list, returned_list)
kwargs = {'scope.system': 'all', 'group.id': self.TEST_GROUP_ID}
self.assertQueryStringContains(**kwargs)
def test_group_assignments_list(self): def test_group_assignments_list(self):
ref_list = self.TEST_GROUP_PROJECT_LIST ref_list = self.TEST_GROUP_PROJECT_LIST
self.stub_entity('GET', self.stub_entity('GET',

View File

@ -46,9 +46,25 @@ class RoleAssignmentManager(base.CrudManager):
msg = _('Specify either a domain or project, not both') msg = _('Specify either a domain or project, not both')
raise exceptions.ValidationError(msg) raise exceptions.ValidationError(msg)
def list(self, user=None, group=None, project=None, domain=None, role=None, def _check_not_system_and_domain(self, system, domain):
effective=False, os_inherit_extension_inherited_to=None, if system and domain:
include_subtree=False, include_names=False): msg = _('Specify either system or domain, not both')
raise exceptions.ValidationError(msg)
def _check_not_system_and_project(self, system, project):
if system and project:
msg = _('Specify either system or project, not both')
raise exceptions.ValidationError(msg)
def _check_system_value(self, system):
if system and system != 'all':
msg = _("Only a system scope of 'all' is currently supported")
raise exceptions.ValidationError(msg)
def list(self, user=None, group=None, project=None, domain=None,
system=False, role=None, effective=False,
os_inherit_extension_inherited_to=None, include_subtree=False,
include_names=False):
"""List role assignments. """List role assignments.
If no arguments are provided, all role assignments in the If no arguments are provided, all role assignments in the
@ -64,6 +80,8 @@ class RoleAssignmentManager(base.CrudManager):
(optional) (optional)
:param domain: Domain to be used as query :param domain: Domain to be used as query
filter. (optional) filter. (optional)
:param system: Boolean to be used to filter system assignments.
(optional)
:param role: Role to be used as query filter. (optional) :param role: Role to be used as query filter. (optional)
:param boolean effective: return effective role :param boolean effective: return effective role
assignments. (optional) assignments. (optional)
@ -76,6 +94,9 @@ class RoleAssignmentManager(base.CrudManager):
""" """
self._check_not_user_and_group(user, group) self._check_not_user_and_group(user, group)
self._check_not_domain_and_project(domain, project) self._check_not_domain_and_project(domain, project)
self._check_not_system_and_domain(system, domain)
self._check_not_system_and_project(system, project)
self._check_system_value(system)
query_params = {} query_params = {}
if user: if user:
@ -86,6 +107,8 @@ class RoleAssignmentManager(base.CrudManager):
query_params['scope.project.id'] = base.getid(project) query_params['scope.project.id'] = base.getid(project)
if domain: if domain:
query_params['scope.domain.id'] = base.getid(domain) query_params['scope.domain.id'] = base.getid(domain)
if system:
query_params['scope.system'] = system
if role: if role:
query_params['role.id'] = base.getid(role) query_params['role.id'] = base.getid(role)
if effective: if effective:

View File

@ -54,7 +54,7 @@ class RoleManager(base.CrudManager):
key = 'role' key = 'role'
deprecation_msg = 'keystoneclient.v3.roles.InferenceRuleManager' deprecation_msg = 'keystoneclient.v3.roles.InferenceRuleManager'
def _role_grants_base_url(self, user, group, domain, project, def _role_grants_base_url(self, user, group, system, domain, project,
use_inherit_extension): use_inherit_extension):
# When called, we have already checked that only one of user & group # When called, we have already checked that only one of user & group
# and one of domain & project have been specified # and one of domain & project have been specified
@ -66,6 +66,18 @@ class RoleManager(base.CrudManager):
elif domain: elif domain:
params['domain_id'] = base.getid(domain) params['domain_id'] = base.getid(domain)
base_url = '/domains/%(domain_id)s' base_url = '/domains/%(domain_id)s'
elif system:
if system == 'all':
base_url = '/system'
else:
# NOTE(lbragstad): If we've made it this far, a user is
# attempting to do something with system scope that isn't
# supported yet (e.g. 'all' is currently the only supported
# system scope). In the future that may change but until then
# we should fail like we would if a user provided a bogus
# project name or domain ID.
msg = _("Only a system scope of 'all' is currently supported")
raise exceptions.ValidationError(msg)
if use_inherit_extension: if use_inherit_extension:
base_url = '/OS-INHERIT' + base_url base_url = '/OS-INHERIT' + base_url
@ -79,13 +91,26 @@ class RoleManager(base.CrudManager):
return base_url % params return base_url % params
def _require_domain_xor_project(self, domain, project): def _enforce_mutually_exclusive_group(self, system, domain, project):
if domain and project: if not system:
msg = _('Specify either a domain or project, not both') if domain and project:
raise exceptions.ValidationError(msg) msg = _('Specify either a domain or project, not both')
elif not (domain or project): raise exceptions.ValidationError(msg)
msg = _('Must specify either a domain or project') elif not (domain or project):
raise exceptions.ValidationError(msg) msg = _('Must specify either system, domain, or project')
raise exceptions.ValidationError(msg)
elif system:
if domain and project:
msg = _(
'Specify either system, domain, or project, not all three.'
)
raise exceptions.ValidationError(msg)
if domain:
msg = _('Specify either system or a domain, not both')
raise exceptions.ValidationError(msg)
if project:
msg = _('Specify either a system or project, not both')
raise exceptions.ValidationError(msg)
def _require_user_xor_group(self, user, group): def _require_user_xor_group(self, user, group):
if user and group: if user and group:
@ -130,7 +155,7 @@ class RoleManager(base.CrudManager):
""" """
return super(RoleManager, self).get(role_id=base.getid(role)) return super(RoleManager, self).get(role_id=base.getid(role))
def list(self, user=None, group=None, domain=None, def list(self, user=None, group=None, system=None, domain=None,
project=None, os_inherit_extension_inherited=False, **kwargs): project=None, os_inherit_extension_inherited=False, **kwargs):
"""List roles and role grants. """List roles and role grants.
@ -143,12 +168,12 @@ class RoleManager(base.CrudManager):
User and group are mutually exclusive. User and group are mutually exclusive.
:type group: str or :class:`keystoneclient.v3.groups.Group` :type group: str or :class:`keystoneclient.v3.groups.Group`
:param domain: filter in role grants on the specified domain. Either :param domain: filter in role grants on the specified domain. Either
user or group must be specified. Project and domain user or group must be specified. Project, domain, and
are mutually exclusive. system are mutually exclusive.
:type domain: str or :class:`keystoneclient.v3.domains.Domain` :type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param project: filter in role grants on the specified project. Either :param project: filter in role grants on the specified project. Either
user or group must be specified. Project and domain user or group must be specified. Project, domain and
are mutually exclusive. system are mutually exclusive.
:type project: str or :class:`keystoneclient.v3.projects.Project` :type project: str or :class:`keystoneclient.v3.projects.Project`
:param bool os_inherit_extension_inherited: OS-INHERIT will be used. :param bool os_inherit_extension_inherited: OS-INHERIT will be used.
It provides the ability for It provides the ability for
@ -166,10 +191,12 @@ class RoleManager(base.CrudManager):
kwargs['tail'] = '/inherited_to_projects' kwargs['tail'] = '/inherited_to_projects'
if user or group: if user or group:
self._require_user_xor_group(user, group) self._require_user_xor_group(user, group)
self._require_domain_xor_project(domain, project) self._enforce_mutually_exclusive_group(system, domain, project)
base_url = self._role_grants_base_url( base_url = self._role_grants_base_url(
user, group, domain, project, os_inherit_extension_inherited) user, group, system, domain, project,
os_inherit_extension_inherited
)
return super(RoleManager, self).list(base_url=base_url, return super(RoleManager, self).list(base_url=base_url,
**kwargs) **kwargs)
@ -208,8 +235,8 @@ class RoleManager(base.CrudManager):
return super(RoleManager, self).delete( return super(RoleManager, self).delete(
role_id=base.getid(role)) role_id=base.getid(role))
def grant(self, role, user=None, group=None, domain=None, project=None, def grant(self, role, user=None, group=None, system=None, domain=None,
os_inherit_extension_inherited=False, **kwargs): project=None, os_inherit_extension_inherited=False, **kwargs):
"""Grant a role to a user or group on a domain or project. """Grant a role to a user or group on a domain or project.
:param role: the role to be granted on the server. :param role: the role to be granted on the server.
@ -222,13 +249,16 @@ class RoleManager(base.CrudManager):
resource. Domain or project must be specified. resource. Domain or project must be specified.
User and group are mutually exclusive. User and group are mutually exclusive.
:type group: str or :class:`keystoneclient.v3.groups.Group` :type group: str or :class:`keystoneclient.v3.groups.Group`
:param system: system information to grant the role on. Project,
domain, and system are mutually exclusive.
:type system: str
:param domain: the domain in which the role will be granted. Either :param domain: the domain in which the role will be granted. Either
user or group must be specified. Project and domain user or group must be specified. Project, domain, and
are mutually exclusive. system are mutually exclusive.
:type domain: str or :class:`keystoneclient.v3.domains.Domain` :type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param project: the project in which the role will be granted. Either :param project: the project in which the role will be granted. Either
user or group must be specified. Project and domain user or group must be specified. Project, domain, and
are mutually exclusive. system are mutually exclusive.
:type project: str or :class:`keystoneclient.v3.projects.Project` :type project: str or :class:`keystoneclient.v3.projects.Project`
:param bool os_inherit_extension_inherited: OS-INHERIT will be used. :param bool os_inherit_extension_inherited: OS-INHERIT will be used.
It provides the ability for It provides the ability for
@ -242,20 +272,21 @@ class RoleManager(base.CrudManager):
:rtype: :class:`keystoneclient.v3.roles.Role` :rtype: :class:`keystoneclient.v3.roles.Role`
""" """
self._require_domain_xor_project(domain, project) self._enforce_mutually_exclusive_group(system, domain, project)
self._require_user_xor_group(user, group) self._require_user_xor_group(user, group)
if os_inherit_extension_inherited: if os_inherit_extension_inherited:
kwargs['tail'] = '/inherited_to_projects' kwargs['tail'] = '/inherited_to_projects'
base_url = self._role_grants_base_url( base_url = self._role_grants_base_url(
user, group, domain, project, os_inherit_extension_inherited) user, group, system, domain, project,
os_inherit_extension_inherited)
return super(RoleManager, self).put(base_url=base_url, return super(RoleManager, self).put(base_url=base_url,
role_id=base.getid(role), role_id=base.getid(role),
**kwargs) **kwargs)
def check(self, role, user=None, group=None, domain=None, project=None, def check(self, role, user=None, group=None, system=None, domain=None,
os_inherit_extension_inherited=False, **kwargs): project=None, os_inherit_extension_inherited=False, **kwargs):
"""Check if a user or group has a role on a domain or project. """Check if a user or group has a role on a domain or project.
:param user: check for role grants for the specified user on a :param user: check for role grants for the specified user on a
@ -266,13 +297,16 @@ class RoleManager(base.CrudManager):
resource. Domain or project must be specified. resource. Domain or project must be specified.
User and group are mutually exclusive. User and group are mutually exclusive.
:type group: str or :class:`keystoneclient.v3.groups.Group` :type group: str or :class:`keystoneclient.v3.groups.Group`
:param system: check for role grants on the system. Project, domain,
and system are mutually exclusive.
:type system: str
:param domain: check for role grants on the specified domain. Either :param domain: check for role grants on the specified domain. Either
user or group must be specified. Project and domain user or group must be specified. Project, domain, and
are mutually exclusive. system are mutually exclusive.
:type domain: str or :class:`keystoneclient.v3.domains.Domain` :type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param project: check for role grants on the specified project. Either :param project: check for role grants on the specified project. Either
user or group must be specified. Project and domain user or group must be specified. Project, domain, and
are mutually exclusive. system are mutually exclusive.
:type project: str or :class:`keystoneclient.v3.projects.Project` :type project: str or :class:`keystoneclient.v3.projects.Project`
:param bool os_inherit_extension_inherited: OS-INHERIT will be used. :param bool os_inherit_extension_inherited: OS-INHERIT will be used.
It provides the ability for It provides the ability for
@ -290,22 +324,23 @@ class RoleManager(base.CrudManager):
:rtype: :class:`requests.models.Response` :rtype: :class:`requests.models.Response`
""" """
self._require_domain_xor_project(domain, project) self._enforce_mutually_exclusive_group(system, domain, project)
self._require_user_xor_group(user, group) self._require_user_xor_group(user, group)
if os_inherit_extension_inherited: if os_inherit_extension_inherited:
kwargs['tail'] = '/inherited_to_projects' kwargs['tail'] = '/inherited_to_projects'
base_url = self._role_grants_base_url( base_url = self._role_grants_base_url(
user, group, domain, project, os_inherit_extension_inherited) user, group, system, domain, project,
os_inherit_extension_inherited)
return super(RoleManager, self).head( return super(RoleManager, self).head(
base_url=base_url, base_url=base_url,
role_id=base.getid(role), role_id=base.getid(role),
os_inherit_extension_inherited=os_inherit_extension_inherited, os_inherit_extension_inherited=os_inherit_extension_inherited,
**kwargs) **kwargs)
def revoke(self, role, user=None, group=None, domain=None, project=None, def revoke(self, role, user=None, group=None, system=None, domain=None,
os_inherit_extension_inherited=False, **kwargs): project=None, os_inherit_extension_inherited=False, **kwargs):
"""Revoke a role from a user or group on a domain or project. """Revoke a role from a user or group on a domain or project.
:param user: revoke role grants for the specified user on a :param user: revoke role grants for the specified user on a
@ -316,13 +351,16 @@ class RoleManager(base.CrudManager):
resource. Domain or project must be specified. resource. Domain or project must be specified.
User and group are mutually exclusive. User and group are mutually exclusive.
:type group: str or :class:`keystoneclient.v3.groups.Group` :type group: str or :class:`keystoneclient.v3.groups.Group`
:param system: revoke role grants on the system. Project, domain, and
system are mutually exclusive.
:type system: str
:param domain: revoke role grants on the specified domain. Either :param domain: revoke role grants on the specified domain. Either
user or group must be specified. Project and domain user or group must be specified. Project, domain, and
are mutually exclusive. system are mutually exclusive.
:type domain: str or :class:`keystoneclient.v3.domains.Domain` :type domain: str or :class:`keystoneclient.v3.domains.Domain`
:param project: revoke role grants on the specified project. Either :param project: revoke role grants on the specified project. Either
user or group must be specified. Project and domain user or group must be specified. Project, domain, and
are mutually exclusive. system are mutually exclusive.
:type project: str or :class:`keystoneclient.v3.projects.Project` :type project: str or :class:`keystoneclient.v3.projects.Project`
:param bool os_inherit_extension_inherited: OS-INHERIT will be used. :param bool os_inherit_extension_inherited: OS-INHERIT will be used.
It provides the ability for It provides the ability for
@ -336,14 +374,15 @@ class RoleManager(base.CrudManager):
:rtype: list of :class:`keystoneclient.v3.roles.Role` :rtype: list of :class:`keystoneclient.v3.roles.Role`
""" """
self._require_domain_xor_project(domain, project) self._enforce_mutually_exclusive_group(system, domain, project)
self._require_user_xor_group(user, group) self._require_user_xor_group(user, group)
if os_inherit_extension_inherited: if os_inherit_extension_inherited:
kwargs['tail'] = '/inherited_to_projects' kwargs['tail'] = '/inherited_to_projects'
base_url = self._role_grants_base_url( base_url = self._role_grants_base_url(
user, group, domain, project, os_inherit_extension_inherited) user, group, system, domain, project,
os_inherit_extension_inherited)
return super(RoleManager, self).delete( return super(RoleManager, self).delete(
base_url=base_url, base_url=base_url,
role_id=base.getid(role), role_id=base.getid(role),