Remove stable driver interfaces
bp removed-as-of-ocata Change-Id: I4672cf7d9d72ef725212085972dbcd90db0e47cf
This commit is contained in:
parent
fb6ff30009
commit
810e15689b
@ -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())
|
@ -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'),)
|
@ -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)
|
||||
|
@ -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'
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -24,7 +24,7 @@ LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class CredentialDriverV8(object):
|
||||
class CredentialDriverBase(object):
|
||||
# credential crud
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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
|
@ -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()
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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, {
|
||||
|
@ -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)
|
||||
|
@ -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'),)
|
@ -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]
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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':
|
||||
|
@ -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)
|
||||
|
@ -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.")
|
@ -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')
|
@ -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
|
@ -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_')
|
@ -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
|
||||
|
@ -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)
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
9
tox.ini
9
tox.ini
@ -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 =
|
||||
|
Loading…
x
Reference in New Issue
Block a user