Remove Dependency Injection
Refactors all of keystone's dependency injection to maintain a single centralized repository of instantiated objects. This means that we are no longer having to resolve order. All objects that need to reference the various manager APIs simply do so via the __getattr__ built into the Manager common object or the ProviderAPIMixin object. This is also the first step towards correcting our tests to where they cannot run "load_backends" multiple times. This forces any/all managers to properly run super() as the way to register the api is via __init__. This eliminates all use of the @dependency.requires and @dependency.provides decorators, simplifying the objects all around. Any instantiations of a Manager after keystone is running will now generate an error, ensuring everything for keystone is running before handling requests. An exception is for CLI and CLI tests, as the CLI may directly instantiate managers and will not lock the registry. Change-Id: I4ba17855efd797c0db9f4824936b49e4bff54b6a
This commit is contained in:
parent
3cc3986a89
commit
81f9fe6fed
@ -42,6 +42,7 @@ class AssignmentType(object):
|
||||
|
||||
class Assignment(base.AssignmentDriverBase):
|
||||
|
||||
@classmethod
|
||||
def default_role_driver(self):
|
||||
return 'sql'
|
||||
|
||||
|
@ -22,7 +22,6 @@ from oslo_log import log
|
||||
from keystone.assignment import schema
|
||||
from keystone.common import authorization
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import validation
|
||||
from keystone.common import wsgi
|
||||
import keystone.conf
|
||||
@ -34,7 +33,6 @@ CONF = keystone.conf.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'identity_api', 'token_provider_api')
|
||||
class TenantAssignment(controller.V2Controller):
|
||||
"""The V2 Project APIs that are processing assignments."""
|
||||
|
||||
@ -61,7 +59,6 @@ class TenantAssignment(controller.V2Controller):
|
||||
return self.format_project_list(tenant_refs, **params)
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'resource_api')
|
||||
class ProjectAssignmentV3(controller.V3Controller):
|
||||
"""The V3 Project APIs that are processing assignments."""
|
||||
|
||||
@ -81,7 +78,6 @@ class ProjectAssignmentV3(controller.V3Controller):
|
||||
hints=hints)
|
||||
|
||||
|
||||
@dependency.requires('role_api')
|
||||
class RoleV3(controller.V3Controller):
|
||||
"""The V3 Role CRUD APIs.
|
||||
|
||||
@ -246,7 +242,6 @@ class RoleV3(controller.V3Controller):
|
||||
return hints
|
||||
|
||||
|
||||
@dependency.requires('role_api')
|
||||
class ImpliedRolesV3(controller.V3Controller):
|
||||
"""The V3 ImpliedRoles CRD APIs. There is no Update."""
|
||||
|
||||
@ -383,8 +378,6 @@ class ImpliedRolesV3(controller.V3Controller):
|
||||
return results
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'identity_api', 'resource_api',
|
||||
'role_api')
|
||||
class GrantAssignmentV3(controller.V3Controller):
|
||||
"""The V3 Grant Assignment APIs."""
|
||||
|
||||
@ -503,7 +496,6 @@ class GrantAssignmentV3(controller.V3Controller):
|
||||
context=request.context_dict)
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'identity_api', 'resource_api')
|
||||
class RoleAssignmentV3(controller.V3Controller):
|
||||
"""The V3 Role Assignment APIs, really just list_role_assignment()."""
|
||||
|
||||
|
@ -20,7 +20,6 @@ import functools
|
||||
from oslo_log import log
|
||||
|
||||
from keystone.common import cache
|
||||
from keystone.common import dependency
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
@ -46,9 +45,6 @@ MEMOIZE_COMPUTED_ASSIGNMENTS = cache.get_memoization_decorator(
|
||||
|
||||
|
||||
@notifications.listener
|
||||
@dependency.provider('assignment_api')
|
||||
@dependency.requires('credential_api', 'identity_api', 'resource_api',
|
||||
'role_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Assignment backend.
|
||||
|
||||
@ -58,6 +54,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.assignment'
|
||||
_provides_api = 'assignment_api'
|
||||
|
||||
_PROJECT = 'project'
|
||||
_ROLE_REMOVED_FROM_USER = 'role_removed_from_user'
|
||||
@ -1057,12 +1054,11 @@ class Manager(manager.Manager):
|
||||
)
|
||||
|
||||
|
||||
@dependency.provider('role_api')
|
||||
@dependency.requires('assignment_api')
|
||||
class RoleManager(manager.Manager):
|
||||
"""Default pivot point for the Role backend."""
|
||||
|
||||
driver_namespace = 'keystone.role'
|
||||
_provides_api = 'role_api'
|
||||
|
||||
_ROLE = 'role'
|
||||
|
||||
@ -1072,8 +1068,12 @@ class RoleManager(manager.Manager):
|
||||
role_driver = CONF.role.driver
|
||||
|
||||
if role_driver is None:
|
||||
assignment_manager = dependency.get_provider('assignment_api')
|
||||
role_driver = assignment_manager.default_role_driver()
|
||||
# Explicitly load the assignment manager object
|
||||
assignment_driver = CONF.assignment.driver
|
||||
assignment_manager_obj = manager.load_driver(
|
||||
Manager.driver_namespace,
|
||||
assignment_driver)
|
||||
role_driver = assignment_manager_obj.default_role_driver()
|
||||
|
||||
super(RoleManager, self).__init__(role_driver)
|
||||
|
||||
|
@ -21,7 +21,6 @@ from keystone.auth import core
|
||||
from keystone.auth import schema
|
||||
from keystone.common import authorization
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import utils
|
||||
from keystone.common import validation
|
||||
from keystone.common import wsgi
|
||||
@ -82,8 +81,6 @@ def validate_issue_token_auth(auth=None):
|
||||
raise exception.SchemaValidationError(detail=msg)
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'catalog_api', 'identity_api',
|
||||
'resource_api', 'token_provider_api', 'trust_api')
|
||||
class Auth(controller.V3Controller):
|
||||
|
||||
# Note(atiwari): From V3 auth controller code we are
|
||||
|
@ -18,7 +18,7 @@ from oslo_utils import importutils
|
||||
import six
|
||||
import stevedore
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import utils
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -132,8 +132,7 @@ class AuthContext(dict):
|
||||
self[key] = val
|
||||
|
||||
|
||||
@dependency.requires('resource_api', 'trust_api')
|
||||
class AuthInfo(object):
|
||||
class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
"""Encapsulation of "auth" request."""
|
||||
|
||||
@staticmethod
|
||||
@ -353,8 +352,7 @@ class AuthInfo(object):
|
||||
self._scope_data = (domain_id, project_id, trust, unscoped)
|
||||
|
||||
|
||||
@dependency.requires('identity_api')
|
||||
class UserMFARulesValidator(object):
|
||||
class UserMFARulesValidator(provider_api.ProviderAPIMixin, object):
|
||||
"""Helper object that can validate the MFA Rules."""
|
||||
|
||||
@property
|
||||
|
@ -17,6 +17,7 @@ import collections
|
||||
|
||||
import six
|
||||
|
||||
from keystone.common import provider_api
|
||||
from keystone import exception
|
||||
|
||||
|
||||
@ -25,7 +26,7 @@ AuthHandlerResponse = collections.namedtuple(
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class AuthMethodHandler(object):
|
||||
class AuthMethodHandler(provider_api.ProviderAPIMixin, object):
|
||||
"""Abstract base class for an authentication plugin."""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -17,7 +17,7 @@ import sys
|
||||
from oslo_log import log
|
||||
import six
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
|
||||
@ -95,8 +95,7 @@ def convert_integer_to_method_list(method_int):
|
||||
return methods
|
||||
|
||||
|
||||
@dependency.requires('identity_api', 'resource_api')
|
||||
class BaseUserInfo(object):
|
||||
class BaseUserInfo(provider_api.ProviderAPIMixin, object):
|
||||
|
||||
@classmethod
|
||||
def create(cls, auth_payload, method_name):
|
||||
|
@ -19,7 +19,6 @@ import abc
|
||||
import six
|
||||
|
||||
from keystone.auth.plugins import base
|
||||
from keystone.common import dependency
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
@ -65,7 +64,6 @@ class Base(base.AuthMethodHandler):
|
||||
pass
|
||||
|
||||
|
||||
@dependency.requires('identity_api')
|
||||
class DefaultDomain(Base):
|
||||
def _authenticate(self, request):
|
||||
"""Use remote_user to look up the user in the identity backend."""
|
||||
@ -74,7 +72,6 @@ class DefaultDomain(Base):
|
||||
CONF.identity.default_domain_id)
|
||||
|
||||
|
||||
@dependency.requires('identity_api', 'resource_api')
|
||||
class Domain(Base):
|
||||
def _authenticate(self, request):
|
||||
"""Use remote_user to look up the user in the identity backend.
|
||||
|
@ -19,7 +19,6 @@ from six.moves.urllib import parse
|
||||
|
||||
from keystone.auth import plugins as auth_plugins
|
||||
from keystone.auth.plugins import base
|
||||
from keystone.common import dependency
|
||||
from keystone import exception
|
||||
from keystone.federation import constants as federation_constants
|
||||
from keystone.federation import utils
|
||||
@ -32,8 +31,6 @@ LOG = log.getLogger(__name__)
|
||||
METHOD_NAME = 'mapped'
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'federation_api', 'identity_api',
|
||||
'resource_api', 'token_provider_api', 'role_api')
|
||||
class Mapped(base.AuthMethodHandler):
|
||||
|
||||
def _get_token_ref(self, auth_payload):
|
||||
|
@ -16,14 +16,12 @@ from oslo_utils import timeutils
|
||||
|
||||
from keystone.auth.plugins import base
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
from keystone.oauth1 import core as oauth
|
||||
from keystone.oauth1 import validator
|
||||
|
||||
|
||||
@dependency.requires('oauth_api')
|
||||
class OAuth(base.AuthMethodHandler):
|
||||
def authenticate(self, request, auth_payload):
|
||||
"""Turn a signed request with an access key into a keystone token."""
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
from keystone.auth import plugins as auth_plugins
|
||||
from keystone.auth.plugins import base
|
||||
from keystone.common import dependency
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
|
||||
@ -22,7 +21,6 @@ from keystone.i18n import _
|
||||
METHOD_NAME = 'password'
|
||||
|
||||
|
||||
@dependency.requires('identity_api')
|
||||
class Password(base.AuthMethodHandler):
|
||||
|
||||
def authenticate(self, request, auth_payload):
|
||||
|
@ -17,7 +17,6 @@ import six
|
||||
|
||||
from keystone.auth.plugins import base
|
||||
from keystone.auth.plugins import mapped
|
||||
from keystone.common import dependency
|
||||
from keystone.common import wsgi
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -30,7 +29,6 @@ LOG = log.getLogger(__name__)
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
@dependency.requires('federation_api', 'identity_api', 'token_provider_api')
|
||||
class Token(base.AuthMethodHandler):
|
||||
|
||||
def _get_token_ref(self, auth_payload):
|
||||
|
@ -33,7 +33,6 @@ import six
|
||||
|
||||
from keystone.auth import plugins
|
||||
from keystone.auth.plugins import base
|
||||
from keystone.common import dependency
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
|
||||
@ -69,7 +68,6 @@ def _generate_totp_passcode(secret):
|
||||
return totp.generate(timeutils.utcnow_ts(microsecond=True)).decode('utf-8')
|
||||
|
||||
|
||||
@dependency.requires('credential_api')
|
||||
class TOTP(base.AuthMethodHandler):
|
||||
|
||||
def authenticate(self, request, auth_payload):
|
||||
|
@ -16,6 +16,7 @@ import abc
|
||||
|
||||
import six
|
||||
|
||||
from keystone.common import provider_api
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
|
||||
@ -24,7 +25,7 @@ CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class CatalogDriverBase(object):
|
||||
class CatalogDriverBase(provider_api.ProviderAPIMixin, object):
|
||||
"""Interface description for the Catalog driver."""
|
||||
|
||||
def _get_list_limit(self):
|
||||
|
@ -19,7 +19,6 @@ import sqlalchemy
|
||||
from sqlalchemy.sql import true
|
||||
|
||||
from keystone.catalog.backends import base
|
||||
from keystone.common import dependency
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import sql
|
||||
from keystone.common import utils
|
||||
@ -90,7 +89,6 @@ class Endpoint(sql.ModelBase, sql.ModelDictMixinWithExtras):
|
||||
return super(Endpoint, cls).from_dict(new_dict)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class Catalog(base.CatalogDriverBase):
|
||||
# Regions
|
||||
def list_regions(self, hints):
|
||||
|
@ -17,7 +17,6 @@ from six.moves import http_client
|
||||
|
||||
from keystone.catalog import schema
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import utils
|
||||
from keystone.common import validation
|
||||
from keystone.common import wsgi
|
||||
@ -30,7 +29,6 @@ from keystone import resource
|
||||
INTERFACES = ['public', 'internal', 'admin']
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class RegionV3(controller.V3Controller):
|
||||
collection_name = 'regions'
|
||||
member_name = 'region'
|
||||
@ -95,7 +93,6 @@ class RegionV3(controller.V3Controller):
|
||||
)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class ServiceV3(controller.V3Controller):
|
||||
collection_name = 'services'
|
||||
member_name = 'service'
|
||||
@ -142,7 +139,6 @@ class ServiceV3(controller.V3Controller):
|
||||
)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class EndpointV3(controller.V3Controller):
|
||||
collection_name = 'endpoints'
|
||||
member_name = 'endpoint'
|
||||
@ -232,7 +228,6 @@ class EndpointV3(controller.V3Controller):
|
||||
)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api', 'resource_api')
|
||||
class EndpointFilterV3Controller(controller.V3Controller):
|
||||
|
||||
def __init__(self):
|
||||
@ -303,7 +298,6 @@ class EndpointFilterV3Controller(controller.V3Controller):
|
||||
request.context_dict, projects)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api', 'resource_api')
|
||||
class EndpointGroupV3Controller(controller.V3Controller):
|
||||
collection_name = 'endpoint_groups'
|
||||
member_name = 'endpoint_group'
|
||||
@ -408,7 +402,6 @@ class EndpointGroupV3Controller(controller.V3Controller):
|
||||
filtered_endpoints)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api', 'resource_api')
|
||||
class ProjectEndpointGroupV3Controller(controller.V3Controller):
|
||||
collection_name = 'project_endpoint_groups'
|
||||
member_name = 'project_endpoint_group'
|
||||
|
@ -16,7 +16,6 @@
|
||||
"""Main entry point into the Catalog service."""
|
||||
|
||||
from keystone.common import cache
|
||||
from keystone.common import dependency
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
@ -41,8 +40,6 @@ MEMOIZE_COMPUTED_CATALOG = cache.get_memoization_decorator(
|
||||
region=COMPUTED_CATALOG_REGION)
|
||||
|
||||
|
||||
@dependency.provider('catalog_api')
|
||||
@dependency.requires('resource_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Catalog backend.
|
||||
|
||||
@ -52,6 +49,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.catalog'
|
||||
_provides_api = 'catalog_api'
|
||||
|
||||
_ENDPOINT = 'endpoint'
|
||||
_SERVICE = 'service'
|
||||
|
@ -20,8 +20,8 @@ from oslo_log import versionutils
|
||||
import six
|
||||
|
||||
from keystone.common import authorization
|
||||
from keystone.common import dependency
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import utils
|
||||
from keystone.common import wsgi
|
||||
import keystone.conf
|
||||
@ -146,8 +146,7 @@ def protected_wrapper(self, f, check_function, request, filter_attr,
|
||||
check_function(self, request, prep_info, *args, **kwargs)
|
||||
|
||||
|
||||
@dependency.requires('policy_api')
|
||||
class V2Controller(wsgi.Application):
|
||||
class V2Controller(provider_api.ProviderAPIMixin, wsgi.Application):
|
||||
"""Base controller class for Identity API v2."""
|
||||
|
||||
@staticmethod
|
||||
@ -218,8 +217,7 @@ class V2Controller(wsgi.Application):
|
||||
raise ValueError(_('Expected dict or list: %s') % type(ref))
|
||||
|
||||
|
||||
@dependency.requires('policy_api', 'token_provider_api')
|
||||
class V3Controller(wsgi.Application):
|
||||
class V3Controller(provider_api.ProviderAPIMixin, wsgi.Application):
|
||||
"""Base controller class for Identity API v3.
|
||||
|
||||
Child classes should set the ``collection_name`` and ``member_name`` class
|
||||
|
@ -25,23 +25,11 @@ See also:
|
||||
|
||||
"""
|
||||
|
||||
import traceback
|
||||
|
||||
from keystone.common import provider_api
|
||||
from keystone.i18n import _
|
||||
|
||||
|
||||
_REGISTRY = {}
|
||||
|
||||
_future_dependencies = {}
|
||||
_factories = {}
|
||||
|
||||
|
||||
def _set_provider(name, provider):
|
||||
_original_provider, where_registered = _REGISTRY.get(name, (None, None))
|
||||
if where_registered:
|
||||
raise Exception('%s already has a registered provider, at\n%s' %
|
||||
(name, ''.join(where_registered)))
|
||||
_REGISTRY[name] = (provider, traceback.format_stack())
|
||||
REGISTRY = provider_api.ProviderAPIs
|
||||
|
||||
|
||||
GET_REQUIRED = object()
|
||||
@ -49,9 +37,7 @@ GET_OPTIONAL = object()
|
||||
|
||||
|
||||
def get_provider(name, optional=GET_REQUIRED):
|
||||
if optional is GET_REQUIRED:
|
||||
return _REGISTRY[name][0]
|
||||
return _REGISTRY.get(name, (None, None))[0]
|
||||
return None
|
||||
|
||||
|
||||
class UnresolvableDependencyException(Exception):
|
||||
@ -68,163 +54,23 @@ class UnresolvableDependencyException(Exception):
|
||||
|
||||
|
||||
def provider(name):
|
||||
"""A class decorator used to register providers.
|
||||
|
||||
When ``@provider()`` is used to decorate a class, members of that class
|
||||
will register themselves as providers for the named dependency. As an
|
||||
example, In the code fragment::
|
||||
|
||||
@dependency.provider('foo_api')
|
||||
class Foo:
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
...
|
||||
|
||||
foo = Foo()
|
||||
|
||||
The object ``foo`` will be registered as a provider for ``foo_api``. No
|
||||
more than one such instance should be created; additional instances will
|
||||
replace the previous ones, possibly resulting in different instances being
|
||||
used by different consumers.
|
||||
|
||||
"""
|
||||
"""Deprecated, does nothing."""
|
||||
def wrapper(cls):
|
||||
def wrapped(init):
|
||||
def __wrapped_init__(self, *args, **kwargs):
|
||||
"""Initialize the wrapped object and add it to the registry."""
|
||||
init(self, *args, **kwargs)
|
||||
_set_provider(name, self)
|
||||
resolve_future_dependencies(__provider_name=name)
|
||||
|
||||
return __wrapped_init__
|
||||
|
||||
cls.__init__ = wrapped(cls.__init__)
|
||||
_factories[name] = cls
|
||||
return cls
|
||||
return wrapper
|
||||
|
||||
|
||||
def _process_dependencies(obj):
|
||||
# Any dependencies that can be resolved immediately are resolved.
|
||||
# Dependencies that cannot be resolved immediately are stored for
|
||||
# resolution in resolve_future_dependencies.
|
||||
|
||||
def process(obj, attr_name, unresolved_in_out):
|
||||
for dependency in getattr(obj, attr_name, []):
|
||||
if dependency not in _REGISTRY:
|
||||
# We don't know about this dependency, so save it for later.
|
||||
unresolved_in_out.setdefault(dependency, []).append(obj)
|
||||
continue
|
||||
|
||||
setattr(obj, dependency, get_provider(dependency))
|
||||
|
||||
process(obj, '_dependencies', _future_dependencies)
|
||||
|
||||
|
||||
def requires(*dependencies):
|
||||
"""A class decorator used to inject providers into consumers.
|
||||
|
||||
The required providers will be made available to instances of the decorated
|
||||
class via an attribute with the same name as the provider. For example, in
|
||||
the code fragment::
|
||||
|
||||
@dependency.requires('foo_api', 'bar_api')
|
||||
class FooBarClient:
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
...
|
||||
|
||||
client = FooBarClient()
|
||||
|
||||
The object ``client`` will have attributes named ``foo_api`` and
|
||||
``bar_api``, which are instances of the named providers.
|
||||
|
||||
Objects must not rely on the existence of these attributes until after
|
||||
``resolve_future_dependencies()`` has been called; they may not exist
|
||||
beforehand.
|
||||
|
||||
Dependencies registered via ``@required()`` must have providers; if not,
|
||||
an ``UnresolvableDependencyException`` will be raised when
|
||||
``resolve_future_dependencies()`` is called.
|
||||
|
||||
"""
|
||||
def wrapper(self, *args, **kwargs):
|
||||
"""Inject each dependency from the registry."""
|
||||
self.__wrapped_init__(*args, **kwargs)
|
||||
_process_dependencies(self)
|
||||
|
||||
"""Deprecated, does nothing."""
|
||||
def wrapped(cls):
|
||||
"""Note the required dependencies on the object for later injection.
|
||||
|
||||
The dependencies of the parent class are combined with that of the
|
||||
child class to create a new set of dependencies.
|
||||
|
||||
"""
|
||||
existing_dependencies = getattr(cls, '_dependencies', set())
|
||||
cls._dependencies = existing_dependencies.union(dependencies)
|
||||
if not hasattr(cls, '__wrapped_init__'):
|
||||
cls.__wrapped_init__ = cls.__init__
|
||||
cls.__init__ = wrapper
|
||||
return cls
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def resolve_future_dependencies(__provider_name=None):
|
||||
"""Force injection of all dependencies.
|
||||
|
||||
Before this function is called, circular dependencies may not have been
|
||||
injected. This function should be called only once, after all global
|
||||
providers are registered. If an object needs to be created after this
|
||||
call, it must not have circular dependencies.
|
||||
|
||||
If any required dependencies are unresolvable, this function will raise an
|
||||
``UnresolvableDependencyException``.
|
||||
|
||||
Outside of this module, this function should be called with no arguments;
|
||||
the optional argument, ``__provider_name`` is used internally, and should
|
||||
be treated as an implementation detail.
|
||||
|
||||
"""
|
||||
new_providers = dict()
|
||||
if __provider_name:
|
||||
# A provider was registered, so take care of any objects depending on
|
||||
# it.
|
||||
targets = _future_dependencies.pop(__provider_name, [])
|
||||
|
||||
for target in targets:
|
||||
setattr(target, __provider_name, get_provider(__provider_name))
|
||||
|
||||
return
|
||||
|
||||
# Resolve future dependencies, raises UnresolvableDependencyException if
|
||||
# there's no provider registered.
|
||||
try:
|
||||
for dependency, targets in _future_dependencies.copy().items():
|
||||
if dependency not in _REGISTRY:
|
||||
# a Class was registered that could fulfill the dependency, but
|
||||
# it has not yet been initialized.
|
||||
factory = _factories.get(dependency)
|
||||
if factory:
|
||||
provider = factory()
|
||||
new_providers[dependency] = provider
|
||||
else:
|
||||
raise UnresolvableDependencyException(dependency, targets)
|
||||
|
||||
for target in targets:
|
||||
setattr(target, dependency, get_provider(dependency))
|
||||
finally:
|
||||
_future_dependencies.clear()
|
||||
return new_providers
|
||||
"""Deprecated, does nothing."""
|
||||
return {}
|
||||
|
||||
|
||||
def reset():
|
||||
"""Reset the registry of providers.
|
||||
|
||||
This is useful for unit testing to ensure that tests don't use providers
|
||||
from previous tests.
|
||||
"""
|
||||
_REGISTRY.clear()
|
||||
_future_dependencies.clear()
|
||||
"""Deprecated, does nothing."""
|
||||
|
@ -21,6 +21,7 @@ from oslo_log import log
|
||||
import six
|
||||
import stevedore
|
||||
|
||||
from keystone.common import provider_api
|
||||
from keystone.i18n import _
|
||||
|
||||
|
||||
@ -164,12 +165,33 @@ class Manager(object):
|
||||
"""
|
||||
|
||||
driver_namespace = None
|
||||
_provides_api = None
|
||||
|
||||
def __init__(self, driver_name):
|
||||
self.driver = load_driver(self.driver_namespace, driver_name)
|
||||
if self._provides_api is None:
|
||||
raise ValueError('Programming Error: All managers must provide an '
|
||||
'API that can be referenced by other components '
|
||||
'of Keystone.')
|
||||
if driver_name is not None:
|
||||
self.driver = load_driver(self.driver_namespace, driver_name)
|
||||
self.__register_provider_api()
|
||||
|
||||
def __register_provider_api(self):
|
||||
provider_api.ProviderAPIs._register_provider_api(
|
||||
name=self._provides_api, obj=self)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Forward calls to the underlying driver."""
|
||||
"""Forward calls to the underlying driver.
|
||||
|
||||
This method checks for a provider api before forwarding.
|
||||
"""
|
||||
try:
|
||||
return getattr(provider_api.ProviderAPIs, name)
|
||||
except AttributeError:
|
||||
# NOTE(morgan): We didn't find a provider api, move on and
|
||||
# forward to the driver as expected.
|
||||
pass
|
||||
|
||||
f = getattr(self.driver, name)
|
||||
if callable(f):
|
||||
# NOTE(dstanek): only if this is callable (class or function)
|
||||
|
87
keystone/common/provider_api.py
Normal file
87
keystone/common/provider_api.py
Normal file
@ -0,0 +1,87 @@
|
||||
# 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.
|
||||
|
||||
ProviderAPIs = None
|
||||
|
||||
|
||||
def _create_provider_api_instance():
|
||||
class _ProviderAPIs(object):
|
||||
|
||||
def __init__(self):
|
||||
self.__registry = {}
|
||||
self.__locked = False
|
||||
|
||||
self.__iter__ = self.__registry.__iter__
|
||||
self.__getitem__ = self.__registry.__getitem__
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Do attr lookup."""
|
||||
try:
|
||||
return self.__registry[item]
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
"'ProviderAPIs' has no attribute %s" % item)
|
||||
|
||||
def _register_provider_api(self, name, obj):
|
||||
"""Register an instance of a class as a provider api."""
|
||||
if name == 'driver':
|
||||
raise ValueError('A provider may not be named "driver".')
|
||||
|
||||
if self.__locked:
|
||||
raise RuntimeError(
|
||||
'Programming Error: The provider api registry has been '
|
||||
'locked (post configuration). Ensure all provider api '
|
||||
'managers are instantiated before locking.')
|
||||
|
||||
if name in self.__registry:
|
||||
raise DuplicateProviderError(
|
||||
'`%(name)s` has already been registered as an api '
|
||||
'provider by `%(prov)r`' % {'name': name,
|
||||
'prov': self.__registry[name]})
|
||||
self.__registry[name] = obj
|
||||
|
||||
def _clear_registry_instances(self):
|
||||
"""ONLY USED FOR TESTING."""
|
||||
self.__registry.clear()
|
||||
self.__locked = False
|
||||
|
||||
def lock_provider_registry(self):
|
||||
self.__locked = True
|
||||
|
||||
global ProviderAPIs
|
||||
if ProviderAPIs is None:
|
||||
ProviderAPIs = _ProviderAPIs()
|
||||
else:
|
||||
raise RuntimeError('Programming Error: ProviderAPIs object cannot be '
|
||||
'instatiated more than one time. It is meant to '
|
||||
'act as a singleton.')
|
||||
|
||||
|
||||
class DuplicateProviderError(Exception):
|
||||
"""Attempting to register a duplicate API provider."""
|
||||
|
||||
|
||||
class ProviderAPIMixin(object):
|
||||
"""Allow referencing provider apis on self via __getattr__.
|
||||
|
||||
Be sure this class is first in the class definition for inheritance.
|
||||
"""
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Magic getattr method."""
|
||||
try:
|
||||
return getattr(ProviderAPIs, item)
|
||||
except AttributeError:
|
||||
return self.__getattribute__(item)
|
||||
|
||||
|
||||
_create_provider_api_instance()
|
@ -18,7 +18,7 @@ import hashlib
|
||||
from oslo_log import log
|
||||
|
||||
from keystone.auth import core
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
from keystone.federation import constants as federation_constants
|
||||
@ -30,9 +30,7 @@ CONF = keystone.conf.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'federation_api',
|
||||
'identity_api', 'resource_api')
|
||||
class TokenlessAuthHelper(object):
|
||||
class TokenlessAuthHelper(provider_api.ProviderAPIMixin, object):
|
||||
def __init__(self, env):
|
||||
"""A init class for TokenlessAuthHelper.
|
||||
|
||||
|
@ -43,7 +43,7 @@ from six.moves import http_client
|
||||
|
||||
from keystone.common import authorization
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import utils
|
||||
from keystone.common import wsgi
|
||||
from keystone import exception
|
||||
@ -53,11 +53,8 @@ from keystone.token import controllers as token_controllers
|
||||
CRED_TYPE_EC2 = 'ec2'
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'catalog_api', 'credential_api',
|
||||
'identity_api', 'resource_api', 'role_api',
|
||||
'token_provider_api')
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Ec2ControllerCommon(object):
|
||||
class Ec2ControllerCommon(provider_api.ProviderAPIMixin, object):
|
||||
def check_signature(self, creds_ref, credentials):
|
||||
signer = ec2_utils.Ec2Signer(creds_ref['secret'])
|
||||
signature = signer.generate(credentials)
|
||||
@ -270,7 +267,6 @@ class Ec2ControllerCommon(object):
|
||||
headers=headers)
|
||||
|
||||
|
||||
@dependency.requires('policy_api', 'token_provider_api')
|
||||
class Ec2Controller(Ec2ControllerCommon, controller.V2Controller):
|
||||
|
||||
@controller.v2_ec2_deprecated
|
||||
@ -358,7 +354,6 @@ class Ec2Controller(Ec2ControllerCommon, controller.V2Controller):
|
||||
raise exception.Forbidden(_('Credential belongs to another user'))
|
||||
|
||||
|
||||
@dependency.requires('policy_api', 'token_provider_api')
|
||||
class Ec2ControllerV3(Ec2ControllerCommon, controller.V3Controller):
|
||||
|
||||
collection_name = 'credentials'
|
||||
|
@ -17,14 +17,12 @@ import hashlib
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import validation
|
||||
from keystone.credential import schema
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
|
||||
|
||||
@dependency.requires('credential_api')
|
||||
class CredentialV3(controller.V3Controller):
|
||||
collection_name = 'credentials'
|
||||
member_name = 'credential'
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
import json
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
@ -26,8 +25,6 @@ from keystone import exception
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
@dependency.provider('credential_api')
|
||||
@dependency.requires('credential_provider_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Credential backend.
|
||||
|
||||
@ -37,6 +34,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.credential'
|
||||
_provides_api = 'credential_api'
|
||||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.credential.driver)
|
||||
|
@ -10,7 +10,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
|
||||
@ -18,10 +17,10 @@ import keystone.conf
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
@dependency.provider('credential_provider_api')
|
||||
class Manager(manager.Manager):
|
||||
|
||||
driver_namespace = 'keystone.credential.provider'
|
||||
_provides_api = 'credential_provider_api'
|
||||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.credential.provider)
|
||||
|
@ -13,11 +13,9 @@
|
||||
# under the License.
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone import notifications
|
||||
|
||||
|
||||
@dependency.requires('policy_api', 'catalog_api', 'endpoint_policy_api')
|
||||
class EndpointPolicyV3Controller(controller.V3Controller):
|
||||
collection_name = 'endpoints'
|
||||
member_name = 'endpoint'
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -25,8 +24,6 @@ CONF = keystone.conf.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
@dependency.provider('endpoint_policy_api')
|
||||
@dependency.requires('catalog_api', 'policy_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Endpoint Policy backend.
|
||||
|
||||
@ -36,6 +33,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.endpoint_policy'
|
||||
_provides_api = 'endpoint_policy_api'
|
||||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.endpoint_policy.driver)
|
||||
|
@ -22,7 +22,6 @@ import webob
|
||||
|
||||
from keystone.auth import controllers as auth_controllers
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import utils as k_utils
|
||||
from keystone.common import validation
|
||||
from keystone.common import wsgi
|
||||
@ -49,7 +48,6 @@ class _ControllerBase(controller.V3Controller):
|
||||
return super(_ControllerBase, cls).base_url(context, path=path)
|
||||
|
||||
|
||||
@dependency.requires('federation_api')
|
||||
class IdentityProvider(_ControllerBase):
|
||||
"""Identity Provider representation."""
|
||||
|
||||
@ -128,7 +126,6 @@ class IdentityProvider(_ControllerBase):
|
||||
return IdentityProvider.wrap_member(request.context_dict, idp_ref)
|
||||
|
||||
|
||||
@dependency.requires('federation_api')
|
||||
class FederationProtocol(_ControllerBase):
|
||||
"""A federation protocol representation.
|
||||
|
||||
@ -219,7 +216,6 @@ class FederationProtocol(_ControllerBase):
|
||||
self.federation_api.delete_protocol(idp_id, protocol_id)
|
||||
|
||||
|
||||
@dependency.requires('federation_api')
|
||||
class MappingController(_ControllerBase):
|
||||
collection_name = 'mappings'
|
||||
member_name = 'mapping'
|
||||
@ -257,7 +253,6 @@ class MappingController(_ControllerBase):
|
||||
return MappingController.wrap_member(request.context_dict, mapping_ref)
|
||||
|
||||
|
||||
@dependency.requires('federation_api')
|
||||
class Auth(auth_controllers.Auth):
|
||||
|
||||
def _get_sso_origin_host(self, request):
|
||||
@ -432,7 +427,6 @@ class Auth(auth_controllers.Auth):
|
||||
headers=headers)
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'resource_api')
|
||||
class DomainV3(controller.V3Controller):
|
||||
collection_name = 'domains'
|
||||
member_name = 'domain'
|
||||
@ -462,7 +456,6 @@ class DomainV3(controller.V3Controller):
|
||||
return DomainV3.wrap_collection(request.context_dict, domains)
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'resource_api')
|
||||
class ProjectAssignmentV3(controller.V3Controller):
|
||||
collection_name = 'projects'
|
||||
member_name = 'project'
|
||||
@ -493,7 +486,6 @@ class ProjectAssignmentV3(controller.V3Controller):
|
||||
projects)
|
||||
|
||||
|
||||
@dependency.requires('federation_api')
|
||||
class ServiceProvider(_ControllerBase):
|
||||
"""Service Provider representation."""
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
import uuid
|
||||
|
||||
from keystone.common import cache
|
||||
from keystone.common import dependency
|
||||
from keystone.common import extension
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
@ -44,8 +43,6 @@ extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
|
||||
extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
|
||||
|
||||
|
||||
@dependency.provider('federation_api')
|
||||
@dependency.requires('resource_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Federation backend.
|
||||
|
||||
@ -55,6 +52,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.federation'
|
||||
_provides_api = 'federation_api'
|
||||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.federation.driver)
|
||||
|
@ -17,7 +17,6 @@
|
||||
from oslo_log import log
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import validation
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -29,7 +28,6 @@ CONF = keystone.conf.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
@dependency.requires('identity_api')
|
||||
class UserV3(controller.V3Controller):
|
||||
collection_name = 'users'
|
||||
member_name = 'user'
|
||||
@ -139,7 +137,6 @@ class UserV3(controller.V3Controller):
|
||||
'Error when changing user password: %s') % e)
|
||||
|
||||
|
||||
@dependency.requires('identity_api')
|
||||
class GroupV3(controller.V3Controller):
|
||||
collection_name = 'groups'
|
||||
member_name = 'group'
|
||||
|
@ -28,9 +28,9 @@ from pycadf import reason
|
||||
from keystone import assignment # TODO(lbragstad): Decouple this dependency
|
||||
from keystone.common import cache
|
||||
from keystone.common import clean
|
||||
from keystone.common import dependency
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import manager
|
||||
from keystone.common import provider_api
|
||||
from keystone.common.validation import validators
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -62,8 +62,7 @@ REGISTRATION_ATTEMPTS = 10
|
||||
SQL_DRIVER = 'SQL'
|
||||
|
||||
|
||||
@dependency.requires('domain_config_api', 'resource_api')
|
||||
class DomainConfigs(dict):
|
||||
class DomainConfigs(provider_api.ProviderAPIMixin, dict):
|
||||
"""Discover, store and provide access to domain specific configs.
|
||||
|
||||
The setup_domain_drivers() call will be made via the wrapper from
|
||||
@ -436,9 +435,6 @@ def exception_translated(exception_type):
|
||||
|
||||
|
||||
@notifications.listener
|
||||
@dependency.provider('identity_api')
|
||||
@dependency.requires('assignment_api', 'credential_api', 'id_mapping_api',
|
||||
'resource_api', 'shadow_users_api', 'federation_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Identity backend.
|
||||
|
||||
@ -479,6 +475,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.identity'
|
||||
_provides_api = 'identity_api'
|
||||
|
||||
_USER = 'user'
|
||||
_GROUP = 'group'
|
||||
@ -1423,11 +1420,11 @@ class Manager(manager.Manager):
|
||||
return user_dict
|
||||
|
||||
|
||||
@dependency.provider('id_mapping_api')
|
||||
class MappingManager(manager.Manager):
|
||||
"""Default pivot point for the ID Mapping backend."""
|
||||
|
||||
driver_namespace = 'keystone.identity.id_mapping'
|
||||
_provides_api = 'id_mapping_api'
|
||||
|
||||
def __init__(self):
|
||||
super(MappingManager, self).__init__(CONF.identity_mapping.driver)
|
||||
@ -1475,11 +1472,11 @@ class MappingManager(manager.Manager):
|
||||
ID_MAPPING_REGION.invalidate()
|
||||
|
||||
|
||||
@dependency.provider('shadow_users_api')
|
||||
class ShadowUsersManager(manager.Manager):
|
||||
"""Default pivot point for the Shadow Users backend."""
|
||||
|
||||
driver_namespace = 'keystone.identity.shadow_users'
|
||||
_provides_api = 'shadow_users_api'
|
||||
|
||||
def __init__(self):
|
||||
shadow_driver = CONF.shadow_users.driver
|
||||
|
@ -18,7 +18,6 @@ import abc
|
||||
|
||||
import six
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -27,11 +26,11 @@ from keystone import exception
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
@dependency.provider('id_generator_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the identifier generator backend."""
|
||||
|
||||
driver_namespace = 'keystone.identity.id_generator'
|
||||
_provides_api = 'id_generator_api'
|
||||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.identity_mapping.generator)
|
||||
|
@ -16,11 +16,12 @@ import abc
|
||||
|
||||
import six
|
||||
|
||||
from keystone.common import provider_api
|
||||
from keystone import exception
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class MappingDriverBase(object):
|
||||
class MappingDriverBase(provider_api.ProviderAPIMixin, object):
|
||||
"""Interface description for an ID Mapping driver."""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
@ -12,7 +12,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import sql
|
||||
from keystone.identity.mapping_backends import base
|
||||
from keystone.identity.mapping_backends import mapping as identity_mapping
|
||||
@ -35,7 +34,6 @@ class IDMapping(sql.ModelBase, sql.ModelDictMixin):
|
||||
sql.UniqueConstraint('domain_id', 'local_id', 'entity_type'),)
|
||||
|
||||
|
||||
@dependency.requires('id_generator_api')
|
||||
class Mapping(base.MappingDriverBase):
|
||||
|
||||
def get_public_id(self, local_entity):
|
||||
|
@ -15,7 +15,7 @@ from oslo_log import log
|
||||
|
||||
from keystone.common import authorization
|
||||
from keystone.common import context
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import tokenless_auth
|
||||
from keystone.common import wsgi
|
||||
import keystone.conf
|
||||
@ -32,8 +32,8 @@ LOG = log.getLogger(__name__)
|
||||
__all__ = ('AuthContextMiddleware',)
|
||||
|
||||
|
||||
@dependency.requires('token_provider_api')
|
||||
class AuthContextMiddleware(auth_token.BaseAuthProtocol):
|
||||
class AuthContextMiddleware(provider_api.ProviderAPIMixin,
|
||||
auth_token.BaseAuthProtocol):
|
||||
"""Build the authentication context from the request auth token."""
|
||||
|
||||
kwargs_to_fetch_token = True
|
||||
|
@ -30,7 +30,7 @@ from pycadf import eventfactory
|
||||
from pycadf import reason
|
||||
from pycadf import resource
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import utils
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -655,8 +655,7 @@ def send_saml_audit_notification(action, request, user_id, group_ids,
|
||||
_send_audit_notification(action, initiator, outcome, target, event_type)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
class _CatalogHelperObj(object):
|
||||
class _CatalogHelperObj(provider_api.ProviderAPIMixin, object):
|
||||
"""A helper object to allow lookups of identity service id."""
|
||||
|
||||
|
||||
|
@ -22,7 +22,6 @@ from six.moves.urllib import parse as urlparse
|
||||
|
||||
from keystone.common import authorization
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import validation
|
||||
from keystone.common import wsgi
|
||||
import keystone.conf
|
||||
@ -49,7 +48,6 @@ def _emit_user_oauth_consumer_token_invalidate(payload):
|
||||
)
|
||||
|
||||
|
||||
@dependency.requires('oauth_api', 'token_provider_api')
|
||||
class ConsumerCrudV3(controller.V3Controller):
|
||||
collection_name = 'consumers'
|
||||
member_name = 'consumer'
|
||||
@ -102,7 +100,6 @@ class ConsumerCrudV3(controller.V3Controller):
|
||||
)
|
||||
|
||||
|
||||
@dependency.requires('oauth_api')
|
||||
class AccessTokenCrudV3(controller.V3Controller):
|
||||
collection_name = 'access_tokens'
|
||||
member_name = 'access_token'
|
||||
@ -172,7 +169,6 @@ class AccessTokenCrudV3(controller.V3Controller):
|
||||
return formatted_entity
|
||||
|
||||
|
||||
@dependency.requires('oauth_api', 'role_api')
|
||||
class AccessTokenRolesV3(controller.V3Controller):
|
||||
collection_name = 'roles'
|
||||
member_name = 'role'
|
||||
@ -212,8 +208,6 @@ class AccessTokenRolesV3(controller.V3Controller):
|
||||
return formatted_entity
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'oauth_api',
|
||||
'resource_api', 'token_provider_api')
|
||||
class OAuthControllerV3(controller.V3Controller):
|
||||
collection_name = 'not_used'
|
||||
member_name = 'not_used'
|
||||
|
@ -22,7 +22,6 @@ import oauthlib.common
|
||||
from oauthlib import oauth1
|
||||
from oslo_log import log
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import extension
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
@ -123,7 +122,6 @@ def validate_oauth_params(query_string):
|
||||
raise exception.ValidationError(message=msg)
|
||||
|
||||
|
||||
@dependency.provider('oauth_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the OAuth1 backend.
|
||||
|
||||
@ -133,6 +131,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.oauth1'
|
||||
_provides_api = 'oauth_api'
|
||||
|
||||
_ACCESS_TOKEN = "OS-OAUTH1:access_token"
|
||||
_REQUEST_TOKEN = "OS-OAUTH1:request_token"
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import six
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
from keystone import exception
|
||||
from keystone.oauth1.backends import base
|
||||
from keystone.oauth1 import core as oauth1
|
||||
@ -25,8 +25,7 @@ from keystone.oauth1 import core as oauth1
|
||||
METHOD_NAME = 'oauth_validator'
|
||||
|
||||
|
||||
@dependency.requires('oauth_api')
|
||||
class OAuthValidator(oauth1.RequestValidator):
|
||||
class OAuthValidator(provider_api.ProviderAPIMixin, oauth1.RequestValidator):
|
||||
|
||||
# TODO(mhu) set as option probably?
|
||||
@property
|
||||
|
@ -16,7 +16,6 @@ from oslo_log import versionutils
|
||||
import six
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import validation
|
||||
from keystone.policy import schema
|
||||
|
||||
@ -31,7 +30,6 @@ def policy_deprecated(f):
|
||||
return wrapper()
|
||||
|
||||
|
||||
@dependency.requires('policy_api')
|
||||
class PolicyV3(controller.V3Controller):
|
||||
collection_name = 'policies'
|
||||
member_name = 'policy'
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
"""Main entry point into the Policy service."""
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -24,7 +23,6 @@ from keystone import notifications
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
@dependency.provider('policy_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Policy backend.
|
||||
|
||||
@ -34,6 +32,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.policy'
|
||||
_provides_api = 'policy_api'
|
||||
|
||||
_POLICY = 'policy'
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
from six.moves import http_client
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import validation
|
||||
from keystone.common import wsgi
|
||||
import keystone.conf
|
||||
@ -30,7 +29,6 @@ from keystone.resource import schema
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
@dependency.requires('resource_api')
|
||||
class DomainV3(controller.V3Controller):
|
||||
collection_name = 'domains'
|
||||
member_name = 'domain'
|
||||
@ -76,8 +74,6 @@ class DomainV3(controller.V3Controller):
|
||||
)
|
||||
|
||||
|
||||
@dependency.requires('domain_config_api')
|
||||
@dependency.requires('resource_api')
|
||||
class DomainConfigV3(controller.V3Controller):
|
||||
member_name = 'config'
|
||||
|
||||
@ -151,7 +147,6 @@ class DomainConfigV3(controller.V3Controller):
|
||||
return {self.member_name: ref}
|
||||
|
||||
|
||||
@dependency.requires('resource_api')
|
||||
class ProjectV3(controller.V3Controller):
|
||||
collection_name = 'projects'
|
||||
member_name = 'project'
|
||||
@ -266,7 +261,6 @@ class ProjectV3(controller.V3Controller):
|
||||
initiator=request.audit_initiator)
|
||||
|
||||
|
||||
@dependency.requires('resource_api')
|
||||
class ProjectTagV3(controller.V3Controller):
|
||||
collection_name = 'projects'
|
||||
member_name = 'tags'
|
||||
|
@ -18,7 +18,6 @@ import six
|
||||
from keystone import assignment
|
||||
from keystone.common import cache
|
||||
from keystone.common import clean
|
||||
from keystone.common import dependency
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import manager
|
||||
from keystone.common import utils
|
||||
@ -37,9 +36,6 @@ MEMOIZE = cache.get_memoization_decorator(group='resource')
|
||||
TAG_SEARCH_FILTERS = ('tags', 'tags-any', 'not-tags', 'not-tags-any')
|
||||
|
||||
|
||||
@dependency.provider('resource_api')
|
||||
@dependency.requires('assignment_api', 'credential_api', 'domain_config_api',
|
||||
'identity_api', 'trust_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Resource backend.
|
||||
|
||||
@ -49,6 +45,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.resource'
|
||||
_provides_api = 'resource_api'
|
||||
|
||||
_DOMAIN = 'domain'
|
||||
_PROJECT = 'project'
|
||||
@ -60,7 +57,9 @@ class Manager(manager.Manager):
|
||||
# SQL Identity in some form. Even if SQL Identity is not used, there
|
||||
# is almost no reason to have non-SQL Resource. Keystone requires
|
||||
# SQL in a number of ways, this simply codifies it plainly for resource
|
||||
# the driver_name = None simply implies we don't need to load a driver.
|
||||
self.driver = resource_sql.Resource()
|
||||
super(Manager, self).__init__(driver_name=None)
|
||||
|
||||
def _get_hierarchy_depth(self, parents_list):
|
||||
return len(parents_list) + 1
|
||||
@ -946,7 +945,6 @@ class Manager(manager.Manager):
|
||||
MEMOIZE_CONFIG = cache.get_memoization_decorator(group='domain_config')
|
||||
|
||||
|
||||
@dependency.provider('domain_config_api')
|
||||
class DomainConfigManager(manager.Manager):
|
||||
"""Default pivot point for the Domain Config backend."""
|
||||
|
||||
@ -960,6 +958,7 @@ class DomainConfigManager(manager.Manager):
|
||||
# the identity manager are supported.
|
||||
|
||||
driver_namespace = 'keystone.resource.domain_config'
|
||||
_provides_api = 'domain_config_api'
|
||||
|
||||
# We explicitly state each whitelisted option instead of pulling all ldap
|
||||
# options from CONF and selectively pruning them to prevent a security
|
||||
|
@ -13,12 +13,10 @@
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
|
||||
|
||||
@dependency.requires('revoke_api')
|
||||
class RevokeController(controller.V3Controller):
|
||||
@controller.protected()
|
||||
def list_revoke_events(self, request):
|
||||
|
@ -13,7 +13,6 @@
|
||||
"""Main entry point into the Revoke service."""
|
||||
|
||||
from keystone.common import cache
|
||||
from keystone.common import dependency
|
||||
from keystone.common import extension
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
@ -53,7 +52,6 @@ MEMOIZE = cache.get_memoization_decorator(
|
||||
region=REVOKE_REGION)
|
||||
|
||||
|
||||
@dependency.provider('revoke_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Revoke backend.
|
||||
|
||||
@ -65,6 +63,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.revoke'
|
||||
_provides_api = 'revoke_api'
|
||||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.revoke.driver)
|
||||
|
@ -14,6 +14,7 @@ from keystone import assignment
|
||||
from keystone import auth
|
||||
from keystone import catalog
|
||||
from keystone.common import cache
|
||||
from keystone.common import provider_api
|
||||
from keystone import credential
|
||||
from keystone import endpoint_policy
|
||||
from keystone import federation
|
||||
@ -23,6 +24,7 @@ from keystone import policy
|
||||
from keystone import resource
|
||||
from keystone import revoke
|
||||
from keystone import token
|
||||
from keystone.token import persistence
|
||||
from keystone import trust
|
||||
|
||||
|
||||
@ -37,29 +39,21 @@ def load_backends():
|
||||
cache.configure_cache(region=identity.ID_MAPPING_REGION)
|
||||
cache.configure_invalidation_region()
|
||||
|
||||
# NOTE(knikolla): The assignment manager must be instantiated before the
|
||||
# resource manager. The current dictionary ordering ensures that.
|
||||
DRIVERS = dict(
|
||||
assignment_api=assignment.Manager(),
|
||||
catalog_api=catalog.Manager(),
|
||||
credential_api=credential.Manager(),
|
||||
credential_provider_api=credential.provider.Manager(),
|
||||
domain_config_api=resource.DomainConfigManager(),
|
||||
endpoint_policy_api=endpoint_policy.Manager(),
|
||||
federation_api=federation.Manager(),
|
||||
id_generator_api=identity.generator.Manager(),
|
||||
id_mapping_api=identity.MappingManager(),
|
||||
identity_api=identity.Manager(),
|
||||
shadow_users_api=identity.ShadowUsersManager(),
|
||||
oauth_api=oauth1.Manager(),
|
||||
policy_api=policy.Manager(),
|
||||
resource_api=resource.Manager(),
|
||||
revoke_api=revoke.Manager(),
|
||||
role_api=assignment.RoleManager(),
|
||||
token_api=token.persistence.Manager(),
|
||||
trust_api=trust.Manager(),
|
||||
token_provider_api=token.provider.Manager())
|
||||
managers = [assignment.Manager, catalog.Manager, credential.Manager,
|
||||
credential.provider.Manager, resource.DomainConfigManager,
|
||||
endpoint_policy.Manager, federation.Manager,
|
||||
identity.generator.Manager, identity.MappingManager,
|
||||
identity.Manager, identity.ShadowUsersManager, oauth1.Manager,
|
||||
policy.Manager, resource.Manager, revoke.Manager,
|
||||
assignment.RoleManager, trust.Manager, token.provider.Manager,
|
||||
persistence.PersistenceManager]
|
||||
|
||||
drivers = {d._provides_api: d() for d in managers}
|
||||
|
||||
# NOTE(morgan): lock the APIs, these should only ever be instantiated
|
||||
# before running keystone.
|
||||
provider_api.ProviderAPIs.lock_provider_registry()
|
||||
|
||||
auth.core.load_auth_methods()
|
||||
|
||||
return DRIVERS
|
||||
return drivers
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
from oslo_log import log
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import sql
|
||||
import keystone.conf
|
||||
from keystone.server import backends
|
||||
@ -47,5 +46,4 @@ def setup_backends(load_extra_backends_fn=lambda: {},
|
||||
drivers = backends.load_backends()
|
||||
drivers.update(load_extra_backends_fn())
|
||||
res = startup_application_fn()
|
||||
drivers.update(dependency.resolve_future_dependencies())
|
||||
return drivers, res
|
||||
|
@ -1,238 +0,0 @@
|
||||
# Copyright 2012 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 uuid
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.tests import unit
|
||||
|
||||
|
||||
class TestDependencyInjection(unit.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestDependencyInjection, self).setUp()
|
||||
dependency.reset()
|
||||
self.addCleanup(dependency.reset)
|
||||
|
||||
def test_dependency_injection(self):
|
||||
class Interface(object):
|
||||
def do_work(self):
|
||||
assert False
|
||||
|
||||
@dependency.provider('first_api')
|
||||
class FirstImplementation(Interface):
|
||||
def do_work(self):
|
||||
return True
|
||||
|
||||
@dependency.provider('second_api')
|
||||
class SecondImplementation(Interface):
|
||||
def do_work(self):
|
||||
return True
|
||||
|
||||
@dependency.requires('first_api', 'second_api')
|
||||
class Consumer(object):
|
||||
def do_work_with_dependencies(self):
|
||||
assert self.first_api.do_work()
|
||||
assert self.second_api.do_work()
|
||||
|
||||
# initialize dependency providers
|
||||
first_api = FirstImplementation()
|
||||
second_api = SecondImplementation()
|
||||
|
||||
# ... sometime later, initialize a dependency consumer
|
||||
consumer = Consumer()
|
||||
|
||||
# the expected dependencies should be available to the consumer
|
||||
self.assertIs(consumer.first_api, first_api)
|
||||
self.assertIs(consumer.second_api, second_api)
|
||||
self.assertIsInstance(consumer.first_api, Interface)
|
||||
self.assertIsInstance(consumer.second_api, Interface)
|
||||
consumer.do_work_with_dependencies()
|
||||
|
||||
def test_dependency_provider_configuration(self):
|
||||
@dependency.provider('api')
|
||||
class Configurable(object):
|
||||
def __init__(self, value=None):
|
||||
self.value = value
|
||||
|
||||
def get_value(self):
|
||||
return self.value
|
||||
|
||||
@dependency.requires('api')
|
||||
class Consumer(object):
|
||||
def get_value(self):
|
||||
return self.api.get_value()
|
||||
|
||||
# initialize dependency providers
|
||||
api = Configurable(value=True)
|
||||
|
||||
# ... sometime later, initialize a dependency consumer
|
||||
consumer = Consumer()
|
||||
|
||||
# the expected dependencies should be available to the consumer
|
||||
self.assertIs(consumer.api, api)
|
||||
self.assertIsInstance(consumer.api, Configurable)
|
||||
self.assertTrue(consumer.get_value())
|
||||
|
||||
def test_dependency_consumer_configuration(self):
|
||||
@dependency.provider('api')
|
||||
class Provider(object):
|
||||
def get_value(self):
|
||||
return True
|
||||
|
||||
@dependency.requires('api')
|
||||
class Configurable(object):
|
||||
def __init__(self, value=None):
|
||||
self.value = value
|
||||
|
||||
def get_value(self):
|
||||
if self.value:
|
||||
return self.api.get_value()
|
||||
|
||||
# initialize dependency providers
|
||||
api = Provider()
|
||||
|
||||
# ... sometime later, initialize a dependency consumer
|
||||
consumer = Configurable(value=True)
|
||||
|
||||
# the expected dependencies should be available to the consumer
|
||||
self.assertIs(consumer.api, api)
|
||||
self.assertIsInstance(consumer.api, Provider)
|
||||
self.assertTrue(consumer.get_value())
|
||||
|
||||
def test_inherited_dependency(self):
|
||||
class Interface(object):
|
||||
def do_work(self):
|
||||
assert False
|
||||
|
||||
@dependency.provider('first_api')
|
||||
class FirstImplementation(Interface):
|
||||
def do_work(self):
|
||||
return True
|
||||
|
||||
@dependency.provider('second_api')
|
||||
class SecondImplementation(Interface):
|
||||
def do_work(self):
|
||||
return True
|
||||
|
||||
@dependency.requires('first_api')
|
||||
class ParentConsumer(object):
|
||||
def do_work_with_dependencies(self):
|
||||
assert self.first_api.do_work()
|
||||
|
||||
@dependency.requires('second_api')
|
||||
class ChildConsumer(ParentConsumer):
|
||||
def do_work_with_dependencies(self):
|
||||
assert self.second_api.do_work()
|
||||
super(ChildConsumer, self).do_work_with_dependencies()
|
||||
|
||||
# initialize dependency providers
|
||||
first_api = FirstImplementation()
|
||||
second_api = SecondImplementation()
|
||||
|
||||
# ... sometime later, initialize a dependency consumer
|
||||
consumer = ChildConsumer()
|
||||
|
||||
# dependencies should be naturally inherited
|
||||
self.assertEqual(
|
||||
set(['first_api']),
|
||||
ParentConsumer._dependencies)
|
||||
self.assertEqual(
|
||||
set(['first_api', 'second_api']),
|
||||
ChildConsumer._dependencies)
|
||||
self.assertEqual(
|
||||
set(['first_api', 'second_api']),
|
||||
consumer._dependencies)
|
||||
|
||||
# the expected dependencies should be available to the consumer
|
||||
self.assertIs(consumer.first_api, first_api)
|
||||
self.assertIs(consumer.second_api, second_api)
|
||||
self.assertIsInstance(consumer.first_api, Interface)
|
||||
self.assertIsInstance(consumer.second_api, Interface)
|
||||
consumer.do_work_with_dependencies()
|
||||
|
||||
def test_unresolvable_dependency(self):
|
||||
@dependency.requires(uuid.uuid4().hex)
|
||||
class Consumer(object):
|
||||
pass
|
||||
|
||||
def for_test():
|
||||
Consumer()
|
||||
dependency.resolve_future_dependencies()
|
||||
|
||||
self.assertRaises(dependency.UnresolvableDependencyException, for_test)
|
||||
|
||||
def test_circular_dependency(self):
|
||||
p1_name = uuid.uuid4().hex
|
||||
p2_name = uuid.uuid4().hex
|
||||
|
||||
@dependency.provider(p1_name)
|
||||
@dependency.requires(p2_name)
|
||||
class P1(object):
|
||||
pass
|
||||
|
||||
@dependency.provider(p2_name)
|
||||
@dependency.requires(p1_name)
|
||||
class P2(object):
|
||||
pass
|
||||
|
||||
p1 = P1()
|
||||
p2 = P2()
|
||||
|
||||
dependency.resolve_future_dependencies()
|
||||
|
||||
self.assertIs(getattr(p1, p2_name), p2)
|
||||
self.assertIs(getattr(p2, p1_name), p1)
|
||||
|
||||
def test_reset(self):
|
||||
# Can reset the registry of providers.
|
||||
|
||||
p_id = uuid.uuid4().hex
|
||||
|
||||
@dependency.provider(p_id)
|
||||
class P(object):
|
||||
pass
|
||||
|
||||
p_inst = P()
|
||||
|
||||
self.assertIs(dependency.get_provider(p_id), p_inst)
|
||||
|
||||
dependency.reset()
|
||||
|
||||
self.assertFalse(dependency._REGISTRY)
|
||||
|
||||
def test_get_provider(self):
|
||||
# Can get the instance of a provider using get_provider
|
||||
|
||||
provider_name = uuid.uuid4().hex
|
||||
|
||||
@dependency.provider(provider_name)
|
||||
class P(object):
|
||||
pass
|
||||
|
||||
provider_instance = P()
|
||||
retrieved_provider_instance = dependency.get_provider(provider_name)
|
||||
self.assertIs(provider_instance, retrieved_provider_instance)
|
||||
|
||||
def test_get_provider_not_provided_error(self):
|
||||
# If no provider and provider is required then fails.
|
||||
|
||||
provider_name = uuid.uuid4().hex
|
||||
self.assertRaises(KeyError, dependency.get_provider, provider_name)
|
||||
|
||||
def test_get_provider_not_provided_optional(self):
|
||||
# If no provider and provider is optional then returns None.
|
||||
|
||||
provider_name = uuid.uuid4().hex
|
||||
self.assertIsNone(dependency.get_provider(provider_name,
|
||||
dependency.GET_OPTIONAL))
|
64
keystone/tests/unit/common/test_provider_api.py
Normal file
64
keystone/tests/unit/common/test_provider_api.py
Normal file
@ -0,0 +1,64 @@
|
||||
# Copyright 2012 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 uuid
|
||||
|
||||
from keystone.common import manager
|
||||
from keystone.common import provider_api
|
||||
from keystone.tests import unit
|
||||
|
||||
|
||||
class TestProviderAPIRegistry(unit.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestProviderAPIRegistry, self).setUp()
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
self.addCleanup(provider_api.ProviderAPIs._clear_registry_instances)
|
||||
|
||||
def _create_manager_instance(self, provides_api=None):
|
||||
provides_api = provides_api or '%s_api' % uuid.uuid4().hex
|
||||
|
||||
class TestManager(manager.Manager):
|
||||
_provides_api = provides_api
|
||||
driver_namespace = '_TEST_NOTHING'
|
||||
|
||||
return TestManager(driver_name=None)
|
||||
|
||||
def test_registry_lock(self):
|
||||
provider_api.ProviderAPIs.lock_provider_registry()
|
||||
self.assertRaises(RuntimeError, self._create_manager_instance)
|
||||
|
||||
def test_registry_duplicate(self):
|
||||
test_manager = self._create_manager_instance()
|
||||
self.assertRaises(
|
||||
provider_api.DuplicateProviderError,
|
||||
self._create_manager_instance,
|
||||
provides_api=test_manager._provides_api)
|
||||
|
||||
def test_provider_api_mixin(self):
|
||||
test_manager = self._create_manager_instance()
|
||||
|
||||
class Testing(provider_api.ProviderAPIMixin, object):
|
||||
pass
|
||||
|
||||
instance = Testing()
|
||||
self.assertIs(test_manager, getattr(instance,
|
||||
test_manager._provides_api))
|
||||
|
||||
def test_manager_api_reference(self):
|
||||
manager = self._create_manager_instance()
|
||||
second_manager = self._create_manager_instance()
|
||||
self.assertIs(second_manager, getattr(manager,
|
||||
second_manager._provides_api))
|
||||
self.assertIs(manager, getattr(second_manager,
|
||||
manager._provides_api))
|
@ -41,7 +41,7 @@ import testtools
|
||||
from testtools import testcase
|
||||
|
||||
from keystone.common import context
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import request
|
||||
from keystone.common import sql
|
||||
import keystone.conf
|
||||
@ -677,7 +677,7 @@ class TestCase(BaseTestCase):
|
||||
|
||||
# Clear the registry of providers so that providers from previous
|
||||
# tests aren't used.
|
||||
self.addCleanup(dependency.reset)
|
||||
self.addCleanup(provider_api.ProviderAPIs._clear_registry_instances)
|
||||
|
||||
# Ensure Notification subscriptions and resource types are empty
|
||||
self.addCleanup(notifications.clear_subscribers)
|
||||
@ -691,6 +691,10 @@ class TestCase(BaseTestCase):
|
||||
|
||||
def load_backends(self):
|
||||
"""Initialize each manager and assigns them to an attribute."""
|
||||
# TODO(morgan): Ensure our tests only ever call load_backends
|
||||
# a single time via this method. for now just clear the registry
|
||||
# if we are reloading.
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
self.useFixture(ksfixtures.BackendLoader(self))
|
||||
|
||||
def load_fixtures(self, fixtures):
|
||||
|
@ -14,7 +14,6 @@
|
||||
import fixtures
|
||||
|
||||
from keystone import auth
|
||||
from keystone.common import dependency
|
||||
from keystone.server import common
|
||||
|
||||
|
||||
@ -28,11 +27,6 @@ class BackendLoader(fixtures.Fixture):
|
||||
def setUp(self):
|
||||
super(BackendLoader, self).setUp()
|
||||
|
||||
# TODO(blk-u): Shouldn't need to clear the registry here, but some
|
||||
# tests call load_backends multiple times. These should be fixed to
|
||||
# only call load_backends once.
|
||||
dependency.reset()
|
||||
|
||||
self.clear_auth_plugin_registry()
|
||||
drivers, _unused = common.setup_backends()
|
||||
|
||||
|
@ -37,7 +37,7 @@ from keystone.cmd.doctor import ldap
|
||||
from keystone.cmd.doctor import security_compliance
|
||||
from keystone.cmd.doctor import tokens
|
||||
from keystone.cmd.doctor import tokens_fernet
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
from keystone.common.sql import upgrades
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -61,6 +61,11 @@ class CliTestCase(unit.SQLDriverOverrides, unit.TestCase):
|
||||
def test_token_flush(self):
|
||||
self.useFixture(database.Database())
|
||||
self.load_backends()
|
||||
# NOTE(morgan): we are testing a direct instantiation of the
|
||||
# persistence manager for flushing. We should clear this out so we
|
||||
# don't error. CLI should never impact a running service
|
||||
# and should never actually lock the registry for dependencies.
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
cli.TokenFlush.main()
|
||||
|
||||
|
||||
@ -463,7 +468,7 @@ class CliDomainConfigAllTestCase(unit.SQLDriverOverrides, unit.TestCase):
|
||||
}
|
||||
|
||||
# Clear backend dependencies, since cli loads these manually
|
||||
dependency.reset()
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
cli.DomainConfigUpload.main()
|
||||
|
||||
res = self.domain_config_api.get_config_with_sensitive_info(
|
||||
@ -495,7 +500,7 @@ class CliDomainConfigSingleDomainTestCase(CliDomainConfigAllTestCase):
|
||||
}
|
||||
|
||||
# Clear backend dependencies, since cli loads these manually
|
||||
dependency.reset()
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
cli.DomainConfigUpload.main()
|
||||
|
||||
res = self.domain_config_api.get_config_with_sensitive_info(
|
||||
@ -519,7 +524,7 @@ class CliDomainConfigSingleDomainTestCase(CliDomainConfigAllTestCase):
|
||||
|
||||
# Now try and upload the settings in the configuration file for the
|
||||
# default domain
|
||||
dependency.reset()
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
with mock.patch('six.moves.builtins.print') as mock_print:
|
||||
self.assertRaises(unit.UnexpectedExit, cli.DomainConfigUpload.main)
|
||||
file_name = ('keystone.%s.conf' % self.default_domain['name'])
|
||||
@ -544,7 +549,7 @@ class CliDomainConfigNoOptionsTestCase(CliDomainConfigAllTestCase):
|
||||
project='keystone', default_config_files=config_files)
|
||||
|
||||
def test_config_upload(self):
|
||||
dependency.reset()
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
with mock.patch('six.moves.builtins.print') as mock_print:
|
||||
self.assertRaises(unit.UnexpectedExit, cli.DomainConfigUpload.main)
|
||||
mock_print.assert_has_calls(
|
||||
@ -561,7 +566,7 @@ class CliDomainConfigTooManyOptionsTestCase(CliDomainConfigAllTestCase):
|
||||
project='keystone', default_config_files=config_files)
|
||||
|
||||
def test_config_upload(self):
|
||||
dependency.reset()
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
with mock.patch('six.moves.builtins.print') as mock_print:
|
||||
self.assertRaises(unit.UnexpectedExit, cli.DomainConfigUpload.main)
|
||||
mock_print.assert_has_calls(
|
||||
@ -578,7 +583,7 @@ class CliDomainConfigInvalidDomainTestCase(CliDomainConfigAllTestCase):
|
||||
project='keystone', default_config_files=config_files)
|
||||
|
||||
def test_config_upload(self):
|
||||
dependency.reset()
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
with mock.patch('six.moves.builtins.print') as mock_print:
|
||||
self.assertRaises(unit.UnexpectedExit, cli.DomainConfigUpload.main)
|
||||
file_name = 'keystone.%s.conf' % self.invalid_domain_name
|
||||
@ -742,7 +747,8 @@ class TestMappingPopulate(unit.SQLDriverOverrides, unit.TestCase):
|
||||
'entity_type': identity_mapping.EntityType.USER}
|
||||
self.assertIsNone(self.id_mapping_api.get_public_id(local_entity))
|
||||
|
||||
dependency.reset() # backends are loaded again in the command handler
|
||||
# backends are loaded again in the command handler
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
cli.MappingPopulate.main()
|
||||
|
||||
for user in users:
|
||||
@ -756,7 +762,8 @@ class TestMappingPopulate(unit.SQLDriverOverrides, unit.TestCase):
|
||||
def test_bad_domain_name(self):
|
||||
CONF(args=['mapping_populate', '--domain-name', uuid.uuid4().hex],
|
||||
project='keystone')
|
||||
dependency.reset() # backends are loaded again in the command handler
|
||||
# backends are loaded again in the command handler
|
||||
provider_api.ProviderAPIs._clear_registry_instances()
|
||||
# NOTE: assertEqual is used on purpose. assertFalse passes with None.
|
||||
self.assertEqual(False, cli.MappingPopulate.main())
|
||||
|
||||
|
@ -16,7 +16,6 @@ import datetime
|
||||
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import utils
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -24,8 +23,6 @@ from keystone.tests import unit
|
||||
from keystone.tests.unit import ksfixtures
|
||||
from keystone.tests.unit.ksfixtures import database
|
||||
from keystone import token
|
||||
from keystone.token.providers import fernet
|
||||
from keystone.token.providers import uuid
|
||||
|
||||
|
||||
CONF = keystone.conf.CONF
|
||||
@ -464,21 +461,6 @@ class TestTokenProvider(unit.TestCase):
|
||||
self.token_provider_api.get_token_version,
|
||||
'bogus')
|
||||
|
||||
def test_supported_token_providers(self):
|
||||
# test default config
|
||||
|
||||
dependency.reset()
|
||||
self.assertIsInstance(token.provider.Manager().driver,
|
||||
fernet.Provider)
|
||||
|
||||
dependency.reset()
|
||||
self.config_fixture.config(group='token', provider='uuid')
|
||||
self.assertIsInstance(token.provider.Manager().driver, uuid.Provider)
|
||||
|
||||
dependency.reset()
|
||||
self.config_fixture.config(group='token', provider='fernet')
|
||||
self.assertIsInstance(token.provider.Manager().driver, fernet.Provider)
|
||||
|
||||
def test_unsupported_token_provider(self):
|
||||
self.config_fixture.config(group='token',
|
||||
provider='MyProvider')
|
||||
|
@ -2143,7 +2143,8 @@ class TokenAPITests(object):
|
||||
|
||||
# flush the tokens, this will only have an effect on sql
|
||||
try:
|
||||
self.token_provider_api._persistence.flush_expired_tokens()
|
||||
provider_api = self.token_provider_api
|
||||
provider_api._persistence.flush_expired_tokens()
|
||||
except exception.NotImplemented:
|
||||
pass
|
||||
|
||||
|
@ -535,13 +535,15 @@ class AuthTokenTests(object):
|
||||
|
||||
def test_delete_keystone_tokens_by_consumer_id(self):
|
||||
self.test_oauth_flow()
|
||||
self.token_provider_api._persistence.get_token(self.keystone_token_id)
|
||||
self.token_provider_api._persistence.get_token(
|
||||
self.keystone_token_id)
|
||||
self.token_provider_api._persistence.delete_tokens(
|
||||
self.user_id,
|
||||
consumer_id=self.consumer['key'])
|
||||
self.assertRaises(exception.TokenNotFound,
|
||||
self.token_provider_api._persistence.get_token,
|
||||
self.keystone_token_id)
|
||||
self.assertRaises(
|
||||
exception.TokenNotFound,
|
||||
self.token_provider_api._persistence.get_token,
|
||||
self.keystone_token_id)
|
||||
|
||||
def _create_trust_get_token(self):
|
||||
ref = unit.new_trust_ref(
|
||||
|
@ -17,7 +17,6 @@ import functools
|
||||
import webob
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import extension
|
||||
from keystone.common import json_home
|
||||
from keystone.common import wsgi
|
||||
@ -69,7 +68,6 @@ class Routers(wsgi.RoutersBase):
|
||||
rel=build_resource_relation(resource_name='certificates'))
|
||||
|
||||
|
||||
@dependency.requires('token_provider_api')
|
||||
class SimpleCert(controller.V3Controller):
|
||||
|
||||
def _get_certificate(self, name):
|
||||
|
@ -13,7 +13,7 @@
|
||||
# under the License.
|
||||
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import utils
|
||||
from keystone.common import wsgi
|
||||
import keystone.conf
|
||||
@ -58,8 +58,7 @@ class ExternalAuthNotApplicable(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@dependency.requires('resource_api', 'identity_api')
|
||||
class BaseAuthenticationMethod(object):
|
||||
class BaseAuthenticationMethod(provider_api.ProviderAPIMixin, object):
|
||||
"""Common utilities/dependencies for all authentication method classes."""
|
||||
|
||||
def _get_project_id_from_auth(self, auth):
|
||||
@ -99,7 +98,6 @@ class BaseAuthenticationMethod(object):
|
||||
return project_id
|
||||
|
||||
|
||||
@dependency.requires('token_provider_api', 'trust_api')
|
||||
class TokenAuthenticationMethod(BaseAuthenticationMethod):
|
||||
"""Authenticate using an existing token."""
|
||||
|
||||
@ -280,9 +278,7 @@ class ExternalAuthenticationMethod(BaseAuthenticationMethod):
|
||||
return (user_ref, tenant_id, expiry, bind, audit_id)
|
||||
|
||||
|
||||
@dependency.requires('catalog_api', 'resource_api',
|
||||
'trust_api', 'identity_api')
|
||||
class V2TokenDataHelper(object):
|
||||
class V2TokenDataHelper(provider_api.ProviderAPIMixin, object):
|
||||
"""Create V2 token data."""
|
||||
|
||||
def v3_to_v2_token(self, v3_token_data, token_id):
|
||||
|
@ -13,4 +13,4 @@
|
||||
from keystone.token.persistence.core import * # noqa
|
||||
|
||||
|
||||
__all__ = ('Manager', 'TokenDriverBase')
|
||||
__all__ = ('PersistenceManager', 'TokenDriverBase')
|
||||
|
@ -21,7 +21,6 @@ from oslo_log import log
|
||||
import six
|
||||
|
||||
from keystone.common import cache
|
||||
from keystone.common import dependency
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -34,8 +33,6 @@ REVOCATION_MEMOIZE = cache.get_memoization_decorator(group='token',
|
||||
expiration_group='revoke')
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'identity_api', 'resource_api',
|
||||
'trust_api')
|
||||
class PersistenceManager(manager.Manager):
|
||||
"""Default pivot point for the Token Persistence backend.
|
||||
|
||||
@ -45,6 +42,7 @@ class PersistenceManager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.token.persistence'
|
||||
_provides_api = '_token_persistence_manager'
|
||||
|
||||
def __init__(self):
|
||||
super(PersistenceManager, self).__init__(CONF.token.driver)
|
||||
@ -167,37 +165,6 @@ class PersistenceManager(manager.Manager):
|
||||
self._get_token.invalidate(self, token_id)
|
||||
|
||||
|
||||
@dependency.requires('token_provider_api')
|
||||
@dependency.provider('token_api')
|
||||
class Manager(object):
|
||||
"""The token_api provider.
|
||||
|
||||
This class is a proxy class to the token_provider_api's persistence
|
||||
manager.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# NOTE(morganfainberg): __init__ is required for dependency processing.
|
||||
super(Manager, self).__init__()
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""Forward calls to the `token_provider_api` persistence manager."""
|
||||
# NOTE(morganfainberg): Prevent infinite recursion, raise an
|
||||
# AttributeError for 'token_provider_api' ensuring that the dep
|
||||
# injection doesn't infinitely try and lookup self.token_provider_api
|
||||
# on _process_dependencies. This doesn't need an exception string as
|
||||
# it should only ever be hit on instantiation.
|
||||
if item == 'token_provider_api':
|
||||
raise AttributeError()
|
||||
|
||||
f = getattr(self.token_provider_api._persistence, item)
|
||||
LOG.warning('`token_api.%s` is deprecated as of Juno in favor of '
|
||||
'utilizing methods on `token_provider_api` and may be '
|
||||
'removed in Kilo.', item)
|
||||
setattr(self, item, f)
|
||||
return f
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class TokenDriverBase(object):
|
||||
"""Interface description for a Token driver."""
|
||||
|
@ -22,14 +22,12 @@ from oslo_utils import timeutils
|
||||
import six
|
||||
|
||||
from keystone.common import cache
|
||||
from keystone.common import dependency
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
from keystone.i18n import _
|
||||
from keystone.models import token_model
|
||||
from keystone import notifications
|
||||
from keystone.token import persistence
|
||||
|
||||
|
||||
CONF = keystone.conf.CONF
|
||||
@ -49,8 +47,6 @@ V3 = token_model.V3
|
||||
VERSIONS = token_model.VERSIONS
|
||||
|
||||
|
||||
@dependency.provider('token_provider_api')
|
||||
@dependency.requires('assignment_api', 'revoke_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the token provider backend.
|
||||
|
||||
@ -60,6 +56,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.token.provider'
|
||||
_provides_api = 'token_provider_api'
|
||||
|
||||
V3 = V3
|
||||
VERSIONS = VERSIONS
|
||||
@ -111,7 +108,7 @@ class Manager(manager.Manager):
|
||||
# the provider manager requires the token persistence manager, which
|
||||
# requires the token provider manager).
|
||||
if self._persistence_manager is None:
|
||||
self._persistence_manager = persistence.PersistenceManager()
|
||||
self._persistence_manager = self._token_persistence_manager
|
||||
return self._persistence_manager
|
||||
|
||||
def _create_token(self, token_id, token_data):
|
||||
|
@ -24,7 +24,7 @@ from oslo_utils import timeutils
|
||||
import six
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import provider_api
|
||||
from keystone.common import utils
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -82,10 +82,7 @@ def build_audit_info(parent_audit_id=None):
|
||||
return [audit_id]
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'catalog_api', 'federation_api',
|
||||
'identity_api', 'resource_api', 'role_api', 'trust_api',
|
||||
'oauth_api')
|
||||
class V3TokenDataHelper(object):
|
||||
class V3TokenDataHelper(provider_api.ProviderAPIMixin, object):
|
||||
"""Token data helper."""
|
||||
|
||||
def __init__(self):
|
||||
@ -451,9 +448,7 @@ class V3TokenDataHelper(object):
|
||||
return {'token': token_data}
|
||||
|
||||
|
||||
@dependency.requires('catalog_api', 'identity_api', 'oauth_api',
|
||||
'resource_api', 'role_api', 'trust_api')
|
||||
class BaseProvider(base.Provider):
|
||||
class BaseProvider(provider_api.ProviderAPIMixin, base.Provider):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseProvider, self).__init__(*args, **kwargs)
|
||||
self.v3_token_data_helper = V3TokenDataHelper()
|
||||
|
@ -13,7 +13,6 @@
|
||||
import os
|
||||
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import utils as ks_utils
|
||||
import keystone.conf
|
||||
from keystone.federation import constants as federation_constants
|
||||
@ -25,7 +24,6 @@ from keystone.token.providers.fernet import token_formatters as tf
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
@dependency.requires('trust_api', 'oauth_api', 'identity_api')
|
||||
class Provider(common.BaseProvider):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Provider, self).__init__(*args, **kwargs)
|
||||
|
@ -18,7 +18,6 @@ from oslo_utils import timeutils
|
||||
|
||||
from keystone import assignment
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import driver_hints
|
||||
from keystone.common import utils
|
||||
from keystone.common import validation
|
||||
@ -34,8 +33,6 @@ def _trustor_trustee_only(trust, user_id):
|
||||
action=_('Requested user has no relation to this trust'))
|
||||
|
||||
|
||||
@dependency.requires('assignment_api', 'identity_api', 'resource_api',
|
||||
'role_api', 'token_provider_api', 'trust_api')
|
||||
class TrustV3(controller.V3Controller):
|
||||
collection_name = "trusts"
|
||||
member_name = "trust"
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
from six.moves import zip
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import manager
|
||||
import keystone.conf
|
||||
from keystone import exception
|
||||
@ -27,8 +26,6 @@ from keystone import notifications
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
@dependency.requires('identity_api')
|
||||
@dependency.provider('trust_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Trust backend.
|
||||
|
||||
@ -38,6 +35,7 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
|
||||
driver_namespace = 'keystone.trust'
|
||||
_provides_api = 'trust_api'
|
||||
|
||||
_TRUST = "OS-TRUST:trust"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user