Identity: Add support for system role assignment

Allow granting and revoking system role assignment to a user or group.
Enable filtering for system role when listing role assignments.

https: //docs.openstack.org/api-ref/identity/v3/#system-role-assignments
Change-Id: I2870d322fdfd59c91524b4031d8ecf53b60a9a1d
This commit is contained in:
james kirsch 2022-01-24 15:12:29 -08:00 committed by James Kirsch
parent 12fa1370bc
commit bdd87c7042
12 changed files with 538 additions and 24 deletions

View File

@ -87,7 +87,10 @@ Role Assignment Operations
unassign_project_role_from_group, validate_group_has_project_role, unassign_project_role_from_group, validate_group_has_project_role,
assign_domain_role_to_user, unassign_domain_role_from_user, assign_domain_role_to_user, unassign_domain_role_from_user,
validate_user_has_domain_role, assign_domain_role_to_group, validate_user_has_domain_role, assign_domain_role_to_group,
unassign_domain_role_from_group, validate_group_has_domain_role unassign_domain_role_from_group, validate_group_has_domain_role,
assign_system_role_to_user, unassign_system_role_from_user,
validate_user_has_system_role, assign_system_role_to_group,
unassign_system_role_from_group, validate_group_has_system_role
Service Operations Service Operations
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^

View File

