Remove stable driver interfaces

bp removed-as-of-ocata

Change-Id: I4672cf7d9d72ef725212085972dbcd90db0e47cf
This commit is contained in:
Steve Martinelli 2016-09-24 21:04:01 -07:00 committed by Ronald De Rose
parent fb6ff30009
commit 810e15689b
73 changed files with 50 additions and 2917 deletions

View File

@ -1,452 +0,0 @@
# Copyright 2012-13 OpenStack Foundation
#
# 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.
import sqlalchemy
from sqlalchemy.sql.expression import false
from keystone.assignment.backends import base
from keystone.common import sql
import keystone.conf
from keystone import exception
from keystone.i18n import _
CONF = keystone.conf.CONF
class AssignmentType(object):
USER_PROJECT = 'UserProject'
GROUP_PROJECT = 'GroupProject'
USER_DOMAIN = 'UserDomain'
GROUP_DOMAIN = 'GroupDomain'
@classmethod
def calculate_type(cls, user_id, group_id, project_id, domain_id):
if user_id:
if project_id:
return cls.USER_PROJECT
if domain_id:
return cls.USER_DOMAIN
if group_id:
if project_id:
return cls.GROUP_PROJECT
if domain_id:
return cls.GROUP_DOMAIN
# Invalid parameters combination
raise exception.AssignmentTypeCalculationError(**locals())
class Assignment(base.AssignmentDriverV8):
def default_role_driver(self):
return 'sql'
def default_resource_driver(self):
return 'sql'
def list_user_ids_for_project(self, tenant_id):
with sql.session_for_read() as session:
query = session.query(RoleAssignment.actor_id)
query = query.filter_by(type=AssignmentType.USER_PROJECT)
query = query.filter_by(target_id=tenant_id)
query = query.distinct('actor_id')
assignments = query.all()
return [assignment.actor_id for assignment in assignments]
def create_grant(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
assignment_type = AssignmentType.calculate_type(
user_id, group_id, project_id, domain_id)
try:
with sql.session_for_write() as session:
session.add(RoleAssignment(
type=assignment_type,
actor_id=user_id or group_id,
target_id=project_id or domain_id,
role_id=role_id,
inherited=inherited_to_projects))
except sql.DBDuplicateEntry: # nosec : The v3 grant APIs are silent if
# the assignment already exists
pass
def list_grant_role_ids(self, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
with sql.session_for_read() as session:
q = session.query(RoleAssignment.role_id)
q = q.filter(RoleAssignment.actor_id == (user_id or group_id))
q = q.filter(RoleAssignment.target_id == (project_id or domain_id))
q = q.filter(RoleAssignment.inherited == inherited_to_projects)
return [x.role_id for x in q.all()]
def _build_grant_filter(self, session, role_id, user_id, group_id,
domain_id, project_id, inherited_to_projects):
q = session.query(RoleAssignment)
q = q.filter_by(actor_id=user_id or group_id)
q = q.filter_by(target_id=project_id or domain_id)
q = q.filter_by(role_id=role_id)
q = q.filter_by(inherited=inherited_to_projects)
return q
def check_grant_role_id(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
with sql.session_for_read() as session:
try:
q = self._build_grant_filter(
session, role_id, user_id, group_id, domain_id, project_id,
inherited_to_projects)
q.one()
except sql.NotFound:
actor_id = user_id or group_id
target_id = domain_id or project_id
raise exception.RoleAssignmentNotFound(role_id=role_id,
actor_id=actor_id,
target_id=target_id)
def delete_grant(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
with sql.session_for_write() as session:
q = self._build_grant_filter(
session, role_id, user_id, group_id, domain_id, project_id,
inherited_to_projects)
if not q.delete(False):
actor_id = user_id or group_id
target_id = domain_id or project_id
raise exception.RoleAssignmentNotFound(role_id=role_id,
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.session_for_read() as session:
query = session.query(RoleAssignment.target_id).filter(
sql_constraints).distinct()
return [x.target_id for x in query.all()]
def list_project_ids_for_user(self, user_id, group_ids, hints,
inherited=False):
actor_list = [user_id]
if group_ids:
actor_list = actor_list + group_ids
return self._list_project_ids_for_actor(actor_list, hints, inherited)
def list_domain_ids_for_user(self, user_id, group_ids, hints,
inherited=False):
with sql.session_for_read() as session:
query = session.query(RoleAssignment.target_id)
filters = []
if user_id:
sql_constraints = sqlalchemy.and_(
RoleAssignment.actor_id == user_id,
RoleAssignment.inherited == inherited,
RoleAssignment.type == AssignmentType.USER_DOMAIN)
filters.append(sql_constraints)
if group_ids:
sql_constraints = sqlalchemy.and_(
RoleAssignment.actor_id.in_(group_ids),
RoleAssignment.inherited == inherited,
RoleAssignment.type == AssignmentType.GROUP_DOMAIN)
filters.append(sql_constraints)
if not filters:
return []
query = query.filter(sqlalchemy.or_(*filters)).distinct()
return [assignment.target_id for assignment 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.session_for_read() 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.session_for_read() 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.session_for_read() 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.session_for_write() as session:
session.add(RoleAssignment(
type=AssignmentType.USER_PROJECT,
actor_id=user_id, target_id=tenant_id,
role_id=role_id, inherited=False))
except sql.DBDuplicateEntry:
msg = ('User %s already has role %s in tenant %s'
% (user_id, role_id, tenant_id))
raise exception.Conflict(type='role grant', details=msg)
def remove_role_from_user_and_project(self, user_id, tenant_id, role_id):
with sql.session_for_write() as session:
q = session.query(RoleAssignment)
q = q.filter_by(actor_id=user_id)
q = q.filter_by(target_id=tenant_id)
q = q.filter_by(role_id=role_id)
if q.delete() == 0:
raise exception.RoleNotFound(message=_(
'Cannot remove role that has not been granted, %s') %
role_id)
def _get_user_assignment_types(self):
return [AssignmentType.USER_PROJECT, AssignmentType.USER_DOMAIN]
def _get_group_assignment_types(self):
return [AssignmentType.GROUP_PROJECT, AssignmentType.GROUP_DOMAIN]
def _get_project_assignment_types(self):
return [AssignmentType.USER_PROJECT, AssignmentType.GROUP_PROJECT]
def _get_domain_assignment_types(self):
return [AssignmentType.USER_DOMAIN, AssignmentType.GROUP_DOMAIN]
def _get_assignment_types(self, user, group, project, domain):
"""Return a list of role assignment types based on provided entities.
If one of user or group (the "actor") as well as one of project or
domain (the "target") are provided, the list will contain the role
assignment type for that specific pair of actor and target.
If only an actor or target is provided, the list will contain the
role assignment types that satisfy the specified entity.
For example, if user and project are provided, the return will be:
[AssignmentType.USER_PROJECT]
However, if only user was provided, the return would be:
[AssignmentType.USER_PROJECT, AssignmentType.USER_DOMAIN]
It is not expected that user and group (or project and domain) are
specified - but if they are, the most fine-grained value will be
chosen (i.e. user over group, project over domain).
"""
actor_types = []
if user:
actor_types = self._get_user_assignment_types()
elif group:
actor_types = self._get_group_assignment_types()
target_types = []
if project:
target_types = self._get_project_assignment_types()
elif domain:
target_types = self._get_domain_assignment_types()
if actor_types and target_types:
return list(set(actor_types).intersection(target_types))
return actor_types or target_types
def list_role_assignments(self, role_id=None,
user_id=None, group_ids=None,
domain_id=None, project_ids=None,
inherited_to_projects=None):
def denormalize_role(ref):
assignment = {}
if ref.type == AssignmentType.USER_PROJECT:
assignment['user_id'] = ref.actor_id
assignment['project_id'] = ref.target_id
elif ref.type == AssignmentType.USER_DOMAIN:
assignment['user_id'] = ref.actor_id
assignment['domain_id'] = ref.target_id
elif ref.type == AssignmentType.GROUP_PROJECT:
assignment['group_id'] = ref.actor_id
assignment['project_id'] = ref.target_id
elif ref.type == AssignmentType.GROUP_DOMAIN:
assignment['group_id'] = ref.actor_id
assignment['domain_id'] = ref.target_id
else:
raise exception.Error(message=_(
'Unexpected assignment type encountered, %s') %
ref.type)
assignment['role_id'] = ref.role_id
if ref.inherited:
assignment['inherited_to_projects'] = 'projects'
return assignment
with sql.session_for_read() as session:
assignment_types = self._get_assignment_types(
user_id, group_ids, project_ids, domain_id)
targets = None
if project_ids:
targets = project_ids
elif domain_id:
targets = [domain_id]
actors = None
if group_ids:
actors = group_ids
elif user_id:
actors = [user_id]
query = session.query(RoleAssignment)
if role_id:
query = query.filter_by(role_id=role_id)
if actors:
query = query.filter(RoleAssignment.actor_id.in_(actors))
if targets:
query = query.filter(RoleAssignment.target_id.in_(targets))
if assignment_types:
query = query.filter(RoleAssignment.type.in_(assignment_types))
if inherited_to_projects is not None:
query = query.filter_by(inherited=inherited_to_projects)
return [denormalize_role(ref) for ref in query.all()]
def delete_project_assignments(self, project_id):
with sql.session_for_write() as session:
q = session.query(RoleAssignment)
q = q.filter_by(target_id=project_id)
q.delete(False)
def delete_role_assignments(self, role_id):
with sql.session_for_write() as session:
q = session.query(RoleAssignment)
q = q.filter_by(role_id=role_id)
q.delete(False)
def delete_user_assignments(self, user_id):
with sql.session_for_write() as session:
q = session.query(RoleAssignment)
q = q.filter_by(actor_id=user_id)
q.delete(False)
def delete_group_assignments(self, group_id):
with sql.session_for_write() as session:
q = session.query(RoleAssignment)
q = q.filter_by(actor_id=group_id)
q.delete(False)
class RoleAssignment(sql.ModelBase, sql.DictBase):
__tablename__ = 'assignment'
attributes = ['type', 'actor_id', 'target_id', 'role_id', 'inherited']
# NOTE(henry-nash); Postgres requires a name to be defined for an Enum
type = sql.Column(
sql.Enum(AssignmentType.USER_PROJECT, AssignmentType.GROUP_PROJECT,
AssignmentType.USER_DOMAIN, AssignmentType.GROUP_DOMAIN,
name='type'),
nullable=False)
actor_id = sql.Column(sql.String(64), nullable=False)
target_id = sql.Column(sql.String(64), nullable=False)
role_id = sql.Column(sql.String(64), nullable=False)
inherited = sql.Column(sql.Boolean, default=False, nullable=False)
__table_args__ = (
sql.PrimaryKeyConstraint('type', 'actor_id', 'target_id', 'role_id',
'inherited'),
sql.Index('ix_actor_id', 'actor_id'),
)
def to_dict(self):
"""Override parent method with a simpler implementation.
RoleAssignment doesn't have non-indexed 'extra' attributes, so the
parent implementation is not applicable.
"""
return dict(self.items())

View File

@ -1,80 +0,0 @@
# 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 keystone.assignment.role_backends import base
from keystone.common import sql
from keystone import exception
class Role(base.RoleDriverV8):
@sql.handle_conflicts(conflict_type='role')
def create_role(self, role_id, role):
with sql.session_for_write() as session:
ref = RoleTable.from_dict(role)
session.add(ref)
return ref.to_dict()
@sql.truncated
def list_roles(self, hints):
with sql.session_for_read() as session:
query = session.query(RoleTable)
refs = sql.filter_limit_query(RoleTable, query, hints)
return [ref.to_dict() for ref in refs]
def list_roles_from_ids(self, ids):
if not ids:
return []
else:
with sql.session_for_read() as session:
query = session.query(RoleTable)
query = query.filter(RoleTable.id.in_(ids))
role_refs = query.all()
return [role_ref.to_dict() for role_ref in role_refs]
def _get_role(self, session, role_id):
ref = session.query(RoleTable).get(role_id)
if ref is None:
raise exception.RoleNotFound(role_id=role_id)
return ref
def get_role(self, role_id):
with sql.session_for_read() as session:
return self._get_role(session, role_id).to_dict()
@sql.handle_conflicts(conflict_type='role')
def update_role(self, role_id, role):
with sql.session_for_write() as session:
ref = self._get_role(session, role_id)
old_dict = ref.to_dict()
for k in role:
old_dict[k] = role[k]
new_role = RoleTable.from_dict(old_dict)
for attr in RoleTable.attributes:
if attr != 'id':
setattr(ref, attr, getattr(new_role, attr))
ref.extra = new_role.extra
return ref.to_dict()
def delete_role(self, role_id):
with sql.session_for_write() as session:
ref = self._get_role(session, role_id)
session.delete(ref)
class RoleTable(sql.ModelBase, sql.DictBase):
__tablename__ = 'role'
attributes = ['id', 'name']
id = sql.Column(sql.String(64), primary_key=True)
name = sql.Column(sql.String(255), unique=True, nullable=False)
extra = sql.Column(sql.JsonBlob())
__table_args__ = (sql.UniqueConstraint('name'),)

View File

@ -15,25 +15,16 @@
import abc
from oslo_log import log
from oslo_log import versionutils
import six
import keystone.conf
from keystone import exception
from keystone.i18n import _LW
CONF = keystone.conf.CONF
LOG = log.getLogger(__name__)
# The AssignmentDriverBase class is the set of driver methods from earlier
# drivers that we still support, that have not been removed or modified. This
# class is then used to created the augmented V8 and V9 version abstract driver
# classes, without having to duplicate a lot of abstract method signatures.
# If you remove a method from V9, then move the abstract methods from this Base
# class to the V8 class. Do not modify any of the method signatures in the Base
# class - changes should only be made in the V8 and subsequent classes.
@six.add_metaclass(abc.ABCMeta)
class AssignmentDriverBase(object):
@ -152,249 +143,7 @@ class AssignmentDriverBase(object):
"""
raise exception.NotImplemented() # pragma: no cover
class AssignmentDriverV8(AssignmentDriverBase):
"""Removed or redefined methods from V8.
Move the abstract methods of any methods removed or modified in later
versions of the driver from AssignmentDriverBase to here. We maintain this
so that legacy drivers, which will be a subclass of AssignmentDriverV8, can
still reference them.
"""
@abc.abstractmethod
def list_user_ids_for_project(self, tenant_id):
"""List all user IDs with a role assignment in the specified project.
:returns: a list of user_ids or an empty set.
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def list_project_ids_for_user(self, user_id, group_ids, hints,
inherited=False):
"""List all project ids associated with a given user.
:param user_id: the user in question
:param group_ids: the groups this user is a member of. This list is
built in the Manager, so that the driver itself
does not have to call across to identity.
:param hints: filter hints which the driver should
implement if at all possible.
:param inherited: whether assignments marked as inherited should
be included.
:returns: a list of project ids or an empty list.
This method should not try and expand any inherited assignments,
just report the projects that have the role for this user. The manager
method is responsible for expanding out inherited assignments.
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def list_domain_ids_for_user(self, user_id, group_ids, hints,
inherited=False):
"""List all domain ids associated with a given user.
:param user_id: the user in question
:param group_ids: the groups this user is a member of. This list is
built in the Manager, so that the driver itself
does not have to call across to identity.
:param hints: filter hints which the driver should
implement if at all possible.
:param inherited: whether to return domain_ids that have inherited
assignments or not.
:returns: a list of domain ids or an empty list.
"""
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.
Add any new V9 abstract methods (or those with modified signatures) to
this class.
"""
@abc.abstractmethod
def delete_domain_assignments(self, domain_id):
"""Delete all assignments for a domain."""
raise exception.NotImplemented()
class V9AssignmentWrapperForV8Driver(AssignmentDriverV9):
"""Wrapper class to supported a V8 legacy driver.
In order to support legacy drivers without having to make the manager code
driver-version aware, we wrap legacy drivers so that they look like the
latest version. For the various changes made in a new driver, here are the
actions needed in this wrapper:
Method removed from new driver - remove the call-through method from this
class, since the manager will no longer be
calling it.
Method signature (or meaning) changed - wrap the old method in a new
signature here, and munge the input
and output parameters accordingly.
New method added to new driver - add a method to implement the new
functionality here if possible. If that is
not possible, then return NotImplemented,
since we do not guarantee to support new
functionality with legacy drivers.
"""
@versionutils.deprecated(
as_of=versionutils.deprecated.MITAKA,
what='keystone.assignment.AssignmentDriverV8',
in_favor_of='keystone.assignment.AssignmentDriverV9',
remove_in=+2)
def __init__(self, wrapped_driver):
self.driver = wrapped_driver
def delete_domain_assignments(self, domain_id):
"""Delete all assignments for a domain."""
msg = _LW('delete_domain_assignments method not found in custom '
'assignment driver. Domain assignments for domain (%s) to '
'users from other domains will not be removed. This was '
'added in V9 of the assignment driver.')
LOG.warning(msg, domain_id)
def default_role_driver(self):
return self.driver.default_role_driver()
def default_resource_driver(self):
return self.driver.default_resource_driver()
def add_role_to_user_and_project(self, user_id, tenant_id, role_id):
self.driver.add_role_to_user_and_project(user_id, tenant_id, role_id)
def remove_role_from_user_and_project(self, user_id, tenant_id, role_id):
self.driver.remove_role_from_user_and_project(
user_id, tenant_id, role_id)
def create_grant(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
self.driver.create_grant(
role_id, user_id=user_id, group_id=group_id,
domain_id=domain_id, project_id=project_id,
inherited_to_projects=inherited_to_projects)
def list_grant_role_ids(self, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
return self.driver.list_grant_role_ids(
user_id=user_id, group_id=group_id,
domain_id=domain_id, project_id=project_id,
inherited_to_projects=inherited_to_projects)
def check_grant_role_id(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
self.driver.check_grant_role_id(
role_id, user_id=user_id, group_id=group_id,
domain_id=domain_id, project_id=project_id,
inherited_to_projects=inherited_to_projects)
def delete_grant(self, role_id, user_id=None, group_id=None,
domain_id=None, project_id=None,
inherited_to_projects=False):
self.driver.delete_grant(
role_id, user_id=user_id, group_id=group_id,
domain_id=domain_id, project_id=project_id,
inherited_to_projects=inherited_to_projects)
def list_role_assignments(self, role_id=None,
user_id=None, group_ids=None,
domain_id=None, project_ids=None,
inherited_to_projects=None):
return self.driver.list_role_assignments(
role_id=role_id,
user_id=user_id, group_ids=group_ids,
domain_id=domain_id, project_ids=project_ids,
inherited_to_projects=inherited_to_projects)
def delete_project_assignments(self, project_id):
self.driver.delete_project_assignments(project_id)
def delete_role_assignments(self, role_id):
self.driver.delete_role_assignments(role_id)
def delete_user_assignments(self, user_id):
self.driver.delete_user_assignments(user_id)
def delete_group_assignments(self, group_id):
self.driver.delete_group_assignments(group_id)

View File

@ -40,7 +40,7 @@ class AssignmentType(object):
raise exception.AssignmentTypeCalculationError(**locals())
class Assignment(base.AssignmentDriverV9):
class Assignment(base.AssignmentDriverBase):
def default_role_driver(self):
return 'sql'

View File

@ -20,8 +20,6 @@ import functools
from oslo_log import log
from oslo_log import versionutils
from keystone.assignment.backends import base
from keystone.assignment.role_backends import base as role_base
from keystone.common import cache
from keystone.common import dependency
from keystone.common import driver_hints
@ -92,13 +90,6 @@ class Manager(manager.Manager):
raise exception.KeystoneConfigurationError(msg)
super(Manager, self).__init__(assignment_driver)
# Make sure it is a driver version we support, and if it is a legacy
# driver, then wrap it.
if isinstance(self.driver, base.AssignmentDriverV8):
self.driver = base.V9AssignmentWrapperForV8Driver(self.driver)
elif not isinstance(self.driver, base.AssignmentDriverV9):
raise exception.UnsupportedDriverVersion(driver=assignment_driver)
self.event_callbacks = {
notifications.ACTIONS.deleted: {
'domain': [self._delete_domain_assignments],
@ -1099,46 +1090,6 @@ class Manager(manager.Manager):
)
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.assignment.AssignmentDriverBase',
in_favor_of='keystone.assignment.backends.base.AssignmentDriverBase',
remove_in=+1)
class AssignmentDriverBase(base.AssignmentDriverBase):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.assignment.AssignmentDriverV8',
in_favor_of='keystone.assignment.backends.base.AssignmentDriverV8',
remove_in=+1)
class AssignmentDriverV8(base.AssignmentDriverV8):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.assignment.AssignmentDriverV9',
in_favor_of='keystone.assignment.backends.base.AssignmentDriverV9',
remove_in=+1)
class AssignmentDriverV9(base.AssignmentDriverV9):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.assignment.V9AssignmentWrapperForV8Driver',
in_favor_of=(
'keystone.assignment.backends.base.V9AssignmentWrapperForV8Driver'),
remove_in=+1)
class V9AssignmentWrapperForV8Driver(base.V9AssignmentWrapperForV8Driver):
pass
Driver = manager.create_legacy_driver(base.AssignmentDriverV8)
@dependency.provider('role_api')
@dependency.requires('assignment_api')
class RoleManager(manager.Manager):
@ -1159,13 +1110,6 @@ class RoleManager(manager.Manager):
super(RoleManager, self).__init__(role_driver)
# Make sure it is a driver version we support, and if it is a legacy
# driver, then wrap it.
if isinstance(self.driver, role_base.RoleDriverV8):
self.driver = role_base.V9RoleWrapperForV8Driver(self.driver)
elif not isinstance(self.driver, role_base.RoleDriverV9):
raise exception.UnsupportedDriverVersion(driver=role_driver)
def _append_null_domain_id(f):
"""Append a domain_id field to a role dict if it is not already there.
@ -1244,43 +1188,3 @@ class RoleManager(manager.Manager):
def delete_implied_role(self, prior_role_id, implied_role_id):
self.driver.delete_implied_role(prior_role_id, implied_role_id)
COMPUTED_ASSIGNMENTS_REGION.invalidate()
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.assignment.RoleDriverBase',
in_favor_of='keystone.assignment.role_backends.base.RoleDriverBase',
remove_in=+1)
class RoleDriverBase(role_base.RoleDriverBase):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.assignment.RoleDriverV8',
in_favor_of='keystone.assignment.role_backends.base.RoleDriverV8',
remove_in=+1)
class RoleDriverV8(role_base.RoleDriverV8):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.assignment.RoleDriverV9',
in_favor_of='keystone.assignment.role_backends.base.RoleDriverV9',
remove_in=+1)
class RoleDriverV9(role_base.RoleDriverV9):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.assignment.V9RoleWrapperForV8Driver',
in_favor_of=(
'keystone.assignment.role_backends.base.V9RoleWrapperForV8Driver'),
remove_in=+1)
class V9RoleWrapperForV8Driver(role_base.V9RoleWrapperForV8Driver):
pass
RoleDriver = manager.create_legacy_driver(role_base.RoleDriverV8)

View File

@ -14,24 +14,15 @@
import abc
from oslo_log import versionutils
import six
import keystone.conf
from keystone import exception
from keystone.i18n import _
CONF = keystone.conf.CONF
# The RoleDriverBase class is the set of driver methods from earlier
# drivers that we still support, that have not been removed or modified. This
# class is then used to created the augmented V8 and V9 version abstract driver
# classes, without having to duplicate a lot of abstract method signatures.
# If you remove a method from V9, then move the abstract methods from this Base
# class to the V8 class. Do not modify any of the method signatures in the Base
# class - changes should only be made in the V8 and subsequent classes.
@six.add_metaclass(abc.ABCMeta)
class RoleDriverBase(object):
@ -102,28 +93,6 @@ class RoleDriverBase(object):
"""
raise exception.NotImplemented() # pragma: no cover
class RoleDriverV8(RoleDriverBase):
"""Removed or redefined methods from V8.
Move the abstract methods of any methods removed or modified in later
versions of the driver from RoleDriverBase to here. We maintain this
so that legacy drivers, which will be a subclass of RoleDriverV8, can
still reference them.
"""
pass
class RoleDriverV9(RoleDriverBase):
"""New or redefined methods from V8.
Add any new V9 abstract methods (or those with modified signatures) to
this class.
"""
@abc.abstractmethod
def get_implied_role(self, prior_role_id, implied_role_id):
"""Get a role inference rule.
@ -162,106 +131,3 @@ class RoleDriverV9(RoleDriverBase):
def list_implied_roles(self, prior_role_id):
"""List roles implied from the prior role ID."""
raise exception.NotImplemented() # pragma: no cover
class V9RoleWrapperForV8Driver(RoleDriverV9):
"""Wrapper class to supported a V8 legacy driver.
In order to support legacy drivers without having to make the manager code
driver-version aware, we wrap legacy drivers so that they look like the
latest version. For the various changes made in a new driver, here are the
actions needed in this wrapper:
Method removed from new driver - remove the call-through method from this
class, since the manager will no longer be
calling it.
Method signature (or meaning) changed - wrap the old method in a new
signature here, and munge the input
and output parameters accordingly.
New method added to new driver - add a method to implement the new
functionality here if possible. If that is
not possible, then return NotImplemented,
since we do not guarantee to support new
functionality with legacy drivers.
This V8 wrapper contains the following support for newer manager code:
- The current manager code expects a role entity to have a domain_id
attribute, with a non-None value indicating a domain specific role. V8
drivers will only understand global roles, hence if a non-None domain_id
is passed to this wrapper, it will raise a NotImplemented exception.
If a None-valued domain_id is passed in, it will be trimmed off before
the underlying driver is called (and a None-valued domain_id attribute
is added in for any entities returned to the manager.
"""
@versionutils.deprecated(
as_of=versionutils.deprecated.MITAKA,
what='keystone.assignment.RoleDriverV8',
in_favor_of='keystone.assignment.RoleDriverV9',
remove_in=+2)
def __init__(self, wrapped_driver):
self.driver = wrapped_driver
def _append_null_domain_id(self, role_or_list):
def _append_null_domain_id_to_dict(role):
if 'domain_id' not in role:
role['domain_id'] = None
return role
if isinstance(role_or_list, list):
return [_append_null_domain_id_to_dict(x) for x in role_or_list]
else:
return _append_null_domain_id_to_dict(role_or_list)
def _trim_and_assert_null_domain_id(self, role):
if 'domain_id' in role:
if role['domain_id'] is not None:
raise exception.NotImplemented(
_('Domain specific roles are not supported in the V8 '
'role driver'))
else:
new_role = role.copy()
new_role.pop('domain_id')
return new_role
else:
return role
def create_role(self, role_id, role):
new_role = self._trim_and_assert_null_domain_id(role)
return self._append_null_domain_id(
self.driver.create_role(role_id, new_role))
def list_roles(self, hints):
return self._append_null_domain_id(self.driver.list_roles(hints))
def list_roles_from_ids(self, role_ids):
return self._append_null_domain_id(
self.driver.list_roles_from_ids(role_ids))
def get_role(self, role_id):
return self._append_null_domain_id(self.driver.get_role(role_id))
def update_role(self, role_id, role):
update_role = self._trim_and_assert_null_domain_id(role)
return self._append_null_domain_id(
self.driver.update_role(role_id, update_role))
def delete_role(self, role_id):
self.driver.delete_role(role_id)
def get_implied_role(self, prior_role_id, implied_role_id):
raise exception.NotImplemented() # pragma: no cover
def create_implied_role(self, prior_role_id, implied_role_id):
raise exception.NotImplemented() # pragma: no cover
def delete_implied_role(self, prior_role_id, implied_role_id):
raise exception.NotImplemented() # pragma: no cover
def list_implied_roles(self, prior_role_id):
raise exception.NotImplemented() # pragma: no cover
def list_role_inference_rules(self):
raise exception.NotImplemented() # pragma: no cover

View File

@ -25,7 +25,7 @@ from keystone import exception
NULL_DOMAIN_ID = '<<null>>'
class Role(base.RoleDriverV9):
class Role(base.RoleDriverBase):
@sql.handle_conflicts(conflict_type='role')
def create_role(self, role_id, role):

View File

@ -1,26 +0,0 @@
# Copyright 2013 OpenStack Foundation
#
# 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 oslo_log import versionutils
from keystone.auth.plugins import base
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.auth.AuthMethodHandler',
in_favor_of='keystone.auth.plugins.base.AuthMethodHandler',
remove_in=+1)
class AuthMethodHandler(base.AuthMethodHandler):
pass

View File

@ -24,7 +24,7 @@ CONF = keystone.conf.CONF
@six.add_metaclass(abc.ABCMeta)
class CatalogDriverV8(object):
class CatalogDriverBase(object):
"""Interface description for the Catalog driver."""
def _get_list_limit(self):

View File

@ -81,7 +81,7 @@ class Endpoint(sql.ModelBase, sql.DictBase):
extra = sql.Column(sql.JsonBlob())
class Catalog(base.CatalogDriverV8):
class Catalog(base.CatalogDriverBase):
# Regions
def list_regions(self, hints):
with sql.session_for_read() as session:

View File

@ -56,7 +56,7 @@ def parse_templates(template_lines):
return o
class Catalog(base.CatalogDriverV8):
class Catalog(base.CatalogDriverBase):
"""A backend that generates endpoints for the Catalog based on templates.
It is usually configured via config entries that look like:

View File

@ -15,9 +15,6 @@
"""Main entry point into the Catalog service."""
from oslo_log import versionutils
from keystone.catalog.backends import base
from keystone.common import cache
from keystone.common import dependency
from keystone.common import driver_hints
@ -329,15 +326,3 @@ class Manager(manager.Manager):
except exception.NotImplemented:
# Some catalog drivers don't support this
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.catalog.CatalogDriverV8',
in_favor_of='keystone.catalog.backends.base.CatalogDriverV8',
remove_in=+1)
class CatalogDriverV8(base.CatalogDriverV8):
pass
Driver = manager.create_legacy_driver(base.CatalogDriverV8)

View File

@ -20,7 +20,6 @@ import types
from oslo_log import log
from oslo_log import versionutils
from oslo_utils import importutils
from oslo_utils import reflection
import six
import stevedore
@ -190,34 +189,3 @@ class Manager(object):
# cache this
setattr(self, name, f)
return f
def create_legacy_driver(driver_class):
"""Helper function to deprecate the original driver classes.
The keystone.{subsystem}.Driver classes are deprecated in favor of the
new versioned classes. This function creates a new class based on a
versioned class and adds a deprecation message when it is used.
This will allow existing custom drivers to work when the Driver class is
renamed to include a version.
Example usage:
Driver = create_legacy_driver(CatalogDriverV8)
"""
module_name = driver_class.__module__
class_name = reflection.get_class_name(driver_class)
class Driver(driver_class):
@versionutils.deprecated(
as_of=versionutils.deprecated.LIBERTY,
what='%s.Driver' % module_name,
in_favor_of=class_name,
remove_in=+2)
def __init__(self, *args, **kwargs):
super(Driver, self).__init__(*args, **kwargs)
return Driver

View File

@ -24,7 +24,7 @@ LOG = log.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class CredentialDriverV8(object):
class CredentialDriverBase(object):
# credential crud
@abc.abstractmethod

View File

@ -33,7 +33,7 @@ class CredentialModel(sql.ModelBase, sql.DictBase):
extra = sql.Column(sql.JsonBlob())
class Credential(base.CredentialDriverV8):
class Credential(base.CredentialDriverBase):
# credential crud

View File

@ -16,13 +16,10 @@
import json
from oslo_log import versionutils
from keystone.common import dependency
from keystone.common import driver_hints
from keystone.common import manager
import keystone.conf
from keystone.credential.backends import base
from keystone import exception
@ -142,15 +139,3 @@ class Manager(manager.Manager):
else:
ref['blob'] = existing_blob
return ref
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.credential.CredentialDriverV8',
in_favor_of='keystone.credential.backends.base.CredentialDriverV8',
remove_in=+1)
class AuthMethodHandler(base.CredentialDriverV8):
pass
Driver = manager.create_legacy_driver(base.CredentialDriverV8)

View File

@ -17,7 +17,7 @@ from keystone import exception
@six.add_metaclass(abc.ABCMeta)
class EndpointPolicyDriverV8(object):
class EndpointPolicyDriverBase(object):
"""Interface description for an Endpoint Policy driver."""
@abc.abstractmethod

View File

@ -48,7 +48,7 @@ class PolicyAssociation(sql.ModelBase, sql.ModelDictMixin):
return d
class EndpointPolicy(base.EndpointPolicyDriverV8):
class EndpointPolicy(base.EndpointPolicyDriverBase):
def create_policy_association(self, policy_id, endpoint_id=None,
service_id=None, region_id=None):

View File

@ -13,12 +13,10 @@
# under the License.
from oslo_log import log
from oslo_log import versionutils
from keystone.common import dependency
from keystone.common import manager
import keystone.conf
from keystone.endpoint_policy.backends import base
from keystone import exception
from keystone.i18n import _, _LE, _LW
@ -263,16 +261,3 @@ class Manager(manager.Manager):
msg = _('No policy is associated with endpoint '
'%(endpoint_id)s.') % {'endpoint_id': endpoint_id}
raise exception.NotFound(msg)
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.endpoint_policy.EndpointPolicyDriverV8',
in_favor_of=(
'keystone.endpoint_policy.backends.base.EndpointPolicyDriverV8'),
remove_in=+1)
class EndpointPolicyDriverV8(base.EndpointPolicyDriverV8):
pass
Driver = manager.create_legacy_driver(base.EndpointPolicyDriverV8)

View File

@ -1,389 +0,0 @@
# Copyright 2014 OpenStack Foundation
#
# 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 oslo_log import log
from oslo_serialization import jsonutils
import six
from sqlalchemy import orm
from keystone.common import sql
from keystone import exception
from keystone.federation import core
from keystone.i18n import _
LOG = log.getLogger(__name__)
class FederationProtocolModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'federation_protocol'
attributes = ['id', 'idp_id', 'mapping_id']
mutable_attributes = frozenset(['mapping_id'])
id = sql.Column(sql.String(64), primary_key=True)
idp_id = sql.Column(sql.String(64), sql.ForeignKey('identity_provider.id',
ondelete='CASCADE'), primary_key=True)
mapping_id = sql.Column(sql.String(64), nullable=False)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
return d
class IdentityProviderModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'identity_provider'
attributes = ['id', 'enabled', 'description', 'remote_ids']
mutable_attributes = frozenset(['description', 'enabled', 'remote_ids'])
id = sql.Column(sql.String(64), primary_key=True)
enabled = sql.Column(sql.Boolean, nullable=False)
description = sql.Column(sql.Text(), nullable=True)
remote_ids = orm.relationship('IdPRemoteIdsModel',
order_by='IdPRemoteIdsModel.remote_id',
cascade='all, delete-orphan')
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
remote_ids_list = new_dictionary.pop('remote_ids', None)
if not remote_ids_list:
remote_ids_list = []
identity_provider = cls(**new_dictionary)
remote_ids = []
# NOTE(fmarco76): the remote_ids_list contains only remote ids
# associated with the IdP because of the "relationship" established in
# sqlalchemy and corresponding to the FK in the idp_remote_ids table
for remote in remote_ids_list:
remote_ids.append(IdPRemoteIdsModel(remote_id=remote))
identity_provider.remote_ids = remote_ids
return identity_provider
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
d['remote_ids'] = []
for remote in self.remote_ids:
d['remote_ids'].append(remote.remote_id)
return d
class IdPRemoteIdsModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'idp_remote_ids'
attributes = ['idp_id', 'remote_id']
mutable_attributes = frozenset(['idp_id', 'remote_id'])
idp_id = sql.Column(sql.String(64),
sql.ForeignKey('identity_provider.id',
ondelete='CASCADE'))
remote_id = sql.Column(sql.String(255),
primary_key=True)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
return d
class MappingModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'mapping'
attributes = ['id', 'rules']
id = sql.Column(sql.String(64), primary_key=True)
rules = sql.Column(sql.JsonBlob(), nullable=False)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
new_dictionary['rules'] = jsonutils.dumps(new_dictionary['rules'])
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
d['rules'] = jsonutils.loads(d['rules'])
return d
class ServiceProviderModel(sql.ModelBase, sql.DictBase):
__tablename__ = 'service_provider'
attributes = ['auth_url', 'id', 'enabled', 'description',
'relay_state_prefix', 'sp_url']
mutable_attributes = frozenset(['auth_url', 'description', 'enabled',
'relay_state_prefix', 'sp_url'])
id = sql.Column(sql.String(64), primary_key=True)
enabled = sql.Column(sql.Boolean, nullable=False)
description = sql.Column(sql.Text(), nullable=True)
auth_url = sql.Column(sql.String(256), nullable=False)
sp_url = sql.Column(sql.String(256), nullable=False)
relay_state_prefix = sql.Column(sql.String(256), nullable=False)
@classmethod
def from_dict(cls, dictionary):
new_dictionary = dictionary.copy()
return cls(**new_dictionary)
def to_dict(self):
"""Return a dictionary with model's attributes."""
d = dict()
for attr in self.__class__.attributes:
d[attr] = getattr(self, attr)
return d
class Federation(core.FederationDriverV8):
_CONFLICT_LOG_MSG = 'Conflict %(conflict_type)s: %(details)s'
def _handle_idp_conflict(self, e):
conflict_type = 'identity_provider'
details = six.text_type(e)
LOG.debug(self._CONFLICT_LOG_MSG, {'conflict_type': conflict_type,
'details': details})
if 'remote_id' in details:
msg = _('Duplicate remote ID: %s')
else:
msg = _('Duplicate entry: %s')
msg = msg % e.value
raise exception.Conflict(type=conflict_type, details=msg)
# Identity Provider CRUD
@sql.handle_conflicts(conflict_type='identity_provider')
def create_idp(self, idp_id, idp):
idp['id'] = idp_id
with sql.session_for_write() as session:
idp_ref = IdentityProviderModel.from_dict(idp)
session.add(idp_ref)
return idp_ref.to_dict()
def delete_idp(self, idp_id):
with sql.session_for_write() as session:
self._delete_assigned_protocols(session, idp_id)
idp_ref = self._get_idp(session, idp_id)
session.delete(idp_ref)
def _get_idp(self, session, idp_id):
idp_ref = session.query(IdentityProviderModel).get(idp_id)
if not idp_ref:
raise exception.IdentityProviderNotFound(idp_id=idp_id)
return idp_ref
def _get_idp_from_remote_id(self, session, remote_id):
q = session.query(IdPRemoteIdsModel)
q = q.filter_by(remote_id=remote_id)
try:
return q.one()
except sql.NotFound:
raise exception.IdentityProviderNotFound(idp_id=remote_id)
def list_idps(self):
with sql.session_for_read() as session:
idps = session.query(IdentityProviderModel)
idps_list = [idp.to_dict() for idp in idps]
return idps_list
def get_idp(self, idp_id):
with sql.session_for_read() as session:
idp_ref = self._get_idp(session, idp_id)
return idp_ref.to_dict()
def get_idp_from_remote_id(self, remote_id):
with sql.session_for_read() as session:
ref = self._get_idp_from_remote_id(session, remote_id)
return ref.to_dict()
def update_idp(self, idp_id, idp):
try:
with sql.session_for_write() as session:
idp_ref = self._get_idp(session, idp_id)
old_idp = idp_ref.to_dict()
old_idp.update(idp)
new_idp = IdentityProviderModel.from_dict(old_idp)
for attr in IdentityProviderModel.mutable_attributes:
setattr(idp_ref, attr, getattr(new_idp, attr))
return idp_ref.to_dict()
except sql.DBDuplicateEntry as e:
self._handle_idp_conflict(e)
# Protocol CRUD
def _get_protocol(self, session, idp_id, protocol_id):
q = session.query(FederationProtocolModel)
q = q.filter_by(id=protocol_id, idp_id=idp_id)
try:
return q.one()
except sql.NotFound:
kwargs = {'protocol_id': protocol_id,
'idp_id': idp_id}
raise exception.FederatedProtocolNotFound(**kwargs)
@sql.handle_conflicts(conflict_type='federation_protocol')
def create_protocol(self, idp_id, protocol_id, protocol):
protocol['id'] = protocol_id
protocol['idp_id'] = idp_id
with sql.session_for_write() as session:
self._get_idp(session, idp_id)
protocol_ref = FederationProtocolModel.from_dict(protocol)
session.add(protocol_ref)
return protocol_ref.to_dict()
def update_protocol(self, idp_id, protocol_id, protocol):
with sql.session_for_write() as session:
proto_ref = self._get_protocol(session, idp_id, protocol_id)
old_proto = proto_ref.to_dict()
old_proto.update(protocol)
new_proto = FederationProtocolModel.from_dict(old_proto)
for attr in FederationProtocolModel.mutable_attributes:
setattr(proto_ref, attr, getattr(new_proto, attr))
return proto_ref.to_dict()
def get_protocol(self, idp_id, protocol_id):
with sql.session_for_read() as session:
protocol_ref = self._get_protocol(session, idp_id, protocol_id)
return protocol_ref.to_dict()
def list_protocols(self, idp_id):
with sql.session_for_read() as session:
q = session.query(FederationProtocolModel)
q = q.filter_by(idp_id=idp_id)
protocols = [protocol.to_dict() for protocol in q]
return protocols
def delete_protocol(self, idp_id, protocol_id):
with sql.session_for_write() as session:
key_ref = self._get_protocol(session, idp_id, protocol_id)
session.delete(key_ref)
def _delete_assigned_protocols(self, session, idp_id):
query = session.query(FederationProtocolModel)
query = query.filter_by(idp_id=idp_id)
query.delete()
# Mapping CRUD
def _get_mapping(self, session, mapping_id):
mapping_ref = session.query(MappingModel).get(mapping_id)
if not mapping_ref:
raise exception.MappingNotFound(mapping_id=mapping_id)
return mapping_ref
@sql.handle_conflicts(conflict_type='mapping')
def create_mapping(self, mapping_id, mapping):
ref = {}
ref['id'] = mapping_id
ref['rules'] = mapping.get('rules')
with sql.session_for_write() as session:
mapping_ref = MappingModel.from_dict(ref)
session.add(mapping_ref)
return mapping_ref.to_dict()
def delete_mapping(self, mapping_id):
with sql.session_for_write() as session:
mapping_ref = self._get_mapping(session, mapping_id)
session.delete(mapping_ref)
def list_mappings(self):
with sql.session_for_read() as session:
mappings = session.query(MappingModel)
return [x.to_dict() for x in mappings]
def get_mapping(self, mapping_id):
with sql.session_for_read() as session:
mapping_ref = self._get_mapping(session, mapping_id)
return mapping_ref.to_dict()
@sql.handle_conflicts(conflict_type='mapping')
def update_mapping(self, mapping_id, mapping):
ref = {}
ref['id'] = mapping_id
ref['rules'] = mapping.get('rules')
with sql.session_for_write() as session:
mapping_ref = self._get_mapping(session, mapping_id)
old_mapping = mapping_ref.to_dict()
old_mapping.update(ref)
new_mapping = MappingModel.from_dict(old_mapping)
for attr in MappingModel.attributes:
setattr(mapping_ref, attr, getattr(new_mapping, attr))
return mapping_ref.to_dict()
def get_mapping_from_idp_and_protocol(self, idp_id, protocol_id):
with sql.session_for_read() as session:
protocol_ref = self._get_protocol(session, idp_id, protocol_id)
mapping_id = protocol_ref.mapping_id
mapping_ref = self._get_mapping(session, mapping_id)
return mapping_ref.to_dict()
# Service Provider CRUD
@sql.handle_conflicts(conflict_type='service_provider')
def create_sp(self, sp_id, sp):
sp['id'] = sp_id
with sql.session_for_write() as session:
sp_ref = ServiceProviderModel.from_dict(sp)
session.add(sp_ref)
return sp_ref.to_dict()
def delete_sp(self, sp_id):
with sql.session_for_write() as session:
sp_ref = self._get_sp(session, sp_id)
session.delete(sp_ref)
def _get_sp(self, session, sp_id):
sp_ref = session.query(ServiceProviderModel).get(sp_id)
if not sp_ref:
raise exception.ServiceProviderNotFound(sp_id=sp_id)
return sp_ref
def list_sps(self):
with sql.session_for_read() as session:
sps = session.query(ServiceProviderModel)
sps_list = [sp.to_dict() for sp in sps]
return sps_list
def get_sp(self, sp_id):
with sql.session_for_read() as session:
sp_ref = self._get_sp(session, sp_id)
return sp_ref.to_dict()
def update_sp(self, sp_id, sp):
with sql.session_for_write() as session:
sp_ref = self._get_sp(session, sp_id)
old_sp = sp_ref.to_dict()
old_sp.update(sp)
new_sp = ServiceProviderModel.from_dict(old_sp)
for attr in ServiceProviderModel.mutable_attributes:
setattr(sp_ref, attr, getattr(new_sp, attr))
return sp_ref.to_dict()
def get_enabled_service_providers(self):
with sql.session_for_read() as session:
service_providers = session.query(ServiceProviderModel)
service_providers = service_providers.filter_by(enabled=True)
return service_providers

View File

@ -14,20 +14,11 @@
import abc
from oslo_log import versionutils
import six
from keystone import exception
# The FederationDriverBase class is the set of driver methods from earlier
# drivers that we still support, that have not been removed or modified. This
# class is then used to created the augmented V8 and V9 version abstract driver
# classes, without having to duplicate a lot of abstract method signatures.
# If you remove a method from V9, then move the abstract methods from this Base
# class to the V8 class. Do not modify any of the method signatures in the Base
# class - changes should only be made in the V8 and subsequent classes.
@six.add_metaclass(abc.ABCMeta)
class FederationDriverBase(object):
@ -329,6 +320,7 @@ class FederationDriverBase(object):
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def get_enabled_service_providers(self):
"""List enabled service providers for Service Catalog.
@ -346,49 +338,6 @@ class FederationDriverBase(object):
"""
raise exception.NotImplemented() # pragma: no cover
class FederationDriverV8(FederationDriverBase):
"""Removed or redefined methods from V8.
Move the abstract methods of any methods removed or modified in later
versions of the driver from FederationDriverBase to here. We maintain this
so that legacy drivers, which will be a subclass of FederationDriverV8, can
still reference them.
"""
@abc.abstractmethod
def list_idps(self):
"""List all identity providers.
:returns: list of idp refs
:rtype: list of dicts
:raises keystone.exception.IdentityProviderNotFound: If the IdP
doesn't exist.
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def list_sps(self):
"""List all service providers.
:returns: List of service provider ref objects
:rtype: list of dicts
"""
raise exception.NotImplemented() # pragma: no cover
class FederationDriverV9(FederationDriverBase):
"""New or redefined methods from V8.
Add any new V9 abstract methods (or those with modified signatures) to
this class.
"""
@abc.abstractmethod
def list_idps(self, hints):
"""List all identity providers.
@ -418,112 +367,3 @@ class FederationDriverV9(FederationDriverBase):
"""
raise exception.NotImplemented() # pragma: no cover
class V9FederationWrapperForV8Driver(FederationDriverV9):
"""Wrapper class to supported a V8 legacy driver.
In order to support legacy drivers without having to make the manager code
driver-version aware, we wrap legacy drivers so that they look like the
latest version. For the various changes made in a new driver, here are the
actions needed in this wrapper:
Method removed from new driver - remove the call-through method from this
class, since the manager will no longer be
calling it.
Method signature (or meaning) changed - wrap the old method in a new
signature here, and munge the input
and output parameters accordingly.
New method added to new driver - add a method to implement the new
functionality here if possible. If that is
not possible, then return NotImplemented,
since we do not guarantee to support new
functionality with legacy drivers.
"""
@versionutils.deprecated(
as_of=versionutils.deprecated.MITAKA,
what='keystone.federation.FederationDriverV8',
in_favor_of='keystone.federation.FederationDriverV9',
remove_in=+2)
def __init__(self, wrapped_driver):
self.driver = wrapped_driver
def create_idp(self, idp_id, idp):
return self.driver.create_idp(idp_id, idp)
def delete_idp(self, idp_id):
self.driver.delete_idp(idp_id)
# NOTE(davechen): The hints is ignored here to support legacy drivers,
# but the filters in hints will be remain unsatisfied and V3Controller
# wrapper will apply these filters at the end. So that the result get
# returned for list IdP will still be filtered with the legacy drivers.
def list_idps(self, hints):
return self.driver.list_idps()
def get_idp(self, idp_id):
return self.driver.get_idp(idp_id)
def get_idp_from_remote_id(self, remote_id):
return self.driver.get_idp_from_remote_id(remote_id)
def update_idp(self, idp_id, idp):
return self.driver.update_idp(idp_id, idp)
def create_protocol(self, idp_id, protocol_id, protocol):
return self.driver.create_protocol(idp_id, protocol_id, protocol)
def update_protocol(self, idp_id, protocol_id, protocol):
return self.driver.update_protocol(idp_id, protocol_id, protocol)
def get_protocol(self, idp_id, protocol_id):
return self.driver.get_protocol(idp_id, protocol_id)
def list_protocols(self, idp_id):
return self.driver.list_protocols(idp_id)
def delete_protocol(self, idp_id, protocol_id):
self.driver.delete_protocol(idp_id, protocol_id)
def create_mapping(self, mapping_id, mapping):
return self.driver.create_mapping(mapping_id, mapping)
def delete_mapping(self, mapping_id):
self.driver.delete_mapping(mapping_id)
def update_mapping(self, mapping_id, mapping_ref):
return self.driver.update_mapping(mapping_id, mapping_ref)
def list_mappings(self):
return self.driver.list_mappings()
def get_mapping(self, mapping_id):
return self.driver.get_mapping(mapping_id)
def get_mapping_from_idp_and_protocol(self, idp_id, protocol_id):
return self.driver.get_mapping_from_idp_and_protocol(
idp_id, protocol_id)
def create_sp(self, sp_id, sp):
return self.driver.create_sp(sp_id, sp)
def delete_sp(self, sp_id):
self.driver.delete_sp(sp_id)
# NOTE(davechen): The hints is ignored here to support legacy drivers,
# but the filters in hints will be remain unsatisfied and V3Controller
# wrapper will apply these filters at the end. So that the result get
# returned for list SPs will still be filtered with the legacy drivers.
def list_sps(self, hints):
return self.driver.list_sps()
def get_sp(self, sp_id):
return self.driver.get_sp(sp_id)
def update_sp(self, sp_id, sp):
return self.driver.update_sp(sp_id, sp)
def get_enabled_service_providers(self):
return self.driver.get_enabled_service_providers()

View File

@ -161,7 +161,7 @@ class ServiceProviderModel(sql.ModelBase, sql.DictBase):
return d
class Federation(base.FederationDriverV9):
class Federation(base.FederationDriverBase):
_CONFLICT_LOG_MSG = 'Conflict %(conflict_type)s: %(details)s'

View File

@ -12,15 +12,11 @@
"""Main entry point into the Federation service."""
from oslo_log import versionutils
from keystone.common import cache
from keystone.common import dependency
from keystone.common import extension
from keystone.common import manager
import keystone.conf
from keystone import exception
from keystone.federation.backends import base
from keystone.federation import utils
@ -58,14 +54,6 @@ class Manager(manager.Manager):
def __init__(self):
super(Manager, self).__init__(CONF.federation.driver)
# Make sure it is a driver version we support, and if it is a legacy
# driver, then wrap it.
if isinstance(self.driver, base.FederationDriverV8):
self.driver = base.V9FederationWrapperForV8Driver(self.driver)
elif not isinstance(self.driver, base.FederationDriverV9):
raise exception.UnsupportedDriverVersion(
driver=CONF.federation.driver)
@MEMOIZE
def get_enabled_service_providers(self):
"""List enabled service providers for Service Catalog.
@ -113,43 +101,3 @@ class Manager(manager.Manager):
rule_processor = utils.RuleProcessor(mapping['id'], rules)
mapped_properties = rule_processor.process(assertion_data)
return mapped_properties, mapping['id']
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.federation.FederationDriverBase',
in_favor_of='keystone.federation.backends.base.FederationDriverBase',
remove_in=+1)
class FederationDriverBase(base.FederationDriverBase):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.federation.FederationDriverV8',
in_favor_of='keystone.federation.backends.base.FederationDriverV8',
remove_in=+1)
class FederationDriverV8(base.FederationDriverV8):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.federation.FederationDriverV9',
in_favor_of='keystone.federation.backends.base.FederationDriverV9',
remove_in=+1)
class FederationDriverV9(base.FederationDriverV9):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.federation.V9FederationWrapperForV8Driver',
in_favor_of=(
'keystone.federation.backends.base.V9FederationWrapperForV8Driver'),
remove_in=+1)
class V9FederationWrapperForV8Driver(base.V9FederationWrapperForV8Driver):
pass
Driver = manager.create_legacy_driver(base.FederationDriverV8)

