Merge "Enhance manager list_role_assignments to support group listing"
This commit is contained in:
commit
f473352e1a
@ -13,8 +13,6 @@
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
import sqlalchemy
|
||||
from sqlalchemy.sql.expression import false
|
||||
|
||||
from keystone import assignment as keystone_assignment
|
||||
from keystone.common import sql
|
||||
@ -122,105 +120,6 @@ class Assignment(keystone_assignment.AssignmentDriverV9):
|
||||
actor_id=actor_id,
|
||||
target_id=target_id)
|
||||
|
||||
def _list_project_ids_for_actor(self, actors, hints, inherited,
|
||||
group_only=False):
|
||||
# TODO(henry-nash): Now that we have a single assignment table, we
|
||||
# should be able to honor the hints list that is provided.
|
||||
|
||||
assignment_type = [AssignmentType.GROUP_PROJECT]
|
||||
if not group_only:
|
||||
assignment_type.append(AssignmentType.USER_PROJECT)
|
||||
|
||||
sql_constraints = sqlalchemy.and_(
|
||||
RoleAssignment.type.in_(assignment_type),
|
||||
RoleAssignment.inherited == inherited,
|
||||
RoleAssignment.actor_id.in_(actors))
|
||||
|
||||
with sql.transaction() as session:
|
||||
query = session.query(RoleAssignment.target_id).filter(
|
||||
sql_constraints).distinct()
|
||||
|
||||
return [x.target_id for x in query.all()]
|
||||
|
||||
def list_role_ids_for_groups_on_domain(self, group_ids, domain_id):
|
||||
if not group_ids:
|
||||
# If there's no groups then there will be no domain roles.
|
||||
return []
|
||||
|
||||
sql_constraints = sqlalchemy.and_(
|
||||
RoleAssignment.type == AssignmentType.GROUP_DOMAIN,
|
||||
RoleAssignment.target_id == domain_id,
|
||||
RoleAssignment.inherited == false(),
|
||||
RoleAssignment.actor_id.in_(group_ids))
|
||||
|
||||
with sql.transaction() as session:
|
||||
query = session.query(RoleAssignment.role_id).filter(
|
||||
sql_constraints).distinct()
|
||||
return [role.role_id for role in query.all()]
|
||||
|
||||
def list_role_ids_for_groups_on_project(
|
||||
self, group_ids, project_id, project_domain_id, project_parents):
|
||||
|
||||
if not group_ids:
|
||||
# If there's no groups then there will be no project roles.
|
||||
return []
|
||||
|
||||
# NOTE(rodrigods): First, we always include projects with
|
||||
# non-inherited assignments
|
||||
sql_constraints = sqlalchemy.and_(
|
||||
RoleAssignment.type == AssignmentType.GROUP_PROJECT,
|
||||
RoleAssignment.inherited == false(),
|
||||
RoleAssignment.target_id == project_id)
|
||||
|
||||
if CONF.os_inherit.enabled:
|
||||
# Inherited roles from domains
|
||||
sql_constraints = sqlalchemy.or_(
|
||||
sql_constraints,
|
||||
sqlalchemy.and_(
|
||||
RoleAssignment.type == AssignmentType.GROUP_DOMAIN,
|
||||
RoleAssignment.inherited,
|
||||
RoleAssignment.target_id == project_domain_id))
|
||||
|
||||
# Inherited roles from projects
|
||||
if project_parents:
|
||||
sql_constraints = sqlalchemy.or_(
|
||||
sql_constraints,
|
||||
sqlalchemy.and_(
|
||||
RoleAssignment.type == AssignmentType.GROUP_PROJECT,
|
||||
RoleAssignment.inherited,
|
||||
RoleAssignment.target_id.in_(project_parents)))
|
||||
|
||||
sql_constraints = sqlalchemy.and_(
|
||||
sql_constraints, RoleAssignment.actor_id.in_(group_ids))
|
||||
|
||||
with sql.transaction() as session:
|
||||
# NOTE(morganfainberg): Only select the columns we actually care
|
||||
# about here, in this case role_id.
|
||||
query = session.query(RoleAssignment.role_id).filter(
|
||||
sql_constraints).distinct()
|
||||
|
||||
return [result.role_id for result in query.all()]
|
||||
|
||||
def list_project_ids_for_groups(self, group_ids, hints,
|
||||
inherited=False):
|
||||
return self._list_project_ids_for_actor(
|
||||
group_ids, hints, inherited, group_only=True)
|
||||
|
||||
def list_domain_ids_for_groups(self, group_ids, inherited=False):
|
||||
if not group_ids:
|
||||
# If there's no groups then there will be no domains.
|
||||
return []
|
||||
|
||||
group_sql_conditions = sqlalchemy.and_(
|
||||
RoleAssignment.type == AssignmentType.GROUP_DOMAIN,
|
||||
RoleAssignment.inherited == inherited,
|
||||
RoleAssignment.actor_id.in_(group_ids))
|
||||
|
||||
with sql.transaction() as session:
|
||||
query = session.query(RoleAssignment.target_id).filter(
|
||||
group_sql_conditions).distinct()
|
||||
return [x.target_id for x in query.all()]
|
||||
|
||||
def add_role_to_user_and_project(self, user_id, tenant_id, role_id):
|
||||
try:
|
||||
with sql.transaction() as session:
|
||||
|
@ -133,16 +133,18 @@ class Manager(manager.Manager):
|
||||
def get_roles_for_groups(self, group_ids, project_id=None, domain_id=None):
|
||||
"""Get a list of roles for this group on domain and/or project."""
|
||||
if project_id is not None:
|
||||
project = self.resource_api.get_project(project_id)
|
||||
role_ids = self.list_role_ids_for_groups_on_project(
|
||||
group_ids, project_id, project['domain_id'],
|
||||
self._list_parent_ids_of_project(project_id))
|
||||
self.resource_api.get_project(project_id)
|
||||
assignment_list = self.list_role_assignments(
|
||||
source_from_group_ids=group_ids, project_id=project_id,
|
||||
effective=True)
|
||||
elif domain_id is not None:
|
||||
role_ids = self.list_role_ids_for_groups_on_domain(
|
||||
group_ids, domain_id)
|
||||
assignment_list = self.list_role_assignments(
|
||||
source_from_group_ids=group_ids, domain_id=domain_id,
|
||||
effective=True)
|
||||
else:
|
||||
raise AttributeError(_("Must specify either domain or project"))
|
||||
|
||||
role_ids = list(set([x['role_id'] for x in assignment_list]))
|
||||
return self.role_api.list_roles_from_ids(role_ids)
|
||||
|
||||
def add_user_to_project(self, tenant_id, user_id):
|
||||
@ -244,37 +246,18 @@ class Manager(manager.Manager):
|
||||
return self.resource_api.list_domains_from_ids(domain_ids)
|
||||
|
||||
def list_domains_for_groups(self, group_ids):
|
||||
domain_ids = self.list_domain_ids_for_groups(group_ids)
|
||||
assignment_list = self.list_role_assignments(
|
||||
source_from_group_ids=group_ids, effective=True)
|
||||
domain_ids = list(set([x['domain_id'] for x in assignment_list
|
||||
if x.get('domain_id')]))
|
||||
return self.resource_api.list_domains_from_ids(domain_ids)
|
||||
|
||||
def list_projects_for_groups(self, group_ids):
|
||||
project_ids = (
|
||||
self.list_project_ids_for_groups(group_ids, driver_hints.Hints()))
|
||||
if not CONF.os_inherit.enabled:
|
||||
return self.resource_api.list_projects_from_ids(project_ids)
|
||||
|
||||
# os_inherit extension is enabled, so check to see if these groups have
|
||||
# any inherited role assignment on: i) any domain, in which case we
|
||||
# must add in all the projects in that domain; ii) any project, in
|
||||
# which case we must add in all the subprojects under that project in
|
||||
# the hierarchy.
|
||||
|
||||
domain_ids = self.list_domain_ids_for_groups(group_ids, inherited=True)
|
||||
|
||||
project_ids_from_domains = (
|
||||
self.resource_api.list_project_ids_from_domain_ids(domain_ids))
|
||||
|
||||
parents_ids = self.list_project_ids_for_groups(group_ids,
|
||||
driver_hints.Hints(),
|
||||
inherited=True)
|
||||
|
||||
subproject_ids = []
|
||||
for parent_id in parents_ids:
|
||||
subtree = self.resource_api.list_projects_in_subtree(parent_id)
|
||||
subproject_ids += [subproject['id'] for subproject in subtree]
|
||||
|
||||
return self.resource_api.list_projects_from_ids(
|
||||
list(set(project_ids + project_ids_from_domains + subproject_ids)))
|
||||
assignment_list = self.list_role_assignments(
|
||||
source_from_group_ids=group_ids, effective=True)
|
||||
project_ids = list(set([x['project_id'] for x in assignment_list
|
||||
if x.get('project_id')]))
|
||||
return self.resource_api.list_projects_from_ids(project_ids)
|
||||
|
||||
@notifications.role_assignment('deleted')
|
||||
def _remove_role_from_user_and_project_adapter(self, role_id, user_id=None,
|
||||
@ -409,8 +392,8 @@ class Manager(manager.Manager):
|
||||
# kept as it is in order to detect unnecessarily complex code, which is not
|
||||
# this case.
|
||||
|
||||
def _expand_indirect_assignment(self, ref, user_id=None,
|
||||
project_id=None, subtree_ids=None):
|
||||
def _expand_indirect_assignment(self, ref, user_id=None, project_id=None,
|
||||
subtree_ids=None, expand_groups=True):
|
||||
"""Returns a list of expanded role assignments.
|
||||
|
||||
This methods is called for each discovered assignment that either needs
|
||||
@ -428,6 +411,9 @@ class Manager(manager.Manager):
|
||||
already ensured only those assignments that could affect them
|
||||
were passed to this method.
|
||||
|
||||
If expand_groups is True then we expand groups out to a list of
|
||||
assignments, one for each member of that group.
|
||||
|
||||
"""
|
||||
def create_group_assignment(base_ref, user_id):
|
||||
"""Creates a group assignment from the provided ref."""
|
||||
@ -482,12 +468,15 @@ class Manager(manager.Manager):
|
||||
for m in self.identity_api.list_users_in_group(
|
||||
ref['group_id'])]
|
||||
|
||||
def expand_inherited_assignment(ref, user_id, project_id, subtree_ids):
|
||||
def expand_inherited_assignment(ref, user_id, project_id, subtree_ids,
|
||||
expand_groups):
|
||||
"""Expands inherited role assignments.
|
||||
|
||||
If this is a group role assignment on a target, replace it by a
|
||||
list of role assignments containing one for each user of that
|
||||
group, on every project under that target.
|
||||
If expand_groups is True and this is a group role assignment on a
|
||||
target, replace it by a list of role assignments containing one for
|
||||
each user of that group, on every project under that target. If
|
||||
expand_groups is False, then return a group assignment on an
|
||||
inherited target.
|
||||
|
||||
If this is a user role assignment on a specific target (i.e.
|
||||
project_id is specified, but subtree_ids is None) then simply
|
||||
@ -587,8 +576,15 @@ class Manager(manager.Manager):
|
||||
|
||||
new_refs = []
|
||||
if 'group_id' in ref:
|
||||
# Expand role assignment for all members and for all projects
|
||||
for ref in expand_group_assignment(ref, user_id):
|
||||
if expand_groups:
|
||||
# Expand role assignment to all group members on any
|
||||
# inherited target of any of the projects
|
||||
for ref in expand_group_assignment(ref, user_id):
|
||||
new_refs += [create_inherited_assignment(ref, proj_id)
|
||||
for proj_id in project_ids]
|
||||
else:
|
||||
# Just place the group assignment on any inherited target
|
||||
# of any of the projects
|
||||
new_refs += [create_inherited_assignment(ref, proj_id)
|
||||
for proj_id in project_ids]
|
||||
else:
|
||||
@ -599,9 +595,9 @@ class Manager(manager.Manager):
|
||||
return new_refs
|
||||
|
||||
if ref.get('inherited_to_projects') == 'projects':
|
||||
return expand_inherited_assignment(ref, user_id, project_id,
|
||||
subtree_ids)
|
||||
elif 'group_id' in ref:
|
||||
return expand_inherited_assignment(
|
||||
ref, user_id, project_id, subtree_ids, expand_groups)
|
||||
elif 'group_id' in ref and expand_groups:
|
||||
return expand_group_assignment(ref, user_id)
|
||||
return [ref]
|
||||
|
||||
@ -669,7 +665,7 @@ class Manager(manager.Manager):
|
||||
|
||||
def _list_effective_role_assignments(self, role_id, user_id, group_id,
|
||||
domain_id, project_id, subtree_ids,
|
||||
inherited):
|
||||
inherited, source_from_group_ids):
|
||||
"""List role assignments in effective mode.
|
||||
|
||||
When using effective mode, besides the direct assignments, the indirect
|
||||
@ -786,24 +782,31 @@ class Manager(manager.Manager):
|
||||
if group_id or (domain_id and inherited):
|
||||
return []
|
||||
|
||||
if user_id and source_from_group_ids:
|
||||
# You can't do both - and since source_from_group_ids is only used
|
||||
# internally, this must be a coding error by the caller.
|
||||
msg = _('Cannot list assignments sourced from groups and filtered '
|
||||
'by user ID.')
|
||||
raise exception.UnexpectedError(msg)
|
||||
|
||||
# If filtering by domain, then only non-inherited assignments are
|
||||
# relevant, since domains don't inherit assignments
|
||||
inherited = False if domain_id else inherited
|
||||
|
||||
# List user assignments.
|
||||
# List user or explicit group assignments.
|
||||
# Due to the need to expand implied roles, this call will skip
|
||||
# filtering by role_id and instead return the whole set of roles.
|
||||
# Matching on the specified role is performed at the end.
|
||||
|
||||
direct_refs = list_role_assignments_for_actor(
|
||||
role_id=None, user_id=user_id, project_id=project_id,
|
||||
subtree_ids=subtree_ids, domain_id=domain_id,
|
||||
inherited=inherited)
|
||||
role_id=None, user_id=user_id, group_ids=source_from_group_ids,
|
||||
project_id=project_id, subtree_ids=subtree_ids,
|
||||
domain_id=domain_id, inherited=inherited)
|
||||
|
||||
# And those from the user's groups. Again, role_id is not
|
||||
# used to filter here
|
||||
# And those from the user's groups, so long as we are not restricting
|
||||
# to a set of source groups (in which case we already got those
|
||||
# assignments in the direct listing above).
|
||||
group_refs = []
|
||||
if user_id:
|
||||
if not source_from_group_ids and user_id:
|
||||
group_ids = self._get_group_ids_for_user_id(user_id)
|
||||
if group_ids:
|
||||
group_refs = list_role_assignments_for_actor(
|
||||
@ -813,10 +816,10 @@ class Manager(manager.Manager):
|
||||
|
||||
# Expand grouping and inheritance on retrieved role assignments
|
||||
refs = []
|
||||
expand_groups = (source_from_group_ids is None)
|
||||
for ref in (direct_refs + group_refs):
|
||||
refs += self._expand_indirect_assignment(
|
||||
ref=ref, user_id=user_id, project_id=project_id,
|
||||
subtree_ids=subtree_ids)
|
||||
ref, user_id, project_id, subtree_ids, expand_groups)
|
||||
|
||||
refs = self._add_implied_roles(refs)
|
||||
if role_id:
|
||||
@ -850,7 +853,8 @@ class Manager(manager.Manager):
|
||||
def list_role_assignments(self, role_id=None, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
include_subtree=False, inherited=None,
|
||||
effective=None, include_names=False):
|
||||
effective=None, include_names=False,
|
||||
source_from_group_ids=None):
|
||||
"""List role assignments, honoring effective mode and provided filters.
|
||||
|
||||
Returns a list of role assignments, where their attributes match the
|
||||
@ -869,7 +873,18 @@ class Manager(manager.Manager):
|
||||
affect a user, for example the roles that would be placed in a token.
|
||||
|
||||
If include_names is set to true the entities' names are returned
|
||||
in addition to their id's
|
||||
in addition to their id's.
|
||||
|
||||
source_from_group_ids is a list of group IDs and, if specified, then
|
||||
only those assignments that are derived from membership of these groups
|
||||
are considered, and any such assignments will not be expanded into
|
||||
their user membership assignments. This is different to a group filter
|
||||
of the resulting list, instead being a restriction on which assignments
|
||||
should be considered before expansion of inheritance. This option is
|
||||
only used internally (i.e. it is not exposed at the API level) and is
|
||||
only supported in effective mode (since in regular mode there is no
|
||||
difference between this and a group filter, other than it is a list of
|
||||
groups).
|
||||
|
||||
If OS-INHERIT extension is disabled or the used driver does not support
|
||||
inherited roles retrieval, inherited role assignments will be ignored.
|
||||
@ -889,7 +904,7 @@ class Manager(manager.Manager):
|
||||
if effective:
|
||||
role_assignments = self._list_effective_role_assignments(
|
||||
role_id, user_id, group_id, domain_id, project_id,
|
||||
subtree_ids, inherited)
|
||||
subtree_ids, inherited, source_from_group_ids)
|
||||
else:
|
||||
role_assignments = self._list_direct_role_assignments(
|
||||
role_id, user_id, group_id, domain_id, project_id,
|
||||
@ -1105,71 +1120,6 @@ class AssignmentDriverBase(object):
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_project_ids_for_groups(self, group_ids, hints,
|
||||
inherited=False):
|
||||
"""List project ids accessible to specified groups.
|
||||
|
||||
:param group_ids: List of group ids.
|
||||
:param hints: filter hints which the driver should
|
||||
implement if at all possible.
|
||||
:param inherited: whether assignments marked as inherited should
|
||||
be included.
|
||||
:returns: List of project ids accessible to specified groups.
|
||||
|
||||
This method should not try and expand any inherited assignments,
|
||||
just report the projects that have the role for this group. The manager
|
||||
method is responsible for expanding out inherited assignments.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_domain_ids_for_groups(self, group_ids, inherited=False):
|
||||
"""List domain ids accessible to specified groups.
|
||||
|
||||
:param group_ids: List of group ids.
|
||||
:param inherited: whether to return domain_ids that have inherited
|
||||
assignments or not.
|
||||
:returns: List of domain ids accessible to specified groups.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_role_ids_for_groups_on_project(
|
||||
self, group_ids, project_id, project_domain_id, project_parents):
|
||||
"""List the group role ids for a specific project.
|
||||
|
||||
Supports the ``OS-INHERIT`` role inheritance from the project's domain
|
||||
if supported by the assignment driver.
|
||||
|
||||
:param group_ids: list of group ids
|
||||
:type group_ids: list
|
||||
:param project_id: project identifier
|
||||
:type project_id: str
|
||||
:param project_domain_id: project's domain identifier
|
||||
:type project_domain_id: str
|
||||
:param project_parents: list of parent ids of this project
|
||||
:type project_parents: list
|
||||
:returns: list of role ids for the project
|
||||
:rtype: list
|
||||
"""
|
||||
raise exception.NotImplemented()
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_role_ids_for_groups_on_domain(self, group_ids, domain_id):
|
||||
"""List the group role ids for a specific domain.
|
||||
|
||||
:param group_ids: list of group ids
|
||||
:type group_ids: list
|
||||
:param domain_id: domain identifier
|
||||
:type domain_id: str
|
||||
:returns: list of role ids for the project
|
||||
:rtype: list
|
||||
"""
|
||||
raise exception.NotImplemented()
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_project_assignments(self, project_id):
|
||||
"""Deletes all assignments for a project.
|
||||
@ -1265,6 +1215,71 @@ class AssignmentDriverV8(AssignmentDriverBase):
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_project_ids_for_groups(self, group_ids, hints,
|
||||
inherited=False):
|
||||
"""List project ids accessible to specified groups.
|
||||
|
||||
:param group_ids: List of group ids.
|
||||
:param hints: filter hints which the driver should
|
||||
implement if at all possible.
|
||||
:param inherited: whether assignments marked as inherited should
|
||||
be included.
|
||||
:returns: List of project ids accessible to specified groups.
|
||||
|
||||
This method should not try and expand any inherited assignments,
|
||||
just report the projects that have the role for this group. The manager
|
||||
method is responsible for expanding out inherited assignments.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_domain_ids_for_groups(self, group_ids, inherited=False):
|
||||
"""List domain ids accessible to specified groups.
|
||||
|
||||
:param group_ids: List of group ids.
|
||||
:param inherited: whether to return domain_ids that have inherited
|
||||
assignments or not.
|
||||
:returns: List of domain ids accessible to specified groups.
|
||||
|
||||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_role_ids_for_groups_on_project(
|
||||
self, group_ids, project_id, project_domain_id, project_parents):
|
||||
"""List the group role ids for a specific project.
|
||||
|
||||
Supports the ``OS-INHERIT`` role inheritance from the project's domain
|
||||
if supported by the assignment driver.
|
||||
|
||||
:param group_ids: list of group ids
|
||||
:type group_ids: list
|
||||
:param project_id: project identifier
|
||||
:type project_id: str
|
||||
:param project_domain_id: project's domain identifier
|
||||
:type project_domain_id: str
|
||||
:param project_parents: list of parent ids of this project
|
||||
:type project_parents: list
|
||||
:returns: list of role ids for the project
|
||||
:rtype: list
|
||||
"""
|
||||
raise exception.NotImplemented()
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_role_ids_for_groups_on_domain(self, group_ids, domain_id):
|
||||
"""List the group role ids for a specific domain.
|
||||
|
||||
:param group_ids: list of group ids
|
||||
:type group_ids: list
|
||||
:param domain_id: domain identifier
|
||||
:type domain_id: str
|
||||
:returns: list of role ids for the project
|
||||
:rtype: list
|
||||
"""
|
||||
raise exception.NotImplemented()
|
||||
|
||||
|
||||
class AssignmentDriverV9(AssignmentDriverBase):
|
||||
"""New or redefined methods from V8.
|
||||
@ -1362,24 +1377,6 @@ class V9AssignmentWrapperForV8Driver(AssignmentDriverV9):
|
||||
domain_id=domain_id, project_ids=project_ids,
|
||||
inherited_to_projects=inherited_to_projects)
|
||||
|
||||
def list_project_ids_for_groups(self, group_ids, hints,
|
||||
inherited=False):
|
||||
return self.driver.list_project_ids_for_groups(
|
||||
group_ids, hints, inherited=inherited)
|
||||
|
||||
def list_domain_ids_for_groups(self, group_ids, inherited=False):
|
||||
return self.driver.list_domain_ids_for_groups(
|
||||
group_ids, inherited=inherited)
|
||||
|
||||
def list_role_ids_for_groups_on_project(
|
||||
self, group_ids, project_id, project_domain_id, project_parents):
|
||||
return self.driver.list_role_ids_for_groups_on_project(
|
||||
group_ids, project_id, project_domain_id, project_parents)
|
||||
|
||||
def list_role_ids_for_groups_on_domain(self, group_ids, domain_id):
|
||||
return self.driver.list_role_ids_for_groups_on_domain(
|
||||
group_ids, domain_id)
|
||||
|
||||
def delete_project_assignments(self, project_id):
|
||||
self.driver.delete_project_assignments(project_id)
|
||||
|
||||
|
@ -138,8 +138,9 @@ class AssignmentTestHelperMixin(object):
|
||||
'results': [{'group': 0, 'role': 2, 'domain': 0},
|
||||
{'user': 0, 'role': 2, 'project': 0}]}]
|
||||
|
||||
# The 'params' key also supports the 'effective' and
|
||||
# 'inherited_to_projects' options to list_role_assignments.}
|
||||
# The 'params' key also supports the 'effective',
|
||||
# 'inherited_to_projects' and 'source_from_group_ids' options to
|
||||
# list_role_assignments.}
|
||||
|
||||
"""
|
||||
|
||||
@ -394,6 +395,13 @@ class AssignmentTestHelperMixin(object):
|
||||
expected_assignment[key] = value
|
||||
self.assertIn(expected_assignment, actual)
|
||||
|
||||
def convert_group_ids_sourced_from_list(index_list, reference_data):
|
||||
value_list = []
|
||||
for group_index in index_list:
|
||||
value_list.append(
|
||||
reference_data['groups'][group_index]['id'])
|
||||
return value_list
|
||||
|
||||
# Go through each test in the array, processing the input params, which
|
||||
# we build into an args dict, and then call list_role_assignments. Then
|
||||
# check the results against those specified in the test plan.
|
||||
@ -403,6 +411,10 @@ class AssignmentTestHelperMixin(object):
|
||||
if param in ['effective', 'inherited', 'include_subtree']:
|
||||
# Just pass the value into the args
|
||||
args[param] = test['params'][param]
|
||||
elif param == 'source_from_group_ids':
|
||||
# Convert the list of indexes into a list of IDs
|
||||
args[param] = convert_group_ids_sourced_from_list(
|
||||
test['params']['source_from_group_ids'], test_data)
|
||||
else:
|
||||
# Turn 'entity : 0' into 'entity_id = ac6736ba873d'
|
||||
# where entity in user, group, project or domain
|
||||
@ -1970,6 +1982,91 @@ class IdentityTests(AssignmentTestHelperMixin):
|
||||
}
|
||||
self.execute_assignment_plan(test_plan)
|
||||
|
||||
def test_list_role_assignment_using_sourced_groups(self):
|
||||
"""Test listing assignments when restricted by source groups."""
|
||||
test_plan = {
|
||||
# The default domain with 3 users, 3 groups, 3 projects,
|
||||
# plus 3 roles.
|
||||
'entities': {'domains': {'id': DEFAULT_DOMAIN_ID,
|
||||
'users': 3, 'groups': 3, 'projects': 3},
|
||||
'roles': 3},
|
||||
# Users 0 & 1 are in the group 0, User 0 also in group 1
|
||||
'group_memberships': [{'group': 0, 'users': [0, 1]},
|
||||
{'group': 1, 'users': [0]}],
|
||||
# Spread the assignments around - we want to be able to show that
|
||||
# if sourced by group, assignments from other sources are excluded
|
||||
'assignments': [{'user': 0, 'role': 0, 'project': 0},
|
||||
{'group': 0, 'role': 1, 'project': 1},
|
||||
{'group': 1, 'role': 2, 'project': 0},
|
||||
{'group': 1, 'role': 2, 'project': 1},
|
||||
{'user': 2, 'role': 1, 'project': 1},
|
||||
{'group': 2, 'role': 2, 'project': 2}
|
||||
],
|
||||
'tests': [
|
||||
# List all effective assignments sourced from groups 0 and 1
|
||||
{'params': {'source_from_group_ids': [0, 1],
|
||||
'effective': True},
|
||||
'results': [{'group': 0, 'role': 1, 'project': 1},
|
||||
{'group': 1, 'role': 2, 'project': 0},
|
||||
{'group': 1, 'role': 2, 'project': 1}
|
||||
]},
|
||||
# Adding a role a filter should further restrict the entries
|
||||
{'params': {'source_from_group_ids': [0, 1], 'role': 2,
|
||||
'effective': True},
|
||||
'results': [{'group': 1, 'role': 2, 'project': 0},
|
||||
{'group': 1, 'role': 2, 'project': 1}
|
||||
]},
|
||||
]
|
||||
}
|
||||
self.execute_assignment_plan(test_plan)
|
||||
|
||||
def test_list_role_assignment_using_sourced_groups_with_domains(self):
|
||||
"""Test listing domain assignments when restricted by source groups."""
|
||||
test_plan = {
|
||||
# A domain with 3 users, 3 groups, 3 projects, a second domain,
|
||||
# plus 3 roles.
|
||||
'entities': {'domains': [{'users': 3, 'groups': 3, 'projects': 3},
|
||||
1],
|
||||
'roles': 3},
|
||||
# Users 0 & 1 are in the group 0, User 0 also in group 1
|
||||
'group_memberships': [{'group': 0, 'users': [0, 1]},
|
||||
{'group': 1, 'users': [0]}],
|
||||
# Spread the assignments around - we want to be able to show that
|
||||
# if sourced by group, assignments from other sources are excluded
|
||||
'assignments': [{'user': 0, 'role': 0, 'domain': 0},
|
||||
{'group': 0, 'role': 1, 'domain': 1},
|
||||
{'group': 1, 'role': 2, 'project': 0},
|
||||
{'group': 1, 'role': 2, 'project': 1},
|
||||
{'user': 2, 'role': 1, 'project': 1},
|
||||
{'group': 2, 'role': 2, 'project': 2}
|
||||
],
|
||||
'tests': [
|
||||
# List all effective assignments sourced from groups 0 and 1
|
||||
{'params': {'source_from_group_ids': [0, 1],
|
||||
'effective': True},
|
||||
'results': [{'group': 0, 'role': 1, 'domain': 1},
|
||||
{'group': 1, 'role': 2, 'project': 0},
|
||||
{'group': 1, 'role': 2, 'project': 1}
|
||||
]},
|
||||
# Adding a role a filter should further restrict the entries
|
||||
{'params': {'source_from_group_ids': [0, 1], 'role': 1,
|
||||
'effective': True},
|
||||
'results': [{'group': 0, 'role': 1, 'domain': 1},
|
||||
]},
|
||||
]
|
||||
}
|
||||
self.execute_assignment_plan(test_plan)
|
||||
|
||||
def test_list_role_assignment_fails_with_userid_and_source_groups(self):
|
||||
"""Show we trap this unsupported internal combination of params."""
|
||||
group = unit.new_group_ref(domain_id=DEFAULT_DOMAIN_ID)
|
||||
group = self.identity_api.create_group(group)
|
||||
self.assertRaises(exception.UnexpectedError,
|
||||
self.assignment_api.list_role_assignments,
|
||||
effective=True,
|
||||
user_id=self.user_foo['id'],
|
||||
source_from_group_ids=[group['id']])
|
||||
|
||||
def test_delete_domain_with_user_group_project_links(self):
|
||||
# TODO(chungg):add test case once expected behaviour defined
|
||||
pass
|
||||
@ -6546,6 +6643,47 @@ class InheritanceTests(AssignmentTestHelperMixin):
|
||||
for x in range(0, 4):
|
||||
self.assertIn(test_data['users'][x]['id'], user_ids)
|
||||
|
||||
def test_list_role_assignment_using_inherited_sourced_groups(self):
|
||||
"""Test listing inherited assignments when restricted by groups."""
|
||||
test_plan = {
|
||||
# A domain with 3 users, 3 groups, 3 projects, a second domain,
|
||||
# plus 3 roles.
|
||||
'entities': {'domains': [{'users': 3, 'groups': 3, 'projects': 3},
|
||||
1],
|
||||
'roles': 3},
|
||||
# Users 0 & 1 are in the group 0, User 0 also in group 1
|
||||
'group_memberships': [{'group': 0, 'users': [0, 1]},
|
||||
{'group': 1, 'users': [0]}],
|
||||
# Spread the assignments around - we want to be able to show that
|
||||
# if sourced by group, assignments from other sources are excluded
|
||||
'assignments': [{'user': 0, 'role': 0, 'domain': 0},
|
||||
{'group': 0, 'role': 1, 'domain': 1},
|
||||
{'group': 1, 'role': 2, 'domain': 0,
|
||||
'inherited_to_projects': True},
|
||||
{'group': 1, 'role': 2, 'project': 1},
|
||||
{'user': 2, 'role': 1, 'project': 1,
|
||||
'inherited_to_projects': True},
|
||||
{'group': 2, 'role': 2, 'project': 2}
|
||||
],
|
||||
'tests': [
|
||||
# List all effective assignments sourced from groups 0 and 1.
|
||||
# We should see the inherited group assigned on the 3 projects
|
||||
# from domain 0, as well as the direct assignments.
|
||||
{'params': {'source_from_group_ids': [0, 1],
|
||||
'effective': True},
|
||||
'results': [{'group': 0, 'role': 1, 'domain': 1},
|
||||
{'group': 1, 'role': 2, 'project': 0,
|
||||
'indirect': {'domain': 0}},
|
||||
{'group': 1, 'role': 2, 'project': 1,
|
||||
'indirect': {'domain': 0}},
|
||||
{'group': 1, 'role': 2, 'project': 2,
|
||||
'indirect': {'domain': 0}},
|
||||
{'group': 1, 'role': 2, 'project': 1}
|
||||
]},
|
||||
]
|
||||
}
|
||||
self.execute_assignment_plan(test_plan)
|
||||
|
||||
|
||||
class ImpliedRoleTests(AssignmentTestHelperMixin):
|
||||
|
||||
|
@ -981,6 +981,13 @@ class BaseLDAPIdentity(test_backend.IdentityTests):
|
||||
def test_domain_crud(self):
|
||||
self.skipTest('Resource LDAP has been removed')
|
||||
|
||||
def test_list_role_assignment_using_sourced_groups_with_domains(self):
|
||||
"""Multiple domain assignments are not supported."""
|
||||
self.assertRaises(
|
||||
(exception.Forbidden, exception.DomainNotFound),
|
||||
super(BaseLDAPIdentity, self).
|
||||
test_list_role_assignment_using_sourced_groups_with_domains)
|
||||
|
||||
|
||||
class LDAPIdentity(BaseLDAPIdentity, unit.TestCase):
|
||||
|
||||
@ -2553,6 +2560,12 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, unit.SQLDriverOverrides,
|
||||
super(BaseLDAPIdentity, self).\
|
||||
test_list_role_assignment_by_user_with_domain_group_roles
|
||||
|
||||
def test_list_role_assignment_using_sourced_groups_with_domains(self):
|
||||
# With SQL Assignment this method should work, so override the override
|
||||
# from BaseLDAPIdentity
|
||||
base = super(BaseLDAPIdentity, self)
|
||||
base.test_list_role_assignment_using_sourced_groups_with_domains()
|
||||
|
||||
|
||||
class MultiLDAPandSQLIdentityDomainConfigsInSQL(MultiLDAPandSQLIdentity):
|
||||
"""Class to test the use of domain configs stored in the database.
|
||||
|
@ -1,8 +1,10 @@
|
||||
---
|
||||
features:
|
||||
- The list_project_ids_for_user(), list_domain_ids_for_user() and
|
||||
list_user_ids_for_project() methods have been removed from the V9 version
|
||||
of the Assignment driver.
|
||||
- The list_project_ids_for_user(), list_domain_ids_for_user(),
|
||||
list_user_ids_for_project(), list_project_ids_for_groups(),
|
||||
list_domain_ids_for_groups(), list_role_ids_for_groups_on_project() and
|
||||
list_role_ids_for_groups_on_domain() methods have been removed from the
|
||||
V9 version of the Assignment driver.
|
||||
upgrade:
|
||||
- The V8 Assignment driver interface is deprecated, but still supported in
|
||||
this release, so any custom drivers based on the V8 interface should still
|
||||
|
Loading…
Reference in New Issue
Block a user