@ -945,6 +945,7 @@ class IdentityCloudMixin:
* 'group' (string) - Group ID to be used as query filter. * 'group' (string) - Group ID to be used as query filter.
* 'project' (string) - Project ID to be used as query filter. * 'project' (string) - Project ID to be used as query filter.
* 'domain' (string) - Domain ID to be used as query filter. * 'domain' (string) - Domain ID to be used as query filter.
* 'system' (string) - System name to be used as query filter.
* 'role' (string) - Role ID to be used as query filter. * 'role' (string) - Role ID to be used as query filter.
* 'os_inherit_extension_inherited_to' (string) - Return inherited * 'os_inherit_extension_inherited_to' (string) - Return inherited
role assignments for either 'projects' or 'domains' role assignments for either 'projects' or 'domains'
@ -991,6 +992,10 @@ class IdentityCloudMixin:
if k in filters: if k in filters:
filters['scope_%s_id' % k] = filters.pop(k) filters['scope_%s_id' % k] = filters.pop(k)
if 'system' in filters:
system_scope = filters.pop('system')
filters['scope.system'] = system_scope
return list(self.identity.role_assignments(**filters)) return list(self.identity.role_assignments(**filters))
@_utils.valid_kwargs('domain_id') @_utils.valid_kwargs('domain_id')
@ -1055,7 +1060,7 @@ class IdentityCloudMixin:
raise raise
def _get_grant_revoke_params(self, role, user=None, group=None, def _get_grant_revoke_params(self, role, user=None, group=None,
project=None, domain=None): project=None, domain=None, system=None):
data = {} data = {}
search_args = {} search_args = {}
if domain: if domain:
@ -1083,9 +1088,9 @@ class IdentityCloudMixin:
if data.get('user') is None and data.get('group') is None: if data.get('user') is None and data.get('group') is None:
raise exc.OpenStackCloudException( raise exc.OpenStackCloudException(
'Must specify either a user or a group') 'Must specify either a user or a group')
if project is None and domain is None: if project is None and domain is None and system is None:
raise exc.OpenStackCloudException( raise exc.OpenStackCloudException(
'Must specify either a domain or project') 'Must specify either a domain, project or system')
if project: if project:
data['project'] = self.identity.find_project( data['project'] = self.identity.find_project(
@ -1093,7 +1098,8 @@ class IdentityCloudMixin:
return data return data
def grant_role(self, name_or_id, user=None, group=None, def grant_role(self, name_or_id, user=None, group=None,
project=None, domain=None, wait=False, timeout=60): project=None, domain=None, system=None, wait=False,
timeout=60):
"""Grant a role to a user. """Grant a role to a user.
:param string name_or_id: The name or id of the role. :param string name_or_id: The name or id of the role.
@ -1101,6 +1107,7 @@ class IdentityCloudMixin:
:param string group: The name or id of the group. (v3) :param string group: The name or id of the group. (v3)
:param string project: The name or id of the project. :param string project: The name or id of the project.
:param string domain: The id of the domain. (v3) :param string domain: The id of the domain. (v3)
:param bool system: The name of the system. (v3)
:param bool wait: Wait for role to be granted :param bool wait: Wait for role to be granted
:param int timeout: Timeout to wait for role to be granted :param int timeout: Timeout to wait for role to be granted
@ -1112,13 +1119,15 @@ class IdentityCloudMixin:
NOTE: for wait and timeout, sometimes granting roles is not NOTE: for wait and timeout, sometimes granting roles is not
instantaneous. instantaneous.
NOTE: precedence is given first to project, then domain, then system
:returns: True if the role is assigned, otherwise False :returns: True if the role is assigned, otherwise False
:raise OpenStackCloudException: if the role cannot be granted :raise OpenStackCloudException: if the role cannot be granted
""" """
data = self._get_grant_revoke_params( data = self._get_grant_revoke_params(
name_or_id, user=user, group=group, name_or_id, user=user, group=group,
project=project, domain=domain) project=project, domain=domain, system=system)
user = data.get('user') user = data.get('user')
group = data.get('group') group = data.get('group')
@ -1127,7 +1136,7 @@ class IdentityCloudMixin:
role = data.get('role') role = data.get('role')
if project: if project:
# Proceed with project - precedence over domain # Proceed with project - precedence over domain and system
if user: if user:
has_role = self.identity.validate_user_has_project_role( has_role = self.identity.validate_user_has_project_role(
project, user, role) project, user, role)
@ -1144,8 +1153,8 @@ class IdentityCloudMixin:
return False return False
self.identity.assign_project_role_to_group( self.identity.assign_project_role_to_group(
project, group, role) project, group, role)
else: elif domain:
# Proceed with domain # Proceed with domain - precedence over system
if user: if user:
has_role = self.identity.validate_user_has_domain_role( has_role = self.identity.validate_user_has_domain_role(
domain, user, role) domain, user, role)
@ -1162,10 +1171,31 @@ class IdentityCloudMixin:
return False return False
self.identity.assign_domain_role_to_group( self.identity.assign_domain_role_to_group(
domain, group, role) domain, group, role)
else:
# Proceed with system
# System name must be 'all' due to checks performed in
# _get_grant_revoke_params
if user:
has_role = self.identity.validate_user_has_system_role(
user, role, system)
if has_role:
self.log.debug('Assignment already exists')
return False
self.identity.assign_system_role_to_user(
user, role, system)
else:
has_role = self.identity.validate_group_has_system_role(
group, role, system)
if has_role:
self.log.debug('Assignment already exists')
return False
self.identity.assign_system_role_to_group(
group, role, system)
return True return True
def revoke_role(self, name_or_id, user=None, group=None, def revoke_role(self, name_or_id, user=None, group=None,
project=None, domain=None, wait=False, timeout=60): project=None, domain=None, system=None,
wait=False, timeout=60):
"""Revoke a role from a user. """Revoke a role from a user.
:param string name_or_id: The name or id of the role. :param string name_or_id: The name or id of the role.
@ -1173,6 +1203,7 @@ class IdentityCloudMixin:
:param string group: The name or id of the group. (v3) :param string group: The name or id of the group. (v3)
:param string project: The name or id of the project. :param string project: The name or id of the project.
:param string domain: The id of the domain. (v3) :param string domain: The id of the domain. (v3)
:param bool system: The name of the system. (v3)
:param bool wait: Wait for role to be revoked :param bool wait: Wait for role to be revoked
:param int timeout: Timeout to wait for role to be revoked :param int timeout: Timeout to wait for role to be revoked
@ -1181,13 +1212,15 @@ class IdentityCloudMixin:
NOTE: project is required for keystone v2 NOTE: project is required for keystone v2
NOTE: precedence is given first to project, then domain, then system
:returns: True if the role is revoke, otherwise False :returns: True if the role is revoke, otherwise False
:raise OpenStackCloudException: if the role cannot be removed :raise OpenStackCloudException: if the role cannot be removed
""" """
data = self._get_grant_revoke_params( data = self._get_grant_revoke_params(
name_or_id, user=user, group=group, name_or_id, user=user, group=group,
project=project, domain=domain) project=project, domain=domain, system=system)
user = data.get('user') user = data.get('user')
group = data.get('group') group = data.get('group')
@ -1196,7 +1229,7 @@ class IdentityCloudMixin:
role = data.get('role') role = data.get('role')
if project: if project:
# Proceed with project - precedence over domain # Proceed with project - precedence over domain and system
if user: if user:
has_role = self.identity.validate_user_has_project_role( has_role = self.identity.validate_user_has_project_role(
project, user, role) project, user, role)
@ -1213,8 +1246,8 @@ class IdentityCloudMixin:
return False return False
self.identity.unassign_project_role_from_group( self.identity.unassign_project_role_from_group(
project, group, role) project, group, role)
else: elif domain:
# Proceed with domain # Proceed with domain - precedence over system
if user: if user:
has_role = self.identity.validate_user_has_domain_role( has_role = self.identity.validate_user_has_domain_role(
domain, user, role) domain, user, role)
@ -1231,6 +1264,26 @@ class IdentityCloudMixin:
return False return False
self.identity.unassign_domain_role_from_group( self.identity.unassign_domain_role_from_group(
domain, group, role) domain, group, role)
else:
# Proceed with system
# System name must be 'all' due to checks performed in
# _get_grant_revoke_params
if user:
has_role = self.identity.validate_user_has_system_role(
user, role, system)
if not has_role:
self.log.debug('Assignment does not exist')
return False
self.identity.unassign_system_role_from_user(
user, role, system)
else:
has_role = self.identity.validate_group_has_system_role(
group, role, system)
if not has_role:
self.log.debug('Assignment does not exist')
return False
self.identity.unassign_system_role_from_group(
group, role, system)
return True return True
def _get_identity_params(self, domain_id=None, project=None): def _get_identity_params(self, domain_id=None, project=None):

View File

@ -35,7 +35,12 @@ from openstack.identity.v3 import role_project_group_assignment \
as _role_project_group_assignment as _role_project_group_assignment
from openstack.identity.v3 import role_project_user_assignment \ from openstack.identity.v3 import role_project_user_assignment \
as _role_project_user_assignment as _role_project_user_assignment
from openstack.identity.v3 import role_system_group_assignment \
as _role_system_group_assignment
from openstack.identity.v3 import role_system_user_assignment \
as _role_system_user_assignment
from openstack.identity.v3 import service as _service from openstack.identity.v3 import service as _service
from openstack.identity.v3 import system as _system
from openstack.identity.v3 import trust as _trust from openstack.identity.v3 import trust as _trust
from openstack.identity.v3 import user as _user from openstack.identity.v3 import user as _user
from openstack import proxy from openstack import proxy
@ -949,8 +954,8 @@ class Proxy(proxy.Proxy):
""" """
return self._update(_role.Role, role, **attrs) return self._update(_role.Role, role, **attrs)
def role_assignments_filter(self, domain=None, project=None, group=None, def role_assignments_filter(self, domain=None, project=None, system=None,
user=None): group=None, user=None):
"""Retrieve a generator of roles assigned to user/group """Retrieve a generator of roles assigned to user/group
:param domain: Either the ID of a domain or a :param domain: Either the ID of a domain or a
@ -958,6 +963,9 @@ class Proxy(proxy.Proxy):
:param project: Either the ID of a project or a :param project: Either the ID of a project or a
:class:`~openstack.identity.v3.project.Project` :class:`~openstack.identity.v3.project.Project`
instance. instance.
:param system: Either the system name or a
:class:`~openstack.identity.v3.system.System`
instance.
:param group: Either the ID of a group or a :param group: Either the ID of a group or a
:class:`~openstack.identity.v3.group.Group` instance. :class:`~openstack.identity.v3.group.Group` instance.
:param user: Either the ID of a user or a :param user: Either the ID of a user or a
@ -965,13 +973,13 @@ class Proxy(proxy.Proxy):
:return: A generator of role instances. :return: A generator of role instances.
:rtype: :class:`~openstack.identity.v3.role.Role` :rtype: :class:`~openstack.identity.v3.role.Role`
""" """
if domain and project: if domain and project and system:
raise exception.InvalidRequest( raise exception.InvalidRequest(
'Only one of domain or project can be specified') 'Only one of domain, project, or system can be specified')
if domain is None and project is None: if domain is None and project is None and system is None:
raise exception.InvalidRequest( raise exception.InvalidRequest(
'Either domain or project should be specified') 'Either domain, project, or system should be specified')
if group and user: if group and user:
raise exception.InvalidRequest( raise exception.InvalidRequest(
@ -993,7 +1001,7 @@ class Proxy(proxy.Proxy):
return self._list( return self._list(
_role_domain_user_assignment.RoleDomainUserAssignment, _role_domain_user_assignment.RoleDomainUserAssignment,
domain_id=domain.id, user_id=user.id) domain_id=domain.id, user_id=user.id)
else: elif project:
project = self._get_resource(_project.Project, project) project = self._get_resource(_project.Project, project)
if group: if group:
group = self._get_resource(_group.Group, group) group = self._get_resource(_group.Group, group)
@ -1005,6 +1013,18 @@ class Proxy(proxy.Proxy):
return self._list( return self._list(
_role_project_user_assignment.RoleProjectUserAssignment, _role_project_user_assignment.RoleProjectUserAssignment,
project_id=project.id, user_id=user.id) project_id=project.id, user_id=user.id)
else:
system = self._get_resource(_project.System, system)
if group:
group = self._get_resource(_group.Group, group)
return self._list(
_role_system_group_assignment.RoleSystemGroupAssignment,
system_id=system.id, group_id=group.id)
else:
user = self._get_resource(_user.User, user)
return self._list(
_role_system_user_assignment.RoleSystemUserAssignment,
system_id=system.id, user_id=user.id)
def role_assignments(self, **query): def role_assignments(self, **query):
"""Retrieve a generator of role assignments """Retrieve a generator of role assignments
@ -1355,6 +1375,96 @@ class Proxy(proxy.Proxy):
role = self._get_resource(_role.Role, role) role = self._get_resource(_role.Role, role)
return project.validate_group_has_role(self, group, role) return project.validate_group_has_role(self, group, role)
def assign_system_role_to_user(self, user, role, system):
"""Assign a role to user on a system
:param user: Either the ID of a user or a
:class:`~openstack.identity.v3.user.User` instance.
:param role: Either the ID of a role or a
:class:`~openstack.identity.v3.role.Role` instance.
:param system: The system name
:return: ``None``
"""
user = self._get_resource(_user.User, user)
role = self._get_resource(_role.Role, role)
system = self._get_resource(_system.System, system)
system.assign_role_to_user(self, user, role)
def unassign_system_role_from_user(self, user, role, system):
"""Unassign a role from user on a system
:param user: Either the ID of a user or a
:class:`~openstack.identity.v3.user.User` instance.
:param role: Either the ID of a role or a
:class:`~openstack.identity.v3.role.Role` instance.
:param system: The system name
:return: ``None``
"""
user = self._get_resource(_user.User, user)
role = self._get_resource(_role.Role, role)
system = self._get_resource(_system.System, system)
system.unassign_role_from_user(self, user, role)
def validate_user_has_system_role(self, user, role, system):
"""Validates that a user has a role on a system
:param user: Either the ID of a user or a
:class:`~openstack.identity.v3.user.User` instance.
:param role: Either the ID of a role or a
:class:`~openstack.identity.v3.role.Role` instance.
:param system: The system name
:returns: True if user has role in system
"""
user = self._get_resource(_user.User, user)
role = self._get_resource(_role.Role, role)
system = self._get_resource(_system.System, system)
return system.validate_user_has_role(self, user, role)
def assign_system_role_to_group(self, group, role, system):
"""Assign a role to group on a system
:param group: Either the ID of a group or a
:class:`~openstack.identity.v3.group.Group` instance.
:param role: Either the ID of a role or a
:class:`~openstack.identity.v3.role.Role` instance.
:param system: The system name
:return: ``None``
"""
group = self._get_resource(_group.Group, group)
role = self._get_resource(_role.Role, role)
system = self._get_resource(_system.System, system)
system.assign_role_to_group(self, group, role)
def unassign_system_role_from_group(self, group, role, system):
"""Unassign a role from group on a system
:param group: Either the ID of a group or a
:class:`~openstack.identity.v3.group.Group` instance.
:param role: Either the ID of a role or a
:class:`~openstack.identity.v3.role.Role` instance.
:param system: The system name
:return: ``None``
"""
group = self._get_resource(_group.Group, group)
role = self._get_resource(_role.Role, role)
system = self._get_resource(_system.System, system)
system.unassign_role_from_group(self, group, role)
def validate_group_has_system_role(self, group, role, system):
"""Validates that a group has a role on a system
:param group: Either the ID of a group or a
:class:`~openstack.identity.v3.group.Group` instance.
:param role: Either the ID of a role or a
:class:`~openstack.identity.v3.role.Role` instance.
:param system: The system name
:returns: True if group has role on system
"""
group = self._get_resource(_group.Group, group)
role = self._get_resource(_role.Role, role)
system = self._get_resource(_system.System, system)
return system.validate_group_has_role(self, group, role)
def application_credentials(self, user, **query): def application_credentials(self, user, **query):
"""Retrieve a generator of application credentials """Retrieve a generator of application credentials

View File

@ -0,0 +1,28 @@
# 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.
from openstack import resource
class RoleSystemGroupAssignment(resource.Resource):
resource_key = 'role'
resources_key = 'roles'
base_path = '/system/groups/%(group_id)s/roles'
# capabilities
allow_list = True
# Properties
#: The ID of the group to list assignment from. *Type: string*
group_id = resource.URI('group_id')
#: The name of the system to list assignment from. *Type: string*
system_id = resource.URI('system_id')

View File

@ -0,0 +1,28 @@
# 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.
from openstack import resource
class RoleSystemUserAssignment(resource.Resource):
resource_key = 'role'
resources_key = 'roles'
base_path = '/system/users/%(user_id)s/roles'
# capabilities
allow_list = True
# Properties
#: The name of the system to list assignment from. *Type: string*
system_id = resource.URI('system_id')
#: The ID of the user to list assignment from. *Type: string*
user_id = resource.URI('user_id')

View File

@ -0,0 +1,75 @@
# 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.
from openstack import resource
from openstack import utils
class System(resource.Resource):
resource_key = 'system'
base_path = '/system'
# capabilities
def assign_role_to_user(self, session, user, role):
"""Assign role to user on system"""
url = utils.urljoin(self.base_path, 'users', user.id,
'roles', role.id)
resp = session.put(url,)
if resp.status_code == 204:
return True
return False
def validate_user_has_role(self, session, user, role):
"""Validates that a user has a role on a system"""
url = utils.urljoin(self.base_path, 'users', user.id,
'roles', role.id)
resp = session.head(url,)
if resp.status_code == 204:
return True
return False
def unassign_role_from_user(self, session, user, role):
"""Unassigns a role from a user on a system"""
url = utils.urljoin(self.base_path, 'users', user.id,
'roles', role.id)
resp = session.delete(url,)
if resp.status_code == 204:
return True
return False
def assign_role_to_group(self, session, group, role):
"""Assign role to group on system"""
url = utils.urljoin(self.base_path, 'groups', group.id,
'roles', role.id)
resp = session.put(url,)
if resp.status_code == 204:
return True
return False
def validate_group_has_role(self, session, group, role):
"""Validates that a group has a role on a system"""
url = utils.urljoin(self.base_path, 'groups', group.id,
'roles', role.id)
resp = session.head(url,)
if resp.status_code == 204:
return True
return False
def unassign_role_from_group(self, session, group, role):
"""Unassigns a role from a group on a system"""
url = utils.urljoin(self.base_path, 'groups', group.id,
'roles', role.id)
resp = session.delete(url,)
if resp.status_code == 204:
return True
return False

View File

@ -248,3 +248,58 @@ class TestIdentity(base.KeystoneBaseFunctionalTest):
}) })
self.assertIsInstance(assignments, list) self.assertIsInstance(assignments, list)
self.assertEqual(0, len(assignments)) self.assertEqual(0, len(assignments))
def test_grant_revoke_role_user_system(self):
role_name = self.role_prefix + '_grant_user_system'
role = self.operator_cloud.create_role(role_name)
user_name = self.user_prefix + '_user_system'
user_email = 'nobody@nowhere.com'
user = self._create_user(name=user_name,
email=user_email,
default_project='demo')
self.assertTrue(self.operator_cloud.grant_role(
role_name, user=user['id'], system='all'))
assignments = self.operator_cloud.list_role_assignments({
'role': role['id'],
'user': user['id'],
'system': 'all'
})
self.assertIsInstance(assignments, list)
self.assertEqual(1, len(assignments))
self.assertTrue(self.operator_cloud.revoke_role(
role_name, user=user['id'], system='all'))
assignments = self.operator_cloud.list_role_assignments({
'role': role['id'],
'user': user['id'],
'system': 'all'
})
self.assertIsInstance(assignments, list)
self.assertEqual(0, len(assignments))
def test_grant_revoke_role_group_system(self):
if self.identity_version in ('2', '2.0'):
self.skipTest("Identity service does not support system or group")
role_name = self.role_prefix + '_grant_group_system'
role = self.operator_cloud.create_role(role_name)
group_name = self.group_prefix + '_group_system'
group = self.operator_cloud.create_group(
name=group_name,
description='test group')
self.assertTrue(self.operator_cloud.grant_role(
role_name, group=group['id'], system='all'))
assignments = self.operator_cloud.list_role_assignments({
'role': role['id'],
'group': group['id'],
'system': 'all'
})
self.assertIsInstance(assignments, list)
self.assertEqual(1, len(assignments))
self.assertTrue(self.operator_cloud.revoke_role(
role_name, group=group['id'], system='all'))
assignments = self.operator_cloud.list_role_assignments({
'role': role['id'],
'group': group['id'],
'system': 'all'
})
self.assertIsInstance(assignments, list)
self.assertEqual(0, len(assignments))