View File

@ -49,7 +49,7 @@ def filter_user(user_ref):
@six.add_metaclass(abc.ABCMeta)
class IdentityDriverV8(object):
class IdentityDriverBase(object):
"""Interface description for an Identity driver.
The schema for users and groups is different depending on whether the
@ -180,7 +180,7 @@ class IdentityDriverV8(object):
:param str user_id: User ID
:param str password: Password
:returns: user. See user schema in :class:`~.IdentityDriverV8`.
:returns: user. See user schema in :class:`~.IdentityDriverBase`.
:rtype: dict
:raises AssertionError: If user or password is invalid.
@ -195,7 +195,7 @@ class IdentityDriverV8(object):
:param str user_id: user ID. The driver can ignore this value.
:param dict user: user info. See user schema in
:class:`~.IdentityDriverV8`.
:class:`~.IdentityDriverBase`.
:returns: user, matching the user schema. The driver should not return
the password.
@ -215,7 +215,7 @@ class IdentityDriverV8(object):
:type hints: keystone.common.driver_hints.Hints
:returns: a list of users or an empty list. See user schema in
:class:`~.IdentityDriverV8`.
:class:`~.IdentityDriverBase`.
:rtype: list of dict
"""
@ -231,7 +231,7 @@ class IdentityDriverV8(object):
:type hints: keystone.common.driver_hints.Hints
:returns: a list of users or an empty list. See user schema in
:class:`~.IdentityDriverV8`.
:class:`~.IdentityDriverBase`.
:rtype: list of dict
:raises keystone.exception.GroupNotFound: If the group doesn't exist.
@ -245,7 +245,7 @@ class IdentityDriverV8(object):
:param str user_id: User ID.
:returns: user. See user schema in :class:`~.IdentityDriverV8`.
:returns: user. See user schema in :class:`~.IdentityDriverBase`.
:rtype: dict
:raises keystone.exception.UserNotFound: If the user doesn't exist.
@ -259,10 +259,10 @@ class IdentityDriverV8(object):
:param str user_id: User ID.
:param dict user: User modification. See user schema in
:class:`~.IdentityDriverV8`. Properties set to None will be
:class:`~.IdentityDriverBase`. Properties set to None will be
removed. Required properties cannot be removed.
:returns: user. See user schema in :class:`~.IdentityDriverV8`.
:returns: user. See user schema in :class:`~.IdentityDriverBase`.
:raises keystone.exception.UserNotFound: If the user doesn't exist.
:raises keystone.exception.Conflict: If a duplicate user exists in the
@ -352,7 +352,7 @@ class IdentityDriverV8(object):
:param str group_id: group ID. The driver can ignore this value.
:param dict group: group info. See group schema in
:class:`~.IdentityDriverV8`.
:class:`~.IdentityDriverBase`.
:returns: group, matching the group schema.
:rtype: dict
@ -371,7 +371,7 @@ class IdentityDriverV8(object):
:type hints: keystone.common.driver_hints.Hints
:returns: a list of group_refs or an empty list. See group schema in
:class:`~.IdentityDriverV8`.
:class:`~.IdentityDriverBase`.
"""
raise exception.NotImplemented() # pragma: no cover
@ -386,7 +386,7 @@ class IdentityDriverV8(object):
:type hints: keystone.common.driver_hints.Hints
:returns: a list of group_refs or an empty list. See group schema in
:class:`~.IdentityDriverV8`.
:class:`~.IdentityDriverBase`.
:raises keystone.exception.UserNotFound: If the user doesn't exist.
@ -399,7 +399,7 @@ class IdentityDriverV8(object):
:param str group_id: group ID.
:returns: group info. See group schema in :class:`~.IdentityDriverV8`.
:returns: group info. See group schema in :class:`~.IdentityDriverBase`
:rtype: dict
:raises keystone.exception.GroupNotFound: If the group doesn't exist.
@ -413,7 +413,8 @@ class IdentityDriverV8(object):
:param str group_name: group name.
:param str domain_id: domain ID.
:returns: group info. See group schema in :class:`~.IdentityDriverV8`.
:returns: group info. See group schema in
:class:`~.IdentityDriverBase`.
:rtype: dict
:raises keystone.exception.GroupNotFound: If the group doesn't exist.
@ -426,7 +427,8 @@ class IdentityDriverV8(object):
:param str group_id: Group ID.
:param dict group: Group modification. See group schema in
:class:`~.IdentityDriverV8`. Required properties cannot be removed.
:class:`~.IdentityDriverBase`. Required properties cannot be
removed.
:returns: group, matching the group schema.
:rtype: dict

View File

@ -36,7 +36,7 @@ _DEPRECATION_MSG = _('%s for the LDAP identity backend has been deprecated in '
'access. It will be removed in the "O" release.')
class Identity(base.IdentityDriverV8):
class Identity(base.IdentityDriverBase):
def __init__(self, conf=None):
super(Identity, self).__init__()
if conf is None:

View File

@ -29,7 +29,7 @@ from keystone.identity.backends import sql_model as model
CONF = keystone.conf.CONF
class Identity(base.IdentityDriverV8):
class Identity(base.IdentityDriverBase):
# NOTE(henry-nash): Override the __init__() method so as to take a
# config parameter to enable sql to be used as a domain-specific driver.
def __init__(self, conf=None):

View File

@ -33,10 +33,7 @@ from keystone.common.validation import validators
import keystone.conf
from keystone import exception
from keystone.i18n import _, _LW
from keystone.identity.backends import base as identity_interface
from keystone.identity.mapping_backends import base as mapping_interface
from keystone.identity.mapping_backends import mapping
from keystone.identity.shadow_backends import base as shadow_interface
from keystone import notifications
@ -1280,17 +1277,6 @@ class Manager(manager.Manager):
return user_dict
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.identity.IdentityDriverV8',
in_favor_of='keystone.identity.backends.base.IdentityDriverV8',
remove_in=+1)
class IdentityDriverV8(identity_interface.IdentityDriverV8):
pass
Driver = manager.create_legacy_driver(identity_interface.IdentityDriverV8)
@dependency.provider('id_mapping_api')
class MappingManager(manager.Manager):
"""Default pivot point for the ID Mapping backend."""
@ -1343,18 +1329,6 @@ class MappingManager(manager.Manager):
ID_MAPPING_REGION.invalidate()
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.identity.MappingDriverV8',
in_favor_of='keystone.identity.mapping_backends.base.MappingDriverV8',
remove_in=+1)
class MappingDriverV8(mapping_interface.MappingDriverV8):
pass
MappingDriver = manager.create_legacy_driver(mapping_interface.MappingDriverV8)
@dependency.provider('shadow_users_api')
class ShadowUsersManager(manager.Manager):
"""Default pivot point for the Shadow Users backend."""
@ -1365,19 +1339,3 @@ class ShadowUsersManager(manager.Manager):
shadow_driver = CONF.shadow_users.driver
super(ShadowUsersManager, self).__init__(shadow_driver)
if isinstance(self.driver, shadow_interface.ShadowUsersDriverV9):
self.driver = (
shadow_interface.V10ShadowUsersWrapperForV9Driver(self.driver))
elif not isinstance(self.driver,
shadow_interface.ShadowUsersDriverV10):
raise exception.UnsupportedDriverVersion(driver=shadow_driver)
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.identity.ShadowUsersDriverV9',
in_favor_of='keystone.identity.shadow_backends.base.ShadowUsersDriverV9',
remove_in=+1)
class ShadowUsersDriverV9(shadow_interface.ShadowUsersDriverV9):
pass

View File

@ -20,7 +20,7 @@ from keystone import exception
@six.add_metaclass(abc.ABCMeta)
class MappingDriverV8(object):
class MappingDriverBase(object):
"""Interface description for an ID Mapping driver."""
@abc.abstractmethod

View File

@ -36,7 +36,7 @@ class IDMapping(sql.ModelBase, sql.ModelDictMixin):
@dependency.requires('id_generator_api')
class Mapping(base.MappingDriverV8):
class Mapping(base.MappingDriverBase):
def get_public_id(self, local_entity):
# NOTE(henry-nash): Since the Public ID is regeneratable, rather

View File

@ -14,7 +14,6 @@
import abc
from oslo_log import versionutils
import six
from keystone import exception
@ -60,20 +59,6 @@ class ShadowUsersDriverBase(object):
"""
raise exception.NotImplemented()
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.identity.shadow_backends.base.ShadowUsersDriverV9',
in_favor_of='keystone.identity.shadow_backends.base.ShadowUsersDriverV10',
remove_in=+1)
class ShadowUsersDriverV9(ShadowUsersDriverBase):
pass
@six.add_metaclass(abc.ABCMeta)
class ShadowUsersDriverV10(ShadowUsersDriverBase):
"""Interface description for an Shadow Users V10 driver."""
@abc.abstractmethod
def get_user(self, user_id):
"""Return the found user.
@ -102,14 +87,3 @@ class ShadowUsersDriverV10(ShadowUsersDriverBase):
"""
raise exception.NotImplemented()
class V10ShadowUsersWrapperForV9Driver(ShadowUsersDriverV10):
def get_user(self, user_id):
raise exception.UserNotFound(user_id=user_id)
def create_nonlocal_user(self, user_dict):
return user_dict
def set_last_active_at(self, user_id):
pass

