diff --git a/doc/source/user/proxies/identity_v3.rst b/doc/source/user/proxies/identity_v3.rst index 31dccad94..d47e91cb3 100644 --- a/doc/source/user/proxies/identity_v3.rst +++ b/doc/source/user/proxies/identity_v3.rst @@ -87,7 +87,10 @@ Role Assignment Operations unassign_project_role_from_group, validate_group_has_project_role, assign_domain_role_to_user, unassign_domain_role_from_user, 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 ^^^^^^^^^^^^^^^^^^ diff --git a/openstack/cloud/_identity.py b/openstack/cloud/_identity.py index 8244fb798..f1ab25379 100644 --- a/openstack/cloud/_identity.py +++ b/openstack/cloud/_identity.py @@ -945,6 +945,7 @@ class IdentityCloudMixin: * 'group' (string) - Group 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. + * 'system' (string) - System name to be used as query filter. * 'role' (string) - Role ID to be used as query filter. * 'os_inherit_extension_inherited_to' (string) - Return inherited role assignments for either 'projects' or 'domains' @@ -991,6 +992,10 @@ class IdentityCloudMixin: if k in filters: 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)) @_utils.valid_kwargs('domain_id') @@ -1055,7 +1060,7 @@ class IdentityCloudMixin: raise def _get_grant_revoke_params(self, role, user=None, group=None, - project=None, domain=None): + project=None, domain=None, system=None): data = {} search_args = {} if domain: @@ -1083,9 +1088,9 @@ class IdentityCloudMixin: if data.get('user') is None and data.get('group') is None: raise exc.OpenStackCloudException( '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( - 'Must specify either a domain or project') + 'Must specify either a domain, project or system') if project: data['project'] = self.identity.find_project( @@ -1093,7 +1098,8 @@ class IdentityCloudMixin: return data 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. :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 project: The name or id of the project. :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 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 instantaneous. + NOTE: precedence is given first to project, then domain, then system + :returns: True if the role is assigned, otherwise False :raise OpenStackCloudException: if the role cannot be granted """ data = self._get_grant_revoke_params( name_or_id, user=user, group=group, - project=project, domain=domain) + project=project, domain=domain, system=system) user = data.get('user') group = data.get('group') @@ -1127,7 +1136,7 @@ class IdentityCloudMixin: role = data.get('role') if project: - # Proceed with project - precedence over domain + # Proceed with project - precedence over domain and system if user: has_role = self.identity.validate_user_has_project_role( project, user, role) @@ -1144,8 +1153,8 @@ class IdentityCloudMixin: return False self.identity.assign_project_role_to_group( project, group, role) - else: - # Proceed with domain + elif domain: + # Proceed with domain - precedence over system if user: has_role = self.identity.validate_user_has_domain_role( domain, user, role) @@ -1162,10 +1171,31 @@ class IdentityCloudMixin: return False self.identity.assign_domain_role_to_group( 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 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. :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 project: The name or id of the project. :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 int timeout: Timeout to wait for role to be revoked @@ -1181,13 +1212,15 @@ class IdentityCloudMixin: 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 :raise OpenStackCloudException: if the role cannot be removed """ data = self._get_grant_revoke_params( name_or_id, user=user, group=group, - project=project, domain=domain) + project=project, domain=domain, system=system) user = data.get('user') group = data.get('group') @@ -1196,7 +1229,7 @@ class IdentityCloudMixin: role = data.get('role') if project: - # Proceed with project - precedence over domain + # Proceed with project - precedence over domain and system if user: has_role = self.identity.validate_user_has_project_role( project, user, role) @@ -1213,8 +1246,8 @@ class IdentityCloudMixin: return False self.identity.unassign_project_role_from_group( project, group, role) - else: - # Proceed with domain + elif domain: + # Proceed with domain - precedence over system if user: has_role = self.identity.validate_user_has_domain_role( domain, user, role) @@ -1231,6 +1264,26 @@ class IdentityCloudMixin: return False self.identity.unassign_domain_role_from_group( 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 def _get_identity_params(self, domain_id=None, project=None): diff --git a/openstack/identity/v3/_proxy.py b/openstack/identity/v3/_proxy.py index a382e2127..141ef6097 100644 --- a/openstack/identity/v3/_proxy.py +++ b/openstack/identity/v3/_proxy.py @@ -35,7 +35,12 @@ from openstack.identity.v3 import role_project_group_assignment \ as _role_project_group_assignment from openstack.identity.v3 import 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 system as _system from openstack.identity.v3 import trust as _trust from openstack.identity.v3 import user as _user from openstack import proxy @@ -949,8 +954,8 @@ class Proxy(proxy.Proxy): """ return self._update(_role.Role, role, **attrs) - def role_assignments_filter(self, domain=None, project=None, group=None, - user=None): + def role_assignments_filter(self, domain=None, project=None, system=None, + group=None, user=None): """Retrieve a generator of roles assigned to user/group :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 :class:`~openstack.identity.v3.project.Project` 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 :class:`~openstack.identity.v3.group.Group` instance. :param user: Either the ID of a user or a @@ -965,13 +973,13 @@ class Proxy(proxy.Proxy): :return: A generator of role instances. :rtype: :class:`~openstack.identity.v3.role.Role` """ - if domain and project: + if domain and project and system: 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( - 'Either domain or project should be specified') + 'Either domain, project, or system should be specified') if group and user: raise exception.InvalidRequest( @@ -993,7 +1001,7 @@ class Proxy(proxy.Proxy): return self._list( _role_domain_user_assignment.RoleDomainUserAssignment, domain_id=domain.id, user_id=user.id) - else: + elif project: project = self._get_resource(_project.Project, project) if group: group = self._get_resource(_group.Group, group) @@ -1005,6 +1013,18 @@ class Proxy(proxy.Proxy): return self._list( _role_project_user_assignment.RoleProjectUserAssignment, 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): """Retrieve a generator of role assignments @@ -1355,6 +1375,96 @@ class Proxy(proxy.Proxy): role = self._get_resource(_role.Role, 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): """Retrieve a generator of application credentials diff --git a/openstack/identity/v3/role_system_group_assignment.py b/openstack/identity/v3/role_system_group_assignment.py new file mode 100644 index 000000000..a9dd03577 --- /dev/null +++ b/openstack/identity/v3/role_system_group_assignment.py @@ -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') diff --git a/openstack/identity/v3/role_system_user_assignment.py b/openstack/identity/v3/role_system_user_assignment.py new file mode 100644 index 000000000..e11781daa --- /dev/null +++ b/openstack/identity/v3/role_system_user_assignment.py @@ -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') diff --git a/openstack/identity/v3/system.py b/openstack/identity/v3/system.py new file mode 100644 index 000000000..9ceea3f76 --- /dev/null +++ b/openstack/identity/v3/system.py @@ -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 diff --git a/openstack/tests/functional/cloud/test_identity.py b/openstack/tests/functional/cloud/test_identity.py index f9ecdf663..5c087ba93 100644 --- a/openstack/tests/functional/cloud/test_identity.py +++ b/openstack/tests/functional/cloud/test_identity.py @@ -248,3 +248,58 @@ class TestIdentity(base.KeystoneBaseFunctionalTest): }) self.assertIsInstance(assignments, list) 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)) diff --git a/openstack/tests/unit/cloud/test_role_assignment.py b/openstack/tests/unit/cloud/test_role_assignment.py index b13e3b084..48b54f71a 100644 --- a/openstack/tests/unit/cloud/test_role_assignment.py +++ b/openstack/tests/unit/cloud/test_role_assignment.py @@ -1364,14 +1364,14 @@ class TestRoleAssignment(base.TestCase): with testtools.ExpectedException( exc.OpenStackCloudException, - 'Must specify either a domain or project' + 'Must specify either a domain, project or system' ): self.cloud.grant_role( self.role_data.role_name, user=self.user_data.name) 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( self.role_data, user_data=self.user_data, @@ -1381,7 +1381,7 @@ class TestRoleAssignment(base.TestCase): with testtools.ExpectedException( exc.OpenStackCloudException, - 'Must specify either a domain or project' + 'Must specify either a domain, project or system' ): self.cloud.revoke_role( self.role_data.role_name, diff --git a/openstack/tests/unit/identity/v3/test_proxy.py b/openstack/tests/unit/identity/v3/test_proxy.py index b2af2b570..62259c735 100644 --- a/openstack/tests/unit/identity/v3/test_proxy.py +++ b/openstack/tests/unit/identity/v3/test_proxy.py @@ -499,3 +499,75 @@ class TestIdentityProxyRoleAssignments(TestIdentityProxyBase): 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') + ] + ) diff --git a/openstack/tests/unit/identity/v3/test_role_system_group_assignment.py b/openstack/tests/unit/identity/v3/test_role_system_group_assignment.py new file mode 100644 index 000000000..17b8ec14b --- /dev/null +++ b/openstack/tests/unit/identity/v3/test_role_system_group_assignment.py @@ -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) diff --git a/openstack/tests/unit/identity/v3/test_role_system_user_assignment.py b/openstack/tests/unit/identity/v3/test_role_system_user_assignment.py new file mode 100644 index 000000000..98f9cf68a --- /dev/null +++ b/openstack/tests/unit/identity/v3/test_role_system_user_assignment.py @@ -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) diff --git a/releasenotes/notes/add-system-role-assignment-693dd3e1da33a54d.yaml b/releasenotes/notes/add-system-role-assignment-693dd3e1da33a54d.yaml new file mode 100644 index 000000000..cb171b3ef --- /dev/null +++ b/releasenotes/notes/add-system-role-assignment-693dd3e1da33a54d.yaml @@ -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 + `_.