View File

@ -1364,14 +1364,14 @@ class TestRoleAssignment(base.TestCase):
with testtools.ExpectedException( with testtools.ExpectedException(
exc.OpenStackCloudException, exc.OpenStackCloudException,
'Must specify either a domain or project' 'Must specify either a domain, project or system'
): ):
self.cloud.grant_role( self.cloud.grant_role(
self.role_data.role_name, self.role_data.role_name,
user=self.user_data.name) user=self.user_data.name)
self.assert_calls() self.assert_calls()
def test_revoke_no_project_or_domain(self): def test_revoke_no_project_or_domain_or_system(self):
uris = self._get_mock_role_query_urls( uris = self._get_mock_role_query_urls(
self.role_data, self.role_data,
user_data=self.user_data, user_data=self.user_data,
@ -1381,7 +1381,7 @@ class TestRoleAssignment(base.TestCase):
with testtools.ExpectedException( with testtools.ExpectedException(
exc.OpenStackCloudException, exc.OpenStackCloudException,
'Must specify either a domain or project' 'Must specify either a domain, project or system'
): ):
self.cloud.revoke_role( self.cloud.revoke_role(
self.role_data.role_name, self.role_data.role_name,

View File

@ -499,3 +499,75 @@ class TestIdentityProxyRoleAssignments(TestIdentityProxyBase):
self.proxy._get_resource(role.Role, 'rid') self.proxy._get_resource(role.Role, 'rid')
] ]
) )
def test_assign_system_role_to_user(self):
self._verify(
"openstack.identity.v3.system.System.assign_role_to_user",
self.proxy.assign_system_role_to_user,
method_kwargs={'user': 'uid', 'role': 'rid', 'system': 'all'},
expected_args=[
self.proxy,
self.proxy._get_resource(user.User, 'uid'),
self.proxy._get_resource(role.Role, 'rid')
]
)
def test_unassign_system_role_from_user(self):
self._verify(
"openstack.identity.v3.system.System.unassign_role_from_user",
self.proxy.unassign_system_role_from_user,
method_kwargs={'user': 'uid', 'role': 'rid', 'system': 'all'},
expected_args=[
self.proxy,
self.proxy._get_resource(user.User, 'uid'),
self.proxy._get_resource(role.Role, 'rid')
]
)
def test_validate_user_has_system_role(self):
self._verify(
"openstack.identity.v3.system.System.validate_user_has_role",
self.proxy.validate_user_has_system_role,
method_kwargs={'user': 'uid', 'role': 'rid', 'system': 'all'},
expected_args=[
self.proxy,
self.proxy._get_resource(user.User, 'uid'),
self.proxy._get_resource(role.Role, 'rid')
]
)
def test_assign_system_role_to_group(self):
self._verify(
"openstack.identity.v3.system.System.assign_role_to_group",
self.proxy.assign_system_role_to_group,
method_kwargs={'group': 'uid', 'role': 'rid', 'system': 'all'},
expected_args=[
self.proxy,
self.proxy._get_resource(group.Group, 'uid'),
self.proxy._get_resource(role.Role, 'rid')
]
)
def test_unassign_system_role_from_group(self):
self._verify(
"openstack.identity.v3.system.System.unassign_role_from_group",
self.proxy.unassign_system_role_from_group,
method_kwargs={'group': 'uid', 'role': 'rid', 'system': 'all'},
expected_args=[
self.proxy,
self.proxy._get_resource(group.Group, 'uid'),
self.proxy._get_resource(role.Role, 'rid')
]
)
def test_validate_group_has_system_role(self):
self._verify(
"openstack.identity.v3.system.System.validate_group_has_role",
self.proxy.validate_group_has_system_role,
method_kwargs={'group': 'uid', 'role': 'rid', 'system': 'all'},
expected_args=[
self.proxy,
self.proxy._get_resource(group.Group, 'uid'),
self.proxy._get_resource(role.Role, 'rid')
]
)