View File

@ -26,7 +26,7 @@ from keystone.identity.shadow_backends import base
CONF = cfg.CONF
class ShadowUsers(base.ShadowUsersDriverV10):
class ShadowUsers(base.ShadowUsersDriverBase):
@sql.handle_conflicts(conflict_type='federated_user')
def create_federated_user(self, federated_dict):
user = {

View File

@ -56,7 +56,7 @@ def filter_consumer(consumer_ref):
@six.add_metaclass(abc.ABCMeta)
class Oauth1DriverV8(object):
class Oauth1DriverBase(object):
"""Interface description for an OAuth1 driver."""
@abc.abstractmethod

View File

@ -84,7 +84,7 @@ class AccessToken(sql.ModelBase, sql.DictBase):
return dict(self.items())
class OAuth1(base.Oauth1DriverV8):
class OAuth1(base.Oauth1DriverBase):
def _get_consumer(self, session, consumer_id):
consumer_ref = session.query(Consumer).get(consumer_id)
if consumer_ref is None:

View File

@ -21,7 +21,6 @@ import uuid
import oauthlib.common
from oauthlib import oauth1
from oslo_log import log
from oslo_log import versionutils
from keystone.common import dependency
from keystone.common import extension
@ -30,7 +29,6 @@ import keystone.conf
from keystone import exception
from keystone.i18n import _, _LE
from keystone import notifications
from keystone.oauth1.backends import base
RequestValidator = oauth1.RequestValidator
@ -180,15 +178,3 @@ class Manager(manager.Manager):
notifications.Audit.created(self._REQUEST_TOKEN, ret['id'],
initiator)
return ret
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.oauth1.Oauth1DriverV8',
in_favor_of='keystone.oauth1.backends.base.Oauth1DriverV8',
remove_in=+1)
class Oauth1DriverV8(base.Oauth1DriverV8):
pass
Driver = manager.create_legacy_driver(base.Oauth1DriverV8)

