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:
parent
12fa1370bc
commit
bdd87c7042
@ -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
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
28
openstack/identity/v3/role_system_group_assignment.py
Normal file
28
openstack/identity/v3/role_system_group_assignment.py
Normal 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')
|
28
openstack/identity/v3/role_system_user_assignment.py
Normal file
28
openstack/identity/v3/role_system_user_assignment.py
Normal 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')
|
75
openstack/identity/v3/system.py
Normal file
75
openstack/identity/v3/system.py
Normal 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
|
@ -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))
|
||||||
|
@ -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,
|
||||||
|
@ -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')
|
||||||
|
]
|
||||||
|
)
|
||||||
|
@ -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)
|
@ -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)
|
@ -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>`_.
|
Loading…
x
Reference in New Issue
Block a user