Merge "Extracting get group roles for project logic to drivers."

This commit is contained in:
Jenkins 2014-07-21 18:02:08 +00:00 committed by Gerrit Code Review
commit 51f19000ff
5 changed files with 127 additions and 34 deletions

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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.

View File

@ -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)