View File

@ -21,7 +21,7 @@ CONF = keystone.conf.CONF
@six.add_metaclass(abc.ABCMeta)
class PolicyDriverV8(object):
class PolicyDriverBase(object):
def _get_list_limit(self):
return CONF.policy.list_limit or CONF.list_limit

View File

@ -69,7 +69,7 @@ def enforce(credentials, action, target, do_raise=True):
return _ENFORCER.enforce(action, target, credentials, **extra)
class Policy(base.PolicyDriverV8):
class Policy(base.PolicyDriverBase):
def enforce(self, credentials, action, target):
msg = 'enforce %(action)s: %(credentials)s'
LOG.debug(msg, {

View File

@ -14,14 +14,11 @@
"""Main entry point into the Policy service."""
from oslo_log import versionutils
from keystone.common import dependency
from keystone.common import manager
import keystone.conf
from keystone import exception
from keystone import notifications
from keystone.policy.backends import base
CONF = keystone.conf.CONF
@ -78,15 +75,3 @@ class Manager(manager.Manager):
raise exception.PolicyNotFound(policy_id=policy_id)
notifications.Audit.deleted(self._POLICY, policy_id, initiator)
return ret
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.policy.PolicyDriverV8',
in_favor_of='keystone.policy.backends.base.PolicyDriverV8',
remove_in=+1)
class PolicyDriverV8(base.PolicyDriverV8):
pass
Driver = manager.create_legacy_driver(base.PolicyDriverV8)

View File

@ -1,260 +0,0 @@
# 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 oslo_log import log
from keystone.common import clean
from keystone.common import driver_hints
from keystone.common import sql
from keystone import exception
from keystone.i18n import _LE
from keystone.resource.backends import base
LOG = log.getLogger(__name__)
class Resource(base.ResourceDriverV8):
def default_assignment_driver(self):
return 'sql'
def _get_project(self, session, project_id):
project_ref = session.query(Project).get(project_id)
if project_ref is None:
raise exception.ProjectNotFound(project_id=project_id)
return project_ref
def get_project(self, tenant_id):
with sql.session_for_read() as session:
return self._get_project(session, tenant_id).to_dict()
def get_project_by_name(self, tenant_name, domain_id):
with sql.session_for_read() as session:
query = session.query(Project)
query = query.filter_by(name=tenant_name)
query = query.filter_by(domain_id=domain_id)
try:
project_ref = query.one()
except sql.NotFound:
raise exception.ProjectNotFound(project_id=tenant_name)
return project_ref.to_dict()
@driver_hints.truncated
def list_projects(self, hints):
with sql.session_for_read() as session:
query = session.query(Project)
project_refs = sql.filter_limit_query(Project, query, hints)
return [project_ref.to_dict() for project_ref in project_refs]
def list_projects_from_ids(self, ids):
if not ids:
return []
else:
with sql.session_for_read() as session:
query = session.query(Project)
query = query.filter(Project.id.in_(ids))
return [project_ref.to_dict() for project_ref in query.all()]
def list_project_ids_from_domain_ids(self, domain_ids):
if not domain_ids:
return []
else:
with sql.session_for_read() as session:
query = session.query(Project.id)
query = (
query.filter(Project.domain_id.in_(domain_ids)))
return [x.id for x in query.all()]
def list_projects_in_domain(self, domain_id):
with sql.session_for_read() as session:
self._get_domain(session, domain_id)
query = session.query(Project)
project_refs = query.filter_by(domain_id=domain_id)
return [project_ref.to_dict() for project_ref in project_refs]
def _get_children(self, session, project_ids):
query = session.query(Project)
query = query.filter(Project.parent_id.in_(project_ids))
project_refs = query.all()
return [project_ref.to_dict() for project_ref in project_refs]
def list_projects_in_subtree(self, project_id):
with sql.session_for_read() as session:
children = self._get_children(session, [project_id])
subtree = []
examined = set([project_id])
while children:
children_ids = set()
for ref in children:
if ref['id'] in examined:
msg = _LE('Circular reference or a repeated '
'entry found in projects hierarchy - '
'%(project_id)s.')
LOG.error(msg, {'project_id': ref['id']})
return
children_ids.add(ref['id'])
examined.update(children_ids)
subtree += children
children = self._get_children(session, children_ids)
return subtree
def list_project_parents(self, project_id):
with sql.session_for_read() as session:
project = self._get_project(session, project_id).to_dict()
parents = []
examined = set()
while project.get('parent_id') is not None:
if project['id'] in examined:
msg = _LE('Circular reference or a repeated '
'entry found in projects hierarchy - '
'%(project_id)s.')
LOG.error(msg, {'project_id': project['id']})
return
examined.add(project['id'])
parent_project = self._get_project(
session, project['parent_id']).to_dict()
parents.append(parent_project)
project = parent_project
return parents
def is_leaf_project(self, project_id):
with sql.session_for_read() as session:
project_refs = self._get_children(session, [project_id])
return not project_refs
# CRUD
@sql.handle_conflicts(conflict_type='project')
def create_project(self, tenant_id, tenant):
tenant['name'] = clean.project_name(tenant['name'])
with sql.session_for_write() as session:
tenant_ref = Project.from_dict(tenant)
session.add(tenant_ref)
return tenant_ref.to_dict()
@sql.handle_conflicts(conflict_type='project')
def update_project(self, tenant_id, tenant):
if 'name' in tenant:
tenant['name'] = clean.project_name(tenant['name'])
with sql.session_for_write() as session:
tenant_ref = self._get_project(session, tenant_id)
old_project_dict = tenant_ref.to_dict()
for k in tenant:
old_project_dict[k] = tenant[k]
new_project = Project.from_dict(old_project_dict)
for attr in Project.attributes:
if attr != 'id':
setattr(tenant_ref, attr, getattr(new_project, attr))
tenant_ref.extra = new_project.extra
return tenant_ref.to_dict(include_extra_dict=True)
@sql.handle_conflicts(conflict_type='project')
def delete_project(self, tenant_id):
with sql.session_for_write() as session:
tenant_ref = self._get_project(session, tenant_id)
session.delete(tenant_ref)
# domain crud
@sql.handle_conflicts(conflict_type='domain')
def create_domain(self, domain_id, domain):
with sql.session_for_write() as session:
ref = Domain.from_dict(domain)
session.add(ref)
return ref.to_dict()
@driver_hints.truncated
def list_domains(self, hints):
with sql.session_for_read() as session:
query = session.query(Domain)
refs = sql.filter_limit_query(Domain, query, hints)
return [ref.to_dict() for ref in refs]
def list_domains_from_ids(self, ids):
if not ids:
return []
else:
with sql.session_for_read() as session:
query = session.query(Domain)
query = query.filter(Domain.id.in_(ids))
domain_refs = query.all()
return [domain_ref.to_dict() for domain_ref in domain_refs]
def _get_domain(self, session, domain_id):
ref = session.query(Domain).get(domain_id)
if ref is None:
raise exception.DomainNotFound(domain_id=domain_id)
return ref
def get_domain(self, domain_id):
with sql.session_for_read() as session:
return self._get_domain(session, domain_id).to_dict()
def get_domain_by_name(self, domain_name):
with sql.session_for_read() as session:
try:
ref = (session.query(Domain).
filter_by(name=domain_name).one())
except sql.NotFound:
raise exception.DomainNotFound(domain_id=domain_name)
return ref.to_dict()
@sql.handle_conflicts(conflict_type='domain')
def update_domain(self, domain_id, domain):
with sql.session_for_write() as session:
ref = self._get_domain(session, domain_id)
old_dict = ref.to_dict()
for k in domain:
old_dict[k] = domain[k]
new_domain = Domain.from_dict(old_dict)
for attr in Domain.attributes:
if attr != 'id':
setattr(ref, attr, getattr(new_domain, attr))
ref.extra = new_domain.extra
return ref.to_dict()
def delete_domain(self, domain_id):
with sql.session_for_write() as session:
ref = self._get_domain(session, domain_id)
session.delete(ref)
class Domain(sql.ModelBase, sql.DictBase):
__tablename__ = 'domain'
attributes = ['id', 'name', 'enabled']
id = sql.Column(sql.String(64), primary_key=True)
name = sql.Column(sql.String(64), nullable=False)
enabled = sql.Column(sql.Boolean, default=True, nullable=False)
extra = sql.Column(sql.JsonBlob())
__table_args__ = (sql.UniqueConstraint('name'),)
class Project(sql.ModelBase, sql.DictBase):
__tablename__ = 'project'
attributes = ['id', 'name', 'domain_id', 'description', 'enabled',
'parent_id', 'is_domain']
id = sql.Column(sql.String(64), primary_key=True)
name = sql.Column(sql.String(64), nullable=False)
domain_id = sql.Column(sql.String(64), sql.ForeignKey('domain.id'),
nullable=False)
description = sql.Column(sql.Text())
enabled = sql.Column(sql.Boolean)
extra = sql.Column(sql.JsonBlob())
parent_id = sql.Column(sql.String(64), sql.ForeignKey('project.id'))
is_domain = sql.Column(sql.Boolean, default=False, nullable=False,
server_default='0')
# Unique constraint across two columns to create the separation
# rather than just only 'name' being unique
__table_args__ = (sql.UniqueConstraint('domain_id', 'name'),)

View File

@ -13,15 +13,12 @@
# under the License.
import abc
import copy
from oslo_log import log
from oslo_log import versionutils
import six
import keystone.conf
from keystone import exception
from keystone.i18n import _, _LE
CONF = keystone.conf.CONF
@ -38,17 +35,6 @@ def get_project_from_domain(domain_ref):
return project_ref
# The ResourceDriverBase class is the set of driver methods from earlier
# drivers that we still support, that have not been removed or modified. This
# class is then used to created the augmented V8 and V9 version abstract driver
# classes, without having to duplicate a lot of abstract method signatures.
# If you remove a method from V9, then move the abstract methods from this Base
# class to the V8 class. Do not modify any of the method signatures in the Base
# class - changes should only be made in the V8 and subsequent classes.
# Starting with V9, some drivers use a special value to represent a domain_id
# of None. See comment in Project class of resource/backends/sql.py for more
# details.
NULL_DOMAIN_ID = '<<keystone.domain.root>>'
@ -201,168 +187,6 @@ class ResourceDriverBase(object):
if domain_id != CONF.identity.default_domain_id:
raise exception.DomainNotFound(domain_id=domain_id)
class ResourceDriverV8(ResourceDriverBase):
"""Removed or redefined methods from V8.
Move the abstract methods of any methods removed or modified in later
versions of the driver from ResourceDriverBase to here. We maintain this
so that legacy drivers, which will be a subclass of ResourceDriverV8, can
still reference them.
"""
@abc.abstractmethod
def create_project(self, tenant_id, tenant):
"""Create a new project.
:param tenant_id: This parameter can be ignored.
:param dict tenant: The new project
Project schema::
type: object
properties:
id:
type: string
name:
type: string
domain_id:
type: string
description:
type: string
enabled:
type: boolean
parent_id:
type: string
is_domain:
type: boolean
required: [id, name, domain_id]
additionalProperties: true
If project doesn't match the schema the behavior is undefined.
The driver can impose requirements such as the maximum length of a
field. If these requirements are not met the behavior is undefined.
:raises keystone.exception.Conflict: if the project id already exists
or the name already exists for the domain_id.
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def get_project_by_name(self, tenant_name, domain_id):
"""Get a tenant by name.
:returns: tenant_ref
:raises keystone.exception.ProjectNotFound: if a project with the
tenant_name does not exist within the domain
"""
raise exception.NotImplemented() # pragma: no cover
# Domain management functions for backends that only allow a single
# domain. Although we no longer use this, a custom legacy driver might
# have made use of it, so keep it here in case.
def _set_default_domain(self, ref):
"""If the domain ID has not been set, set it to the default."""
if isinstance(ref, dict):
if 'domain_id' not in ref:
ref = ref.copy()
ref['domain_id'] = CONF.identity.default_domain_id
return ref
elif isinstance(ref, list):
return [self._set_default_domain(x) for x in ref]
else:
raise ValueError(_('Expected dict or list: %s') % type(ref))
# domain crud
@abc.abstractmethod
def create_domain(self, domain_id, domain):
"""Create a new domain.
:raises keystone.exception.Conflict: if the domain_id or domain name
already exists
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def list_domains(self, hints):
"""List domains in the system.
:param hints: filter hints which the driver should
implement if at all possible.
:returns: a list of domain_refs or an empty list.
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def list_domains_from_ids(self, domain_ids):
"""List domains for the provided list of ids.
:param domain_ids: list of ids
:returns: a list of domain_refs.
This method is used internally by the assignment manager to bulk read
a set of domains given their ids.
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def get_domain(self, domain_id):
"""Get a domain by ID.
:returns: domain_ref
:raises keystone.exception.DomainNotFound: if domain_id does not exist
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def get_domain_by_name(self, domain_name):
"""Get a domain by name.
:returns: domain_ref
:raises keystone.exception.DomainNotFound: if domain_name does not
exist
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def update_domain(self, domain_id, domain):
"""Update an existing domain.
:raises keystone.exception.DomainNotFound: if domain_id does not exist
:raises keystone.exception.Conflict: if domain name already exists
"""
raise exception.NotImplemented() # pragma: no cover
@abc.abstractmethod
def delete_domain(self, domain_id):
"""Delete an existing domain.
:raises keystone.exception.DomainNotFound: if domain_id does not exist
"""
raise exception.NotImplemented() # pragma: no cover
class ResourceDriverV9(ResourceDriverBase):
"""New or redefined methods from V8.
Add any new V9 abstract methods (or those with modified signatures) to
this class.
"""
@abc.abstractmethod
def create_project(self, project_id, project):
"""Create a new project.
@ -436,197 +260,3 @@ class ResourceDriverV9(ResourceDriverBase):
"""
raise exception.NotImplemented() # pragma: no cover
class V9ResourceWrapperForV8Driver(ResourceDriverV9):
"""Wrapper class to supported a V8 legacy driver.
In order to support legacy drivers without having to make the manager code
driver-version aware, we wrap legacy drivers so that they look like the
latest version. For the various changes made in a new driver, here are the
actions needed in this wrapper:
Method removed from new driver - remove the call-through method from this
class, since the manager will no longer be
calling it.
Method signature (or meaning) changed - wrap the old method in a new
signature here, and munge the input
and output parameters accordingly.
New method added to new driver - add a method to implement the new
functionality here if possible. If that is
not possible, then return NotImplemented,
since we do not guarantee to support new
functionality with legacy drivers.
This wrapper contains the following support for newer manager code:
- The current manager code expects domains to be represented as projects
acting as domains, something that may not be possible in a legacy driver.
Hence the wrapper will map any calls for projects acting as a domain back
onto the driver domain methods. The caveat for this, is that this assumes
that there can not be a clash between a project_id and a domain_id, in
which case it may not be able to locate the correct entry.
"""
@versionutils.deprecated(
as_of=versionutils.deprecated.MITAKA,
what='keystone.resource.ResourceDriverV8',
in_favor_of='keystone.resource.ResourceDriverV9',
remove_in=+2)
def __init__(self, wrapped_driver):
self.driver = wrapped_driver
def _get_domain_from_project(self, project_ref):
"""Create a domain ref from a project ref.
Based on the provided project ref (or partial ref), creates a
domain ref, so that the result can be passed to the driver
domain methods.
"""
domain_ref = project_ref.copy()
for k in ['parent_id', 'domain_id', 'is_domain']:
domain_ref.pop(k, None)
return domain_ref
def get_project_by_name(self, project_name, domain_id):
if domain_id is None:
try:
domain_ref = self.driver.get_domain_by_name(project_name)
return get_project_from_domain(domain_ref)
except exception.DomainNotFound:
raise exception.ProjectNotFound(project_id=project_name)
else:
return self.driver.get_project_by_name(project_name, domain_id)
def create_project(self, project_id, project):
if project['is_domain']:
new_domain = self._get_domain_from_project(project)
domain_ref = self.driver.create_domain(project_id, new_domain)
return get_project_from_domain(domain_ref)
else:
return self.driver.create_project(project_id, project)
def list_projects(self, hints):
"""List projects and/or domains.
We use the hints filter to determine whether we are listing projects,
domains or both.
If the filter includes domain_id==None, then we should only list
domains (convert to a project acting as a domain) since regular
projects always have a non-None value for domain_id.
Likewise, if the filter includes domain_id==<non-None value>, then we
should only list projects.
If there is no domain_id filter, then we need to do a combained listing
of domains and projects, converting domains to projects acting as a
domain.
"""
domain_listing_filter = None
for f in hints.filters:
if (f['name'] == 'domain_id'):
domain_listing_filter = f
if domain_listing_filter is not None:
if domain_listing_filter['value'] is not None:
proj_list = self.driver.list_projects(hints)
else:
domains = self.driver.list_domains(hints)
proj_list = [get_project_from_domain(p) for p in domains]
hints.filters.remove(domain_listing_filter)
return proj_list
else:
# No domain_id filter, so combine domains and projects. Although
# we hand any remaining filters into each driver, since each filter
# might need to be carried out more than once, we use copies of the
# filters, allowing the original filters to be passed back up to
# controller level where a final filter will occur.
local_hints = copy.deepcopy(hints)
proj_list = self.driver.list_projects(local_hints)
local_hints = copy.deepcopy(hints)
domains = self.driver.list_domains(local_hints)
for domain in domains:
proj_list.append(get_project_from_domain(domain))
return proj_list
def list_projects_from_ids(self, project_ids):
return [self.get_project(id) for id in project_ids]
def list_project_ids_from_domain_ids(self, domain_ids):
return self.driver.list_project_ids_from_domain_ids(domain_ids)
def list_projects_in_domain(self, domain_id):
return self.driver.list_projects_in_domain(domain_id)
def get_project(self, project_id):
try:
domain_ref = self.driver.get_domain(project_id)
return get_project_from_domain(domain_ref)
except exception.DomainNotFound:
return self.driver.get_project(project_id)
def _is_domain(self, project_id):
ref = self.get_project(project_id)
return ref.get('is_domain', False)
def update_project(self, project_id, project):
if self._is_domain(project_id):
update_domain = self._get_domain_from_project(project)
domain_ref = self.driver.update_domain(project_id, update_domain)
return get_project_from_domain(domain_ref)
else:
return self.driver.update_project(project_id, project)
def delete_project(self, project_id):
if self._is_domain(project_id):
try:
self.driver.delete_domain(project_id)
except exception.DomainNotFound:
raise exception.ProjectNotFound(project_id=project_id)
else:
self.driver.delete_project(project_id)
def delete_projects_from_ids(self, project_ids):
raise exception.NotImplemented() # pragma: no cover
def list_project_parents(self, project_id):
"""List a project's ancestors.
The current manager expects the ancestor tree to end with the project
acting as the domain (since that's now the top of the tree), but a
legacy driver will not have that top project in their projects table,
since it's still in the domain table. Hence we lift the algorithm for
traversing up the tree from the driver to here, so that our version of
get_project() is called, which will fetch the "project" from the right
table.
"""
project = self.get_project(project_id)
parents = []
examined = set()
while project.get('parent_id') is not None:
if project['id'] in examined:
msg = _LE('Circular reference or a repeated '
'entry found in projects hierarchy - '
'%(project_id)s.')
LOG.error(msg, {'project_id': project['id']})
return
examined.add(project['id'])
parent_project = self.get_project(project['parent_id'])
parents.append(parent_project)
project = parent_project
return parents
def list_projects_in_subtree(self, project_id):
return self.driver.list_projects_in_subtree(project_id)
def is_leaf_project(self, project_id):
return self.driver.is_leaf_project(project_id)
def list_projects_acting_as_domain(self, hints):
refs = self.driver.list_domains(hints)
return [get_project_from_domain(p) for p in refs]

View File

@ -22,7 +22,7 @@ from keystone.resource.backends import base
LOG = log.getLogger(__name__)
class Resource(base.ResourceDriverV9):
class Resource(base.ResourceDriverBase):
def default_assignment_driver(self):
return 'sql'

View File

@ -24,7 +24,7 @@ CONF = keystone.conf.CONF
@six.add_metaclass(abc.ABCMeta)
class DomainConfigDriverV8(object):
class DomainConfigDriverBase(object):
"""Interface description for a Domain Config driver."""
@abc.abstractmethod

View File

@ -48,7 +48,7 @@ class ConfigRegister(sql.ModelBase, sql.ModelDictMixin):
domain_id = sql.Column(sql.String(64), nullable=False)
class DomainConfig(base.DomainConfigDriverV8):
class DomainConfig(base.DomainConfigDriverBase):
def choose_table(self, sensitive):
if sensitive:

View File

@ -28,7 +28,6 @@ from keystone import exception
from keystone.i18n import _, _LE, _LW
from keystone import notifications
from keystone.resource.backends import base
from keystone.resource.config_backends import base as config_base
from keystone.token import provider as token_provider
CONF = keystone.conf.CONF
@ -63,13 +62,6 @@ class Manager(manager.Manager):
super(Manager, self).__init__(resource_driver)
# Make sure it is a driver version we support, and if it is a legacy
# driver, then wrap it.
if isinstance(self.driver, base.ResourceDriverV8):
self.driver = base.V9ResourceWrapperForV8Driver(self.driver)
elif not isinstance(self.driver, base.ResourceDriverV9):
raise exception.UnsupportedDriverVersion(driver=resource_driver)
def _get_hierarchy_depth(self, parents_list):
return len(parents_list) + 1
@ -903,45 +895,6 @@ class Manager(manager.Manager):
raise
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.resource.ResourceDriverBase',
in_favor_of='keystone.resource.backends.base.ResourceDriverBase',
remove_in=+1)
class ResourceDriverBase(base.ResourceDriverBase):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.resource.ResourceDriverV8',
in_favor_of='keystone.resource.backends.base.ResourceDriverV8',
remove_in=+1)
class ResourceDriverV8(base.ResourceDriverV8):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.resource.ResourceDriverV9',
in_favor_of='keystone.resource.backends.base.ResourceDriverV9',
remove_in=+1)
class ResourceDriverV9(base.ResourceDriverV9):
pass
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.resource.V9ResourceWrapperForV8Driver',
in_favor_of='keystone.resource.backends.base.V9ResourceWrapperForV8Driver',
remove_in=+1)
class V9ResourceWrapperForV8Driver(base.V9ResourceWrapperForV8Driver):
pass
Driver = manager.create_legacy_driver(base.ResourceDriverV8)
MEMOIZE_CONFIG = cache.get_memoization_decorator(group='domain_config')
@ -1439,16 +1392,3 @@ class DomainConfigManager(manager.Manager):
config_list.append(_option_dict(each_group, each_option))
return self._list_to_config(config_list, req_option=option)
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.resource.DomainConfigDriverV8',
in_favor_of='keystone.resource.config_backends.base.DomainConfigDriverV8',
remove_in=+1)
class DomainConfigDriverV8(config_base.DomainConfigDriverV8):
pass
DomainConfigDriver = manager.create_legacy_driver(
config_base.DomainConfigDriverV8)