View File

@ -0,0 +1,40 @@
# 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.
from openstack.identity.v3 import role_system_group_assignment
from openstack.tests.unit import base
IDENTIFIER = 'IDENTIFIER'
EXAMPLE = {
'id': IDENTIFIER,
'name': '2',
'group_id': '4'
}
class TestRoleSystemGroupAssignment(base.TestCase):
def test_basic(self):
sot = role_system_group_assignment.RoleSystemGroupAssignment()
self.assertEqual('role', sot.resource_key)
self.assertEqual('roles', sot.resources_key)
self.assertEqual('/system/groups/%(group_id)s/roles',
sot.base_path)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = \
role_system_group_assignment.RoleSystemGroupAssignment(**EXAMPLE)
self.assertEqual(EXAMPLE['id'], sot.id)
self.assertEqual(EXAMPLE['name'], sot.name)
self.assertEqual(EXAMPLE['group_id'], sot.group_id)

View File

@ -0,0 +1,39 @@
# 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.
from openstack.identity.v3 import role_system_user_assignment
from openstack.tests.unit import base
IDENTIFIER = 'IDENTIFIER'
EXAMPLE = {
'id': IDENTIFIER,
'name': '2',
'user_id': '4'
}
class TestRoleSystemUserAssignment(base.TestCase):
def test_basic(self):
sot = role_system_user_assignment.RoleSystemUserAssignment()
self.assertEqual('role', sot.resource_key)
self.assertEqual('roles', sot.resources_key)
self.assertEqual('/system/users/%(user_id)s/roles',
sot.base_path)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = \
role_system_user_assignment.RoleSystemUserAssignment(**EXAMPLE)
self.assertEqual(EXAMPLE['id'], sot.id)
self.assertEqual(EXAMPLE['name'], sot.name)

View File

@ -0,0 +1,11 @@
---
features:
- |
Add support for system role assignment. A system role assignment
ultimately controls access to system-level API calls.
Good examples of system-level APIs include management of the
service catalog and compute hypervisors.
`System role assignment API reference
<https://docs.openstack.org/api-ref/identity/v3/#system-role-assignments>`_.