Merge "Extracting get group roles for project logic to drivers."
This commit is contained in:
commit
51f19000ff
|
@ -15,10 +15,14 @@
|
|||
from keystone import assignment
|
||||
from keystone import clean
|
||||
from keystone.common import kvs
|
||||
from keystone import config
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
class Assignment(kvs.Base, assignment.Driver):
|
||||
"""KVS Assignment backend.
|
||||
|
||||
|
@ -119,6 +123,31 @@ class Assignment(kvs.Base, assignment.Driver):
|
|||
except exception.NotFound:
|
||||
raise exception.RoleNotFound(role_id=role_id)
|
||||
|
||||
def get_group_project_roles(self, groups, project_id, project_domain_id):
|
||||
role_list = []
|
||||
for group_id in groups:
|
||||
try:
|
||||
metadata_ref = self._get_metadata(
|
||||
group_id=group_id, tenant_id=project_id)
|
||||
role_list += self._roles_from_role_dicts(
|
||||
metadata_ref.get('roles', {}), False)
|
||||
except exception.MetadataNotFound:
|
||||
# no group assignment, skip
|
||||
pass
|
||||
|
||||
if CONF.os_inherit.enabled:
|
||||
# Now get any inherited group roles for the owning domain
|
||||
try:
|
||||
metadata_ref = self._get_metadata(
|
||||
group_id=group_id,
|
||||
domain_id=project_domain_id)
|
||||
role_list += self._roles_from_role_dicts(
|
||||
metadata_ref.get('roles', {}), True)
|
||||
except exception.MetadataNotFound:
|
||||
pass
|
||||
|
||||
return role_list
|
||||
|
||||
def list_roles(self, hints):
|
||||
return self._list_roles()
|
||||
|
||||
|
@ -127,25 +156,43 @@ class Assignment(kvs.Base, assignment.Driver):
|
|||
return [self.get_role(x) for x in role_ids]
|
||||
|
||||
def list_projects_for_user(self, user_id, group_ids, hints):
|
||||
# NOTE(henry-nash): The kvs backend is being deprecated, so no
|
||||
# support is provided for projects that the user has a role on solely
|
||||
# by virtue of group membership.
|
||||
|
||||
project_ids = set()
|
||||
all_projects = self.list_projects(hints=None)
|
||||
|
||||
metadata_keys = (k for k in self.db.keys()
|
||||
if k.startswith('metadata_user-'))
|
||||
if (k.startswith('metadata_user-') or
|
||||
k.startswith('metadata_group-')))
|
||||
for key in metadata_keys:
|
||||
i, meta_project_or_domain_id, meta_user_id = key.split('-')
|
||||
i, meta_project_or_domain_id, meta_entity_id = key.split('-')
|
||||
|
||||
if meta_user_id != user_id:
|
||||
# Not the user, so on to next metadata.
|
||||
if meta_entity_id != user_id and meta_entity_id not in group_ids:
|
||||
# Not the user not one of the groups, so on to next metadata.
|
||||
continue
|
||||
|
||||
try:
|
||||
self.get_project(meta_project_or_domain_id)
|
||||
except exception.NotFound:
|
||||
# target is not a project, so on to next metadata.
|
||||
# target is not a project, could it be a domain
|
||||
if not CONF.os_inherit.enabled:
|
||||
# Inheritance is disabled, skip domain handling
|
||||
continue
|
||||
try:
|
||||
self.get_domain(meta_project_or_domain_id)
|
||||
except exception.NotFound:
|
||||
# Not a domain, move on
|
||||
continue
|
||||
|
||||
data = self.db.get(key)
|
||||
for role in data.get('roles', []):
|
||||
if role['inherited_to'] == 'projects':
|
||||
# Role is inherited
|
||||
for project in all_projects:
|
||||
# add all projects for the domain to the list
|
||||
# of ids
|
||||
if (project['domain_id'] ==
|
||||
meta_project_or_domain_id):
|
||||
project_ids.add(project['id'])
|
||||
break
|
||||
continue
|
||||
|
||||
project_id = meta_project_or_domain_id
|
||||
|
|
|
@ -84,6 +84,18 @@ class Assignment(assignment.Driver):
|
|||
tenant['name'] = clean.project_name(tenant['name'])
|
||||
return self._set_default_domain(self.project.update(tenant_id, tenant))
|
||||
|
||||
def get_group_project_roles(self, groups, project_id, project_domain_id):
|
||||
self.get_project(project_id)
|
||||
group_dns = [self.group._id_to_dn(group_id) for group_id in groups]
|
||||
role_list = [self.role._dn_to_id(role_assignment.role_dn)
|
||||
for role_assignment in self.role.get_role_assignments
|
||||
(self.project._id_to_dn(project_id))
|
||||
if role_assignment.user_dn.upper() in group_dns]
|
||||
# NOTE(morganfainberg): Does not support OS-INHERIT as domain
|
||||
# metadata/roles are not supported by LDAP backend. Skip OS-INHERIT
|
||||
# logic.
|
||||
return role_list
|
||||
|
||||
def _get_metadata(self, user_id=None, tenant_id=None,
|
||||
domain_id=None, group_id=None):
|
||||
|
||||
|
|
|
@ -312,6 +312,28 @@ class Assignment(keystone_assignment.Driver):
|
|||
sql_constraints).distinct()
|
||||
return [role.to_dict() for role in query.all()]
|
||||
|
||||
def get_group_project_roles(self, groups, project_id, project_domain_id):
|
||||
sql_constraints = sqlalchemy.and_(
|
||||
RoleAssignment.type == AssignmentType.GROUP_PROJECT,
|
||||
RoleAssignment.target_id == project_id)
|
||||
if CONF.os_inherit.enabled:
|
||||
sql_constraints = sqlalchemy.or_(
|
||||
sql_constraints,
|
||||
sqlalchemy.and_(
|
||||
RoleAssignment.type == AssignmentType.GROUP_DOMAIN,
|
||||
RoleAssignment.inherited,
|
||||
RoleAssignment.target_id == project_domain_id))
|
||||
sql_constraints = sqlalchemy.and_(sql_constraints,
|
||||
RoleAssignment.actor_id.in_(groups))
|
||||
|
||||
# NOTE(morganfainberg): Only select the columns we actually care about
|
||||
# here, in this case role_id.
|
||||
with sql.transaction() as session:
|
||||
query = session.query(RoleAssignment.role_id).filter(
|
||||
sql_constraints).distinct()
|
||||
|
||||
return [result.role_id for result in query.all()]
|
||||
|
||||
def _list_entities_for_groups(self, group_ids, entity):
|
||||
if entity == Domain:
|
||||
assignment_type = AssignmentType.GROUP_DOMAIN
|
||||
|
|
|
@ -151,31 +151,14 @@ class Manager(manager.Manager):
|
|||
|
||||
"""
|
||||
def _get_group_project_roles(user_id, project_ref):
|
||||
role_list = []
|
||||
group_refs = self.identity_api.list_groups_for_user(user_id)
|
||||
for x in group_refs:
|
||||
try:
|
||||
metadata_ref = self._get_metadata(
|
||||
group_id=x['id'], tenant_id=project_ref['id'])
|
||||
role_list += self._roles_from_role_dicts(
|
||||
metadata_ref.get('roles', {}), False)
|
||||
except exception.MetadataNotFound:
|
||||
# no group grant, skip
|
||||
pass
|
||||
|
||||
if CONF.os_inherit.enabled:
|
||||
# Now get any inherited group roles for the owning domain
|
||||
try:
|
||||
metadata_ref = self._get_metadata(
|
||||
group_id=x['id'],
|
||||
domain_id=project_ref['domain_id'])
|
||||
role_list += self._roles_from_role_dicts(
|
||||
metadata_ref.get('roles', {}), True)
|
||||
except (exception.MetadataNotFound,
|
||||
exception.NotImplemented):
|
||||
pass
|
||||
|
||||
return role_list
|
||||
# TODO(morganfainberg): Implement a way to get only group_ids
|
||||
# instead of the more expensive to_dict() call for each record.
|
||||
group_ids = [group['id'] for group in
|
||||
self.identity_api.list_groups_for_user(user_id)]
|
||||
return self.driver.get_group_project_roles(
|
||||
group_ids,
|
||||
project_ref['id'],
|
||||
project_ref['domain_id'])
|
||||
|
||||
def _get_user_project_roles(user_id, project_ref):
|
||||
role_list = []
|
||||
|
@ -967,6 +950,24 @@ class Driver(object):
|
|||
"""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_group_project_roles(self, groups, project_id, project_domain_id):
|
||||
"""Get group roles for a specific project.
|
||||
|
||||
Supports the ``OS-INHERIT`` role inheritance from the project's domain
|
||||
if supported by the assignment driver.
|
||||
|
||||
:param groups: list of group ids
|
||||
:type groups: list
|
||||
:param project_id: project identifier
|
||||
:type project_id: str
|
||||
:param project_domain_id: project's domain identifier
|
||||
:type project_domain_id: str
|
||||
:returns: list of role_refs for the project
|
||||
:rtype: list
|
||||
"""
|
||||
raise exception.NotImplemented()
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_role(self, role_id):
|
||||
"""Get a role by ID.
|
||||
|
|
|
@ -245,3 +245,14 @@ class KvsTokenCacheInvalidation(tests.TestCase,
|
|||
self.config_fixture.config(
|
||||
group='token',
|
||||
driver='keystone.token.backends.kvs.Token')
|
||||
|
||||
|
||||
class KvsInheritanceTests(tests.TestCase, test_backend.InheritanceTests):
|
||||
def setUp(self):
|
||||
# NOTE(dstanek): setup the database for subsystems that only have a
|
||||
# SQL backend (like credentials)
|
||||
self.useFixture(database.Database())
|
||||
|
||||
super(KvsInheritanceTests, self).setUp()
|
||||
self.load_backends()
|
||||
self.load_fixtures(default_fixtures)
|
||||
|
|
Loading…
Reference in New Issue