View File

@ -33,7 +33,7 @@ def revoked_before_cutoff_time():
@six.add_metaclass(abc.ABCMeta)
class RevokeDriverV8(object):
class RevokeDriverBase(object):
"""Interface for recording and reporting revocation events."""
@abc.abstractmethod

View File

@ -38,7 +38,7 @@ class RevocationEvent(sql.ModelBase, sql.ModelDictMixin):
audit_chain_id = sql.Column(sql.String(32))
class Revoke(base.RevokeDriverV8):
class Revoke(base.RevokeDriverBase):
def _flush_batch_size(self, dialect):
batch_size = 0
if dialect == 'ibm_db_sa':

View File

@ -12,8 +12,6 @@
"""Main entry point into the Revoke service."""
from oslo_log import versionutils
from keystone.common import cache
from keystone.common import dependency
from keystone.common import extension
@ -23,7 +21,6 @@ from keystone import exception
from keystone.i18n import _
from keystone.models import revoke_model
from keystone import notifications
from keystone.revoke.backends import base
CONF = keystone.conf.CONF
@ -209,15 +206,3 @@ class Manager(manager.Manager):
def revoke(self, event):
self.driver.revoke(event)
REVOKE_REGION.invalidate()
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.revoke.RevokeDriverV8',
in_favor_of='keystone.revoke.backends.base.RevokeDriverV8',
remove_in=+1)
class RevokeDriverV8(base.RevokeDriverV8):
pass
Driver = manager.create_legacy_driver(base.RevokeDriverV8)

View File

@ -1,39 +0,0 @@
# 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 keystone.tests.unit import test_backend_sql
class SqlIdentityV8(test_backend_sql.SqlIdentity):
"""Test that a V8 driver still passes the same tests.
We use the SQL driver as an example of a V8 legacy driver.
"""
def config_overrides(self):
super(SqlIdentityV8, self).config_overrides()
# V8 SQL specific driver overrides
self.config_fixture.config(
group='assignment',
driver='keystone.assignment.V8_backends.sql.Assignment')
self.use_specific_sql_driver_version(
'keystone.assignment', 'backends', 'V8_')
def test_delete_project_assignments_same_id_as_domain(self):
self.skipTest("V8 doesn't support project acting as a domain.")
def test_delete_user_assignments_user_same_id_as_group(self):
self.skipTest("Groups and users with the same ID are not supported.")
def test_delete_group_assignments_group_same_id_as_user(self):
self.skipTest("Groups and users with the same ID are not supported.")

View File

@ -1,108 +0,0 @@
# 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.
import uuid
from six.moves import http_client
from keystone.tests.unit import test_v3_federation
class FederatedSetupMixinV8(object):
def useV8driver(self):
# We use the SQL driver as an example V8 driver, so override
# the current driver with that version.
self.config_fixture.config(
group='federation',
driver='keystone.federation.V8_backends.sql.Federation')
self.use_specific_sql_driver_version(
'keystone.federation', 'backends', 'V8_')
class FederatedIdentityProviderTestsV8(
test_v3_federation.FederatedIdentityProviderTests,
FederatedSetupMixinV8):
"""Test that a V8 driver still passes the same tests."""
def config_overrides(self):
super(FederatedIdentityProviderTestsV8, self).config_overrides()
self.useV8driver()
def test_create_idp_remote_repeated(self):
"""Create two IdentityProvider entities with some remote_ids.
A remote_id is the same for both so the second IdP is not
created because of the uniqueness of the remote_ids
Expect HTTP 409 Conflict code for the latter call.
Note: V9 drivers and later augment the conflict message with
additional information, which won't be present if we are running
a V8 driver - so override the newer tests to just ensure a
conflict message is raised.
"""
body = self.default_body.copy()
repeated_remote_id = uuid.uuid4().hex
body['remote_ids'] = [uuid.uuid4().hex,
uuid.uuid4().hex,
uuid.uuid4().hex,
repeated_remote_id]
self._create_default_idp(body=body)
url = self.base_url(suffix=uuid.uuid4().hex)
body['remote_ids'] = [uuid.uuid4().hex,
repeated_remote_id]
self.put(url, body={'identity_provider': body},
expected_status=http_client.CONFLICT)
def test_check_idp_uniqueness(self):
"""Add same IdP twice.
Expect HTTP 409 Conflict code for the latter call.
Note: V9 drivers and later augment the conflict message with
additional information, which won't be present if we are running
a V8 driver - so override the newer tests to just ensure a
conflict message is raised.
"""
url = self.base_url(suffix=uuid.uuid4().hex)
body = self._http_idp_input()
self.put(url, body={'identity_provider': body},
expected_status=http_client.CREATED)
self.put(url, body={'identity_provider': body},
expected_status=http_client.CONFLICT)
class MappingCRUDTestsV8(
test_v3_federation.MappingCRUDTests,
FederatedSetupMixinV8):
"""Test that a V8 driver still passes the same tests."""
def config_overrides(self):
super(MappingCRUDTestsV8, self).config_overrides()
self.useV8driver()
class ServiceProviderTestsV8(
test_v3_federation.ServiceProviderTests,
FederatedSetupMixinV8):
"""Test that a V8 driver still passes the same tests."""
def config_overrides(self):
super(ServiceProviderTestsV8, self).config_overrides()
self.useV8driver()
def test_filter_list_sp_by_id(self):
self.skipTest('Operation not supported in v8 and earlier drivers')
def test_filter_list_sp_by_enabled(self):
self.skipTest('Operation not supported in v8 and earlier drivers')

View File

@ -1,71 +0,0 @@
# 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.
import unittest
from keystone.resource.V8_backends import sql
from keystone.tests import unit
from keystone.tests.unit.ksfixtures import database
from keystone.tests.unit.resource import test_backends
from keystone.tests.unit import test_backend_sql
class SqlIdentityV8(test_backend_sql.SqlIdentity):
"""Test that a V8 driver still passes the same tests.
We use the SQL driver as an example of a V8 legacy driver.
"""
def config_overrides(self):
super(SqlIdentityV8, self).config_overrides()
# V8 SQL specific driver overrides
self.config_fixture.config(
group='resource',
driver='keystone.resource.V8_backends.sql.Resource')
self.use_specific_sql_driver_version(
'keystone.resource', 'backends', 'V8_')
def test_delete_projects_from_ids(self):
self.skipTest('Operation not supported in v8 and earlier drivers')
def test_delete_projects_from_ids_with_no_existing_project_id(self):
self.skipTest('Operation not supported in v8 and earlier drivers')
def test_delete_project_cascade(self):
self.skipTest('Operation not supported in v8 and earlier drivers')
def test_delete_large_project_cascade(self):
self.skipTest('Operation not supported in v8 and earlier drivers')
def test_hidden_project_domain_root_is_really_hidden(self):
self.skipTest('Operation not supported in v8 and earlier drivers')
class TestSqlResourceDriverV8(unit.BaseTestCase,
test_backends.ResourceDriverTests):
def setUp(self):
super(TestSqlResourceDriverV8, self).setUp()
version_specifiers = {
'keystone.resource': {
'versionless_backend': 'backends',
'versioned_backend': 'V8_backends'
}
}
self.useFixture(database.Database(version_specifiers))
self.driver = sql.Resource()
@unittest.skip('Null domain not allowed.')
def test_create_project_null_domain(self):
pass

View File

@ -1,30 +0,0 @@
# 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 keystone.tests.unit import test_backend_sql
class SqlIdentityV8(test_backend_sql.SqlIdentity):
"""Test that a V8 driver still passes the same tests.
We use the SQL driver as an example of a V8 legacy driver.
"""
def config_overrides(self):
super(SqlIdentityV8, self).config_overrides()
# V8 SQL specific driver overrides
self.config_fixture.config(
group='role',
driver='keystone.assignment.V8_role_backends.sql.Role')
self.use_specific_sql_driver_version(
'keystone.assignment', 'role_backends', 'V8_')

View File

@ -187,7 +187,7 @@ class CatalogTests(object):
region_two['id'],
{'parent_region_id': region_four['id']})
@mock.patch.object(base.CatalogDriverV8,
@mock.patch.object(base.CatalogDriverBase,
"_ensure_no_circle_in_hierarchical_regions")
def test_circular_regions_can_be_deleted(self, mock_ensure_on_circle):
# turn off the enforcement so that cycles can be created for the test

View File

@ -1,78 +0,0 @@
# 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.
import mock
from keystone import catalog
from keystone.common import manager
from keystone.tests import unit
class TestCreateLegacyDriver(unit.BaseTestCase):
@mock.patch('oslo_log.versionutils.report_deprecated_feature')
def test_class_is_properly_deprecated(self, mock_reporter):
Driver = manager.create_legacy_driver(catalog.CatalogDriverV8)
# NOTE(dstanek): I want to subvert the requirement for this
# class to implement all of the abstract methods.
Driver.__abstractmethods__ = set()
impl = Driver()
details = {
'as_of': 'Liberty',
'what': 'keystone.catalog.core.Driver',
'in_favor_of': 'keystone.catalog.core.CatalogDriverV8',
'remove_in': mock.ANY,
}
mock_reporter.assert_called_with(mock.ANY, mock.ANY, details)
self.assertEqual('N', mock_reporter.call_args[0][2]['remove_in'][0])
self.assertIsInstance(impl, catalog.CatalogDriverV8)
class Manager(manager.Manager):
def __init__(self, driver):
# NOTE(dstanek): I am not calling the parent's __init__ on
# purpose. I don't want to trigger the dynamic loading of a
# driver, I want to provide my own.
self.driver = driver
def test_property_passthru(self):
"""Manager delegating property call to a driver through __getattr__."""
class Driver(object):
def __init__(self):
self.counter = 0
@property
def p(self):
self.counter += 1
return self.counter
mgr = self.Manager(Driver())
# each property call should return a new value
self.assertNotEqual(mgr.p, mgr.p)
def test_callable_passthru(self):
class Driver(object):
class Inner(object):
pass
def method(self):
pass
drv = Driver()
mgr = self.Manager(drv)
self.assertEqual(drv.Inner, mgr.Inner)
self.assertEqual(drv.method, mgr.method)

View File

@ -17,7 +17,7 @@ import uuid
import mock
from six.moves import zip
from keystone import catalog
from keystone.catalog.backends import base as catalog_base
from keystone.tests import unit
from keystone.tests.unit.catalog import test_backends as catalog_tests
from keystone.tests.unit import default_fixtures
@ -205,7 +205,7 @@ class TestTemplatedCatalog(unit.TestCase, catalog_tests.CatalogTests):
def test_avoid_creating_circular_references_in_regions_update(self):
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)
@mock.patch.object(catalog.Driver,
@mock.patch.object(catalog_base.CatalogDriverBase,
"_ensure_no_circle_in_hierarchical_regions")
def test_circular_regions_can_be_deleted(self, mock_ensure_on_circle):
self.skip_test_overrides(BROKEN_WRITE_FUNCTIONALITY_MSG)

View File

@ -36,7 +36,7 @@ LOG = log.getLogger(__name__)
STORE_CONF_LOCK = threading.Lock()
class Token(token.persistence.TokenDriverV8):
class Token(token.persistence.TokenDriverBase):
"""KeyValueStore backend for tokens.
This is the base implementation for any/all key-value-stores (e.g.

View File

@ -81,7 +81,7 @@ def _expiry_range_all(session, upper_bound_func):
yield upper_bound_func()
class Token(token.persistence.TokenDriverV8):
class Token(token.persistence.TokenDriverBase):
# Public interface
def get_token(self, token_id):
if token_id is None:

View File

@ -225,7 +225,7 @@ class Manager(object):
@six.add_metaclass(abc.ABCMeta)
class TokenDriverV8(object):
class TokenDriverBase(object):
"""Interface description for a Token driver."""
@abc.abstractmethod
@ -352,6 +352,3 @@ class TokenDriverV8(object):
def flush_expired_tokens(self):
"""Archive or delete tokens that have expired."""
raise exception.NotImplemented() # pragma: no cover
Driver = manager.create_legacy_driver(TokenDriverV8)

View File

@ -20,7 +20,7 @@ from keystone import exception
@six.add_metaclass(abc.ABCMeta)
class TrustDriverV8(object):
class TrustDriverBase(object):
@abc.abstractmethod
def create_trust(self, trust_id, trust, roles):

View File

@ -56,7 +56,7 @@ class TrustRole(sql.ModelBase):
role_id = sql.Column(sql.String(64), primary_key=True, nullable=False)
class Trust(base.TrustDriverV8):
class Trust(base.TrustDriverBase):
@sql.handle_conflicts(conflict_type='trust')
def create_trust(self, trust_id, trust, roles):
with sql.session_for_write() as session:

View File

@ -14,7 +14,6 @@
"""Main entry point into the Trust service."""
from oslo_log import versionutils
from six.moves import zip
from keystone.common import dependency
@ -23,7 +22,6 @@ import keystone.conf
from keystone import exception
from keystone.i18n import _
from keystone import notifications
from keystone.trust.backends import base
CONF = keystone.conf.CONF
@ -200,15 +198,3 @@ class Manager(manager.Manager):
self.driver.delete_trust(trust_id)
notifications.Audit.deleted(self._TRUST, trust_id, initiator)
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.trust.TrustDriverV8',
in_favor_of='keystone.trust.backends.base.TrustDriverV8',
remove_in=+1)
class TrustDriverV8(base.TrustDriverV8):
pass
Driver = manager.create_legacy_driver(base.TrustDriverV8)

View File

@ -31,14 +31,7 @@ deps = -r{toxinidir}/test-requirements.txt
commands =
# Run each legacy test separately, to avoid SQL model redefinitions
find keystone -type f -name "*.pyc" -delete
nosetests -v -m '^[Tt]est' \
keystone/tests/unit/backend/legacy_drivers/assignment/V8/sql.py
nosetests -v -m '^[Tt]est' \
keystone/tests/unit/backend/legacy_drivers/role/V8/sql.py
nosetests -v -m '^[Tt]est' \
keystone/tests/unit/backend/legacy_drivers/federation/V8/api_v3.py
nosetests -v -m '^[Tt]est' \
keystone/tests/unit/backend/legacy_drivers/resource/V8/sql.py
# TODO(stevemar): Remove this test once we remove the infra job
[testenv:pep8]
deps =