Create a Config object
The _conf_get ugliness in auth_token middleware has been around for a long time now to handle the abstraction from different oslo.config options and the paste overrides. This logic is now also being needed in other middlewares. Extract this into a common config object that has a better interface and is easier to work with. Change-Id: I8b8a1427bc527e43bb1baec25a881d93df3f93cc
This commit is contained in:
parent
2ab0f98a9f
commit
f8c150a9cc
0
keystonemiddleware/_common/__init__.py
Normal file
0
keystonemiddleware/_common/__init__.py
Normal file
132
keystonemiddleware/_common/config.py
Normal file
132
keystonemiddleware/_common/config.py
Normal file
@ -0,0 +1,132 @@
|
||||
# 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_config import cfg
|
||||
import six
|
||||
|
||||
from keystonemiddleware import exceptions
|
||||
from keystonemiddleware.i18n import _
|
||||
|
||||
CONF = cfg.CONF
|
||||
_NOT_SET = object()
|
||||
|
||||
|
||||
def _conf_values_type_convert(group_name, all_options, conf):
|
||||
"""Convert conf values into correct type."""
|
||||
if not conf:
|
||||
return {}
|
||||
|
||||
opts = {}
|
||||
opt_types = {}
|
||||
|
||||
for group, options in all_options:
|
||||
# only accept paste overrides for the primary group
|
||||
if group != group_name:
|
||||
continue
|
||||
|
||||
for o in options:
|
||||
type_dest = (getattr(o, 'type', str), o.dest)
|
||||
opt_types[o.dest] = type_dest
|
||||
# Also add the deprecated name with the same type and dest.
|
||||
for d_o in o.deprecated_opts:
|
||||
opt_types[d_o.name] = type_dest
|
||||
|
||||
break
|
||||
|
||||
for k, v in six.iteritems(conf):
|
||||
dest = k
|
||||
try:
|
||||
if v is not None:
|
||||
type_, dest = opt_types[k]
|
||||
v = type_(v)
|
||||
except KeyError: # nosec
|
||||
# This option is not known to auth_token. v is not converted.
|
||||
# FIXME(jamielennox): This should probably log a warning.
|
||||
pass
|
||||
except ValueError as e:
|
||||
raise exceptions.ConfigurationError(
|
||||
_('Unable to convert the value of %(key)s option into correct '
|
||||
'type: %(ex)s') % {'key': k, 'ex': e})
|
||||
opts[dest] = v
|
||||
|
||||
return opts
|
||||
|
||||
|
||||
class Config(object):
|
||||
|
||||
def __init__(self, group_name, all_options, conf):
|
||||
# NOTE(wanghong): If options are set in paste file, all the option
|
||||
# values passed into conf are string type. So, we should convert the
|
||||
# conf value into correct type.
|
||||
self.paste_overrides = _conf_values_type_convert(group_name,
|
||||
all_options,
|
||||
conf)
|
||||
|
||||
# NOTE(sileht, cdent): If we don't want to use oslo.config global
|
||||
# object there are two options: set "oslo_config_project" in
|
||||
# paste.ini and the middleware will load the configuration with a
|
||||
# local oslo.config object or the caller which instantiates
|
||||
# AuthProtocol can pass in an existing oslo.config as the
|
||||
# value of the "oslo_config_config" key in conf. If both are
|
||||
# set "olso_config_config" is used.
|
||||
local_oslo_config = None
|
||||
|
||||
try:
|
||||
local_oslo_config = conf['oslo_config_config']
|
||||
except KeyError:
|
||||
if 'oslo_config_project' in conf:
|
||||
config_files = filter(None, [conf.get('oslo_config_file')])
|
||||
local_oslo_config = cfg.ConfigOpts()
|
||||
local_oslo_config([],
|
||||
project=conf['oslo_config_project'],
|
||||
default_config_files=config_files,
|
||||
validate_default_values=True)
|
||||
|
||||
if local_oslo_config:
|
||||
for group, opts in all_options:
|
||||
local_oslo_config.register_opts(opts, group=group)
|
||||
|
||||
self.oslo_conf_obj = local_oslo_config or cfg.CONF
|
||||
self.group_name = group_name
|
||||
|
||||
def get(self, name, group=_NOT_SET):
|
||||
# try config from paste-deploy first
|
||||
try:
|
||||
return self.paste_overrides[name]
|
||||
except KeyError:
|
||||
if group is _NOT_SET:
|
||||
group = self.group_name
|
||||
|
||||
return self.oslo_conf_obj[group][name]
|
||||
|
||||
@property
|
||||
def project(self):
|
||||
"""Determine a project name from all available config sources.
|
||||
|
||||
The sources are checked in the following order:
|
||||
|
||||
1. The paste-deploy config for auth_token middleware
|
||||
2. The keystone_authtoken or base group in the project's config
|
||||
3. The oslo.config CONF.project property
|
||||
|
||||
"""
|
||||
try:
|
||||
return self.get('project', group=self.group_name)
|
||||
except cfg.NoSuchOptError:
|
||||
try:
|
||||
# CONF.project will exist only if the service uses
|
||||
# oslo.config. It will only be set when the project
|
||||
# calls CONF(...) and when not set oslo.config oddly
|
||||
# raises a NoSuchOptError exception.
|
||||
return self.oslo_conf_obj.project
|
||||
except cfg.NoSuchOptError:
|
||||
return None
|
@ -218,12 +218,12 @@ from keystoneauth1 import loading
|
||||
from keystoneauth1.loading import session as session_loading
|
||||
from keystoneclient.common import cms
|
||||
from keystoneclient import exceptions as ksc_exceptions
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import jsonutils
|
||||
import pkg_resources
|
||||
import six
|
||||
import webob.dec
|
||||
|
||||
from keystonemiddleware._common import config
|
||||
from keystonemiddleware.auth_token import _auth
|
||||
from keystonemiddleware.auth_token import _base
|
||||
from keystonemiddleware.auth_token import _cache
|
||||
@ -233,163 +233,10 @@ from keystonemiddleware.auth_token import _request
|
||||
from keystonemiddleware.auth_token import _revocations
|
||||
from keystonemiddleware.auth_token import _signing_dir
|
||||
from keystonemiddleware.auth_token import _user_plugin
|
||||
from keystonemiddleware import opts
|
||||
from keystonemiddleware.i18n import _, _LC, _LE, _LI, _LW
|
||||
|
||||
|
||||
# NOTE(jamielennox): A number of options below are deprecated however are left
|
||||
# in the list and only mentioned as deprecated in the help string. This is
|
||||
# because we have to provide the same deprecation functionality for arguments
|
||||
# passed in via the conf in __init__ (from paste) and there is no way to test
|
||||
# that the default value was set or not in CONF.
|
||||
# Also if we were to remove the options from the CONF list (as typical CONF
|
||||
# deprecation works) then other projects will not be able to override the
|
||||
# options via CONF.
|
||||
|
||||
_OPTS = [
|
||||
cfg.StrOpt('auth_uri',
|
||||
default=None,
|
||||
# FIXME(dolph): should be default='http://127.0.0.1:5000/v2.0/',
|
||||
# or (depending on client support) an unversioned, publicly
|
||||
# accessible identity endpoint (see bug 1207517). Further, we
|
||||
# can eliminate this configuration option in favor of pulling
|
||||
# the endpoint from the service catalog that the service user
|
||||
# receives (there should be an identity endpoint listed there).
|
||||
# This wasn't an option originally when many auth_token
|
||||
# deployments were configured with the "ADMIN" token and
|
||||
# endpoint combination.
|
||||
help='Complete "public" Identity API endpoint. This endpoint'
|
||||
' should not be an "admin" endpoint, as it should be accessible'
|
||||
' by all end users. Unauthenticated clients are redirected to'
|
||||
' this endpoint to authenticate. Although this endpoint should '
|
||||
' ideally be unversioned, client support in the wild varies. '
|
||||
' If you\'re using a versioned v2 endpoint here, then this '
|
||||
' should *not* be the same endpoint the service user utilizes '
|
||||
' for validating tokens, because normal end users may not be '
|
||||
' able to reach that endpoint.'),
|
||||
cfg.StrOpt('auth_version',
|
||||
default=None,
|
||||
help='API version of the admin Identity API endpoint.'),
|
||||
cfg.BoolOpt('delay_auth_decision',
|
||||
default=False,
|
||||
help='Do not handle authorization requests within the'
|
||||
' middleware, but delegate the authorization decision to'
|
||||
' downstream WSGI components.'),
|
||||
cfg.IntOpt('http_connect_timeout',
|
||||
default=None,
|
||||
help='Request timeout value for communicating with Identity'
|
||||
' API server.'),
|
||||
cfg.IntOpt('http_request_max_retries',
|
||||
default=3,
|
||||
help='How many times are we trying to reconnect when'
|
||||
' communicating with Identity API Server.'),
|
||||
cfg.StrOpt('cache',
|
||||
default=None,
|
||||
help='Env key for the swift cache.'),
|
||||
cfg.StrOpt('certfile',
|
||||
help='Required if identity server requires client certificate'),
|
||||
cfg.StrOpt('keyfile',
|
||||
help='Required if identity server requires client certificate'),
|
||||
cfg.StrOpt('cafile', default=None,
|
||||
help='A PEM encoded Certificate Authority to use when '
|
||||
'verifying HTTPs connections. Defaults to system CAs.'),
|
||||
cfg.BoolOpt('insecure', default=False, help='Verify HTTPS connections.'),
|
||||
cfg.StrOpt('region_name', default=None,
|
||||
help='The region in which the identity server can be found.'),
|
||||
cfg.StrOpt('signing_dir',
|
||||
help='Directory used to cache files related to PKI tokens.'),
|
||||
cfg.ListOpt('memcached_servers',
|
||||
deprecated_name='memcache_servers',
|
||||
help='Optionally specify a list of memcached server(s) to'
|
||||
' use for caching. If left undefined, tokens will instead be'
|
||||
' cached in-process.'),
|
||||
cfg.IntOpt('token_cache_time',
|
||||
default=300,
|
||||
help='In order to prevent excessive effort spent validating'
|
||||
' tokens, the middleware caches previously-seen tokens for a'
|
||||
' configurable duration (in seconds). Set to -1 to disable'
|
||||
' caching completely.'),
|
||||
cfg.IntOpt('revocation_cache_time',
|
||||
default=10,
|
||||
help='Determines the frequency at which the list of revoked'
|
||||
' tokens is retrieved from the Identity service (in seconds). A'
|
||||
' high number of revocation events combined with a low cache'
|
||||
' duration may significantly reduce performance. Only valid'
|
||||
' for PKI tokens.'),
|
||||
cfg.StrOpt('memcache_security_strategy',
|
||||
default='None',
|
||||
choices=('None', 'MAC', 'ENCRYPT'),
|
||||
ignore_case=True,
|
||||
help='(Optional) If defined, indicate whether token data'
|
||||
' should be authenticated or authenticated and encrypted.'
|
||||
' If MAC, token data is authenticated (with HMAC) in the cache.'
|
||||
' If ENCRYPT, token data is encrypted and authenticated in the'
|
||||
' cache. If the value is not one of these options or empty,'
|
||||
' auth_token will raise an exception on initialization.'),
|
||||
cfg.StrOpt('memcache_secret_key',
|
||||
default=None,
|
||||
secret=True,
|
||||
help='(Optional, mandatory if memcache_security_strategy is'
|
||||
' defined) This string is used for key derivation.'),
|
||||
cfg.IntOpt('memcache_pool_dead_retry',
|
||||
default=5 * 60,
|
||||
help='(Optional) Number of seconds memcached server is'
|
||||
' considered dead before it is tried again.'),
|
||||
cfg.IntOpt('memcache_pool_maxsize',
|
||||
default=10,
|
||||
help='(Optional) Maximum total number of open connections to'
|
||||
' every memcached server.'),
|
||||
cfg.IntOpt('memcache_pool_socket_timeout',
|
||||
default=3,
|
||||
help='(Optional) Socket timeout in seconds for communicating '
|
||||
'with a memcached server.'),
|
||||
cfg.IntOpt('memcache_pool_unused_timeout',
|
||||
default=60,
|
||||
help='(Optional) Number of seconds a connection to memcached'
|
||||
' is held unused in the pool before it is closed.'),
|
||||
cfg.IntOpt('memcache_pool_conn_get_timeout',
|
||||
default=10,
|
||||
help='(Optional) Number of seconds that an operation will wait '
|
||||
'to get a memcached client connection from the pool.'),
|
||||
cfg.BoolOpt('memcache_use_advanced_pool',
|
||||
default=False,
|
||||
help='(Optional) Use the advanced (eventlet safe) memcached '
|
||||
'client pool. The advanced pool will only work under '
|
||||
'python 2.x.'),
|
||||
cfg.BoolOpt('include_service_catalog',
|
||||
default=True,
|
||||
help='(Optional) Indicate whether to set the X-Service-Catalog'
|
||||
' header. If False, middleware will not ask for service'
|
||||
' catalog on token validation and will not set the'
|
||||
' X-Service-Catalog header.'),
|
||||
cfg.StrOpt('enforce_token_bind',
|
||||
default='permissive',
|
||||
help='Used to control the use and type of token binding. Can'
|
||||
' be set to: "disabled" to not check token binding.'
|
||||
' "permissive" (default) to validate binding information if the'
|
||||
' bind type is of a form known to the server and ignore it if'
|
||||
' not. "strict" like "permissive" but if the bind type is'
|
||||
' unknown the token will be rejected. "required" any form of'
|
||||
' token binding is needed to be allowed. Finally the name of a'
|
||||
' binding method that must be present in tokens.'),
|
||||
cfg.BoolOpt('check_revocations_for_cached', default=False,
|
||||
help='If true, the revocation list will be checked for cached'
|
||||
' tokens. This requires that PKI tokens are configured on the'
|
||||
' identity server.'),
|
||||
cfg.ListOpt('hash_algorithms', default=['md5'],
|
||||
help='Hash algorithms to use for hashing PKI tokens. This may'
|
||||
' be a single algorithm or multiple. The algorithms are those'
|
||||
' supported by Python standard hashlib.new(). The hashes will'
|
||||
' be tried in the order given, so put the preferred one first'
|
||||
' for performance. The result of the first hash will be stored'
|
||||
' in the cache. This will typically be set to multiple values'
|
||||
' only while migrating from a less secure algorithm to a more'
|
||||
' secure one. Once all the old tokens are expired this option'
|
||||
' should be set to a single value for better performance.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(_OPTS, group=_base.AUTHTOKEN_GROUP)
|
||||
|
||||
_LOG = logging.getLogger(__name__)
|
||||
_CACHE_INVALID_INDICATOR = 'invalid'
|
||||
|
||||
@ -410,37 +257,6 @@ def _token_is_v3(token_info):
|
||||
return ('token' in token_info)
|
||||
|
||||
|
||||
def _conf_values_type_convert(conf):
|
||||
"""Convert conf values into correct type."""
|
||||
if not conf:
|
||||
return {}
|
||||
|
||||
opt_types = {}
|
||||
for o in _OPTS + _auth.OPTS:
|
||||
type_dest = (getattr(o, 'type', str), o.dest)
|
||||
opt_types[o.dest] = type_dest
|
||||
# Also add the deprecated name with the same type and dest.
|
||||
for d_o in o.deprecated_opts:
|
||||
opt_types[d_o.name] = type_dest
|
||||
|
||||
opts = {}
|
||||
for k, v in six.iteritems(conf):
|
||||
dest = k
|
||||
try:
|
||||
if v is not None:
|
||||
type_, dest = opt_types[k]
|
||||
v = type_(v)
|
||||
except KeyError: # nosec
|
||||
# This option is not known to auth_token. v is not converted.
|
||||
pass
|
||||
except ValueError as e:
|
||||
raise ksm_exceptions.ConfigurationError(
|
||||
_('Unable to convert the value of %(key)s option into correct '
|
||||
'type: %(ex)s') % {'key': k, 'ex': e})
|
||||
opts[dest] = v
|
||||
return opts
|
||||
|
||||
|
||||
def _get_project_version(project):
|
||||
try:
|
||||
return pkg_resources.get_distribution(project).version
|
||||
@ -654,54 +470,25 @@ class AuthProtocol(BaseAuthProtocol):
|
||||
log = logging.getLogger(conf.get('log_name', __name__))
|
||||
log.info(_LI('Starting Keystone auth_token middleware'))
|
||||
|
||||
# NOTE(wanghong): If options are set in paste file, all the option
|
||||
# values passed into conf are string type. So, we should convert the
|
||||
# conf value into correct type.
|
||||
self._conf = _conf_values_type_convert(conf)
|
||||
|
||||
# NOTE(sileht, cdent): If we don't want to use oslo.config global
|
||||
# object there are two options: set "oslo_config_project" in
|
||||
# paste.ini and the middleware will load the configuration with a
|
||||
# local oslo.config object or the caller which instantiates
|
||||
# AuthProtocol can pass in an existing oslo.config as the
|
||||
# value of the "oslo_config_config" key in conf. If both are
|
||||
# set "olso_config_config" is used.
|
||||
self._local_oslo_config = conf.get('oslo_config_config')
|
||||
if (not self._local_oslo_config) and ('oslo_config_project' in conf):
|
||||
if 'oslo_config_file' in conf:
|
||||
default_config_files = [conf['oslo_config_file']]
|
||||
else:
|
||||
default_config_files = None
|
||||
self._local_oslo_config = cfg.ConfigOpts()
|
||||
self._local_oslo_config(
|
||||
[], project=conf['oslo_config_project'],
|
||||
default_config_files=default_config_files,
|
||||
validate_default_values=True)
|
||||
|
||||
if self._local_oslo_config:
|
||||
self._local_oslo_config.register_opts(_OPTS,
|
||||
group=_base.AUTHTOKEN_GROUP)
|
||||
self._local_oslo_config.register_opts(_auth.OPTS,
|
||||
group=_base.AUTHTOKEN_GROUP)
|
||||
|
||||
loading.register_auth_conf_options(self._local_oslo_config,
|
||||
group=_base.AUTHTOKEN_GROUP)
|
||||
self._conf = config.Config(_base.AUTHTOKEN_GROUP,
|
||||
opts.list_auth_token_opts(),
|
||||
conf)
|
||||
|
||||
super(AuthProtocol, self).__init__(
|
||||
app,
|
||||
log=log,
|
||||
enforce_token_bind=self._conf_get('enforce_token_bind'))
|
||||
enforce_token_bind=self._conf.get('enforce_token_bind'))
|
||||
|
||||
# delay_auth_decision means we still allow unauthenticated requests
|
||||
# through and we let the downstream service make the final decision
|
||||
self._delay_auth_decision = self._conf_get('delay_auth_decision')
|
||||
self._include_service_catalog = self._conf_get(
|
||||
self._delay_auth_decision = self._conf.get('delay_auth_decision')
|
||||
self._include_service_catalog = self._conf.get(
|
||||
'include_service_catalog')
|
||||
self._hash_algorithms = self._conf_get('hash_algorithms')
|
||||
self._hash_algorithms = self._conf.get('hash_algorithms')
|
||||
|
||||
self._identity_server = self._create_identity_server()
|
||||
|
||||
self._auth_uri = self._conf_get('auth_uri')
|
||||
self._auth_uri = self._conf.get('auth_uri')
|
||||
if not self._auth_uri:
|
||||
self.log.warning(
|
||||
_LW('Configuring auth_uri to point to the public identity '
|
||||
@ -714,30 +501,21 @@ class AuthProtocol(BaseAuthProtocol):
|
||||
self._auth_uri = self._identity_server.auth_uri
|
||||
|
||||
self._signing_directory = _signing_dir.SigningDirectory(
|
||||
directory_name=self._conf_get('signing_dir'), log=self.log)
|
||||
directory_name=self._conf.get('signing_dir'), log=self.log)
|
||||
|
||||
self._token_cache = self._token_cache_factory()
|
||||
|
||||
revocation_cache_timeout = datetime.timedelta(
|
||||
seconds=self._conf_get('revocation_cache_time'))
|
||||
seconds=self._conf.get('revocation_cache_time'))
|
||||
self._revocations = _revocations.Revocations(revocation_cache_timeout,
|
||||
self._signing_directory,
|
||||
self._identity_server,
|
||||
self._cms_verify,
|
||||
self.log)
|
||||
|
||||
self._check_revocations_for_cached = self._conf_get(
|
||||
self._check_revocations_for_cached = self._conf.get(
|
||||
'check_revocations_for_cached')
|
||||
|
||||
def _conf_get(self, name, group=_base.AUTHTOKEN_GROUP):
|
||||
# try config from paste-deploy first
|
||||
if name in self._conf:
|
||||
return self._conf[name]
|
||||
elif self._local_oslo_config:
|
||||
return self._local_oslo_config[group][name]
|
||||
else:
|
||||
return CONF[group][name]
|
||||
|
||||
def process_request(self, request):
|
||||
"""Process request.
|
||||
|
||||
@ -994,30 +772,30 @@ class AuthProtocol(BaseAuthProtocol):
|
||||
def _get_auth_plugin(self):
|
||||
# NOTE(jamielennox): Ideally this would use load_from_conf_options
|
||||
# however that is not possible because we have to support the override
|
||||
# pattern we use in _conf_get. This function therefore does a manual
|
||||
# pattern we use in _conf.get. This function therefore does a manual
|
||||
# version of load_from_conf_options with the fallback plugin inline.
|
||||
|
||||
group = self._conf_get('auth_section') or _base.AUTHTOKEN_GROUP
|
||||
group = self._conf.get('auth_section') or _base.AUTHTOKEN_GROUP
|
||||
|
||||
# NOTE(jamielennox): auth_plugin was deprecated to auth_type. _conf_get
|
||||
# NOTE(jamielennox): auth_plugin was deprecated to auth_type. _conf.get
|
||||
# doesn't handle that deprecation in the case of conf dict options so
|
||||
# we have to manually check the value
|
||||
plugin_name = (self._conf_get('auth_type', group=group)
|
||||
or self._conf.get('auth_plugin'))
|
||||
plugin_name = (self._conf.get('auth_type', group=group)
|
||||
or self._conf.paste_overrides.get('auth_plugin'))
|
||||
|
||||
if not plugin_name:
|
||||
return _auth.AuthTokenPlugin(
|
||||
log=self.log,
|
||||
auth_admin_prefix=self._conf_get('auth_admin_prefix',
|
||||
auth_admin_prefix=self._conf.get('auth_admin_prefix',
|
||||
group=group),
|
||||
auth_host=self._conf_get('auth_host', group=group),
|
||||
auth_port=self._conf_get('auth_port', group=group),
|
||||
auth_protocol=self._conf_get('auth_protocol', group=group),
|
||||
identity_uri=self._conf_get('identity_uri', group=group),
|
||||
admin_token=self._conf_get('admin_token', group=group),
|
||||
admin_user=self._conf_get('admin_user', group=group),
|
||||
admin_password=self._conf_get('admin_password', group=group),
|
||||
admin_tenant_name=self._conf_get('admin_tenant_name',
|
||||
auth_host=self._conf.get('auth_host', group=group),
|
||||
auth_port=self._conf.get('auth_port', group=group),
|
||||
auth_protocol=self._conf.get('auth_protocol', group=group),
|
||||
identity_uri=self._conf.get('identity_uri', group=group),
|
||||
admin_token=self._conf.get('admin_token', group=group),
|
||||
admin_user=self._conf.get('admin_user', group=group),
|
||||
admin_password=self._conf.get('admin_password', group=group),
|
||||
admin_tenant_name=self._conf.get('admin_tenant_name',
|
||||
group=group)
|
||||
)
|
||||
|
||||
@ -1026,39 +804,12 @@ class AuthProtocol(BaseAuthProtocol):
|
||||
plugin_loader = loading.get_plugin_loader(plugin_name)
|
||||
plugin_opts = loading.get_auth_plugin_conf_options(plugin_loader)
|
||||
|
||||
(self._local_oslo_config or CONF).register_opts(plugin_opts,
|
||||
group=group)
|
||||
|
||||
getter = lambda opt: self._conf_get(opt.dest, group=group)
|
||||
self._conf.oslo_conf_obj.register_opts(plugin_opts, group=group)
|
||||
getter = lambda opt: self._conf.get(opt.dest, group=group)
|
||||
return plugin_loader.load_from_options_getter(getter)
|
||||
|
||||
def _determine_project(self):
|
||||
"""Determine a project name from all available config sources.
|
||||
|
||||
The sources are checked in the following order:
|
||||
|
||||
1. The paste-deploy config for auth_token middleware
|
||||
2. The keystone_authtoken in the project's config
|
||||
3. The oslo.config CONF.project property
|
||||
|
||||
"""
|
||||
try:
|
||||
return self._conf_get('project')
|
||||
except cfg.NoSuchOptError:
|
||||
# Prefer local oslo config object
|
||||
if self._local_oslo_config:
|
||||
return self._local_oslo_config.project
|
||||
try:
|
||||
# CONF.project will exist only if the service uses
|
||||
# oslo.config. It will only be set when the project
|
||||
# calls CONF(...) and when not set oslo.config oddly
|
||||
# raises a NoSuchOptError exception.
|
||||
return CONF.project
|
||||
except cfg.NoSuchOptError:
|
||||
return ''
|
||||
|
||||
def _build_useragent_string(self):
|
||||
project = self._determine_project()
|
||||
project = self._conf.project or ''
|
||||
if project:
|
||||
project_version = _get_project_version(project)
|
||||
project = '{project}/{project_version} '.format(
|
||||
@ -1074,14 +825,14 @@ class AuthProtocol(BaseAuthProtocol):
|
||||
def _create_identity_server(self):
|
||||
# NOTE(jamielennox): Loading Session here should be exactly the
|
||||
# same as calling Session.load_from_conf_options(CONF, GROUP)
|
||||
# however we can't do that because we have to use _conf_get to
|
||||
# however we can't do that because we have to use _conf.get to
|
||||
# support the paste.ini options.
|
||||
sess = session_loading.Session().load_from_options(
|
||||
cert=self._conf_get('certfile'),
|
||||
key=self._conf_get('keyfile'),
|
||||
cacert=self._conf_get('cafile'),
|
||||
insecure=self._conf_get('insecure'),
|
||||
timeout=self._conf_get('http_connect_timeout'),
|
||||
cert=self._conf.get('certfile'),
|
||||
key=self._conf.get('keyfile'),
|
||||
cacert=self._conf.get('cafile'),
|
||||
insecure=self._conf.get('insecure'),
|
||||
timeout=self._conf.get('http_connect_timeout'),
|
||||
user_agent=self._build_useragent_string()
|
||||
)
|
||||
|
||||
@ -1092,10 +843,10 @@ class AuthProtocol(BaseAuthProtocol):
|
||||
auth=auth_plugin,
|
||||
service_type='identity',
|
||||
interface='admin',
|
||||
region_name=self._conf_get('region_name'),
|
||||
connect_retries=self._conf_get('http_request_max_retries'))
|
||||
region_name=self._conf.get('region_name'),
|
||||
connect_retries=self._conf.get('http_request_max_retries'))
|
||||
|
||||
auth_version = self._conf_get('auth_version')
|
||||
auth_version = self._conf.get('auth_version')
|
||||
if auth_version is not None:
|
||||
auth_version = discover.normalize_version_number(auth_version)
|
||||
return _identity.IdentityServer(
|
||||
@ -1105,22 +856,22 @@ class AuthProtocol(BaseAuthProtocol):
|
||||
requested_auth_version=auth_version)
|
||||
|
||||
def _token_cache_factory(self):
|
||||
security_strategy = self._conf_get('memcache_security_strategy')
|
||||
security_strategy = self._conf.get('memcache_security_strategy')
|
||||
|
||||
cache_kwargs = dict(
|
||||
cache_time=int(self._conf_get('token_cache_time')),
|
||||
env_cache_name=self._conf_get('cache'),
|
||||
memcached_servers=self._conf_get('memcached_servers'),
|
||||
use_advanced_pool=self._conf_get('memcache_use_advanced_pool'),
|
||||
dead_retry=self._conf_get('memcache_pool_dead_retry'),
|
||||
maxsize=self._conf_get('memcache_pool_maxsize'),
|
||||
unused_timeout=self._conf_get('memcache_pool_unused_timeout'),
|
||||
conn_get_timeout=self._conf_get('memcache_pool_conn_get_timeout'),
|
||||
socket_timeout=self._conf_get('memcache_pool_socket_timeout'),
|
||||
cache_time=int(self._conf.get('token_cache_time')),
|
||||
env_cache_name=self._conf.get('cache'),
|
||||
memcached_servers=self._conf.get('memcached_servers'),
|
||||
use_advanced_pool=self._conf.get('memcache_use_advanced_pool'),
|
||||
dead_retry=self._conf.get('memcache_pool_dead_retry'),
|
||||
maxsize=self._conf.get('memcache_pool_maxsize'),
|
||||
unused_timeout=self._conf.get('memcache_pool_unused_timeout'),
|
||||
conn_get_timeout=self._conf.get('memcache_pool_conn_get_timeout'),
|
||||
socket_timeout=self._conf.get('memcache_pool_socket_timeout'),
|
||||
)
|
||||
|
||||
if security_strategy.lower() != 'none':
|
||||
secret_key = self._conf_get('memcache_secret_key')
|
||||
secret_key = self._conf.get('memcache_secret_key')
|
||||
return _cache.SecureTokenCache(self.log,
|
||||
security_strategy,
|
||||
secret_key,
|
||||
|
@ -14,7 +14,6 @@ import logging
|
||||
|
||||
from keystoneauth1 import discover
|
||||
from keystoneauth1.identity import v2
|
||||
from keystoneauth1 import loading
|
||||
from keystoneauth1 import plugin
|
||||
from keystoneauth1 import token_endpoint
|
||||
from oslo_config import cfg
|
||||
@ -188,5 +187,4 @@ OPTS = [
|
||||
]
|
||||
|
||||
|
||||
loading.register_auth_conf_options(cfg.CONF, _base.AUTHTOKEN_GROUP)
|
||||
cfg.CONF.register_opts(OPTS, group=_base.AUTHTOKEN_GROUP)
|
||||
|
@ -10,18 +10,19 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystonemiddleware import exceptions
|
||||
|
||||
class InvalidToken(Exception):
|
||||
|
||||
ConfigurationError = exceptions.ConfigurationError
|
||||
|
||||
|
||||
class InvalidToken(exceptions.KeystoneMiddlewareException):
|
||||
pass
|
||||
|
||||
|
||||
class ServiceError(Exception):
|
||||
class ServiceError(exceptions.KeystoneMiddlewareException):
|
||||
pass
|
||||
|
||||
|
||||
class ConfigurationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class RevocationListError(Exception):
|
||||
class RevocationListError(exceptions.KeystoneMiddlewareException):
|
||||
pass
|
||||
|
@ -13,14 +13,170 @@
|
||||
import copy
|
||||
|
||||
from keystoneauth1 import loading
|
||||
from oslo_config import cfg
|
||||
|
||||
import keystonemiddleware.auth_token
|
||||
from keystonemiddleware.auth_token import _base
|
||||
|
||||
|
||||
# NOTE(jamielennox): A number of options below are deprecated however are left
|
||||
# in the list and only mentioned as deprecated in the help string. This is
|
||||
# because we have to provide the same deprecation functionality for arguments
|
||||
# passed in via the conf in __init__ (from paste) and there is no way to test
|
||||
# that the default value was set or not in CONF.
|
||||
# Also if we were to remove the options from the CONF list (as typical CONF
|
||||
# deprecation works) then other projects will not be able to override the
|
||||
# options via CONF.
|
||||
|
||||
_OPTS = [
|
||||
cfg.StrOpt('auth_uri',
|
||||
default=None,
|
||||
# FIXME(dolph): should be default='http://127.0.0.1:5000/v2.0/',
|
||||
# or (depending on client support) an unversioned, publicly
|
||||
# accessible identity endpoint (see bug 1207517). Further, we
|
||||
# can eliminate this configuration option in favor of pulling
|
||||
# the endpoint from the service catalog that the service user
|
||||
# receives (there should be an identity endpoint listed there).
|
||||
# This wasn't an option originally when many auth_token
|
||||
# deployments were configured with the "ADMIN" token and
|
||||
# endpoint combination.
|
||||
help='Complete "public" Identity API endpoint. This endpoint'
|
||||
' should not be an "admin" endpoint, as it should be accessible'
|
||||
' by all end users. Unauthenticated clients are redirected to'
|
||||
' this endpoint to authenticate. Although this endpoint should '
|
||||
' ideally be unversioned, client support in the wild varies. '
|
||||
' If you\'re using a versioned v2 endpoint here, then this '
|
||||
' should *not* be the same endpoint the service user utilizes '
|
||||
' for validating tokens, because normal end users may not be '
|
||||
' able to reach that endpoint.'),
|
||||
cfg.StrOpt('auth_version',
|
||||
default=None,
|
||||
help='API version of the admin Identity API endpoint.'),
|
||||
cfg.BoolOpt('delay_auth_decision',
|
||||
default=False,
|
||||
help='Do not handle authorization requests within the'
|
||||
' middleware, but delegate the authorization decision to'
|
||||
' downstream WSGI components.'),
|
||||
cfg.IntOpt('http_connect_timeout',
|
||||
default=None,
|
||||
help='Request timeout value for communicating with Identity'
|
||||
' API server.'),
|
||||
cfg.IntOpt('http_request_max_retries',
|
||||
default=3,
|
||||
help='How many times are we trying to reconnect when'
|
||||
' communicating with Identity API Server.'),
|
||||
cfg.StrOpt('cache',
|
||||
default=None,
|
||||
help='Env key for the swift cache.'),
|
||||
cfg.StrOpt('certfile',
|
||||
help='Required if identity server requires client certificate'),
|
||||
cfg.StrOpt('keyfile',
|
||||
help='Required if identity server requires client certificate'),
|
||||
cfg.StrOpt('cafile', default=None,
|
||||
help='A PEM encoded Certificate Authority to use when '
|
||||
'verifying HTTPs connections. Defaults to system CAs.'),
|
||||
cfg.BoolOpt('insecure', default=False, help='Verify HTTPS connections.'),
|
||||
cfg.StrOpt('region_name', default=None,
|
||||
help='The region in which the identity server can be found.'),
|
||||
cfg.StrOpt('signing_dir',
|
||||
help='Directory used to cache files related to PKI tokens.'),
|
||||
cfg.ListOpt('memcached_servers',
|
||||
deprecated_name='memcache_servers',
|
||||
help='Optionally specify a list of memcached server(s) to'
|
||||
' use for caching. If left undefined, tokens will instead be'
|
||||
' cached in-process.'),
|
||||
cfg.IntOpt('token_cache_time',
|
||||
default=300,
|
||||
help='In order to prevent excessive effort spent validating'
|
||||
' tokens, the middleware caches previously-seen tokens for a'
|
||||
' configurable duration (in seconds). Set to -1 to disable'
|
||||
' caching completely.'),
|
||||
cfg.IntOpt('revocation_cache_time',
|
||||
default=10,
|
||||
help='Determines the frequency at which the list of revoked'
|
||||
' tokens is retrieved from the Identity service (in seconds). A'
|
||||
' high number of revocation events combined with a low cache'
|
||||
' duration may significantly reduce performance. Only valid'
|
||||
' for PKI tokens.'),
|
||||
cfg.StrOpt('memcache_security_strategy',
|
||||
default='None',
|
||||
choices=('None', 'MAC', 'ENCRYPT'),
|
||||
ignore_case=True,
|
||||
help='(Optional) If defined, indicate whether token data'
|
||||
' should be authenticated or authenticated and encrypted.'
|
||||
' If MAC, token data is authenticated (with HMAC) in the cache.'
|
||||
' If ENCRYPT, token data is encrypted and authenticated in the'
|
||||
' cache. If the value is not one of these options or empty,'
|
||||
' auth_token will raise an exception on initialization.'),
|
||||
cfg.StrOpt('memcache_secret_key',
|
||||
default=None,
|
||||
secret=True,
|
||||
help='(Optional, mandatory if memcache_security_strategy is'
|
||||
' defined) This string is used for key derivation.'),
|
||||
cfg.IntOpt('memcache_pool_dead_retry',
|
||||
default=5 * 60,
|
||||
help='(Optional) Number of seconds memcached server is'
|
||||
' considered dead before it is tried again.'),
|
||||
cfg.IntOpt('memcache_pool_maxsize',
|
||||
default=10,
|
||||
help='(Optional) Maximum total number of open connections to'
|
||||
' every memcached server.'),
|
||||
cfg.IntOpt('memcache_pool_socket_timeout',
|
||||
default=3,
|
||||
help='(Optional) Socket timeout in seconds for communicating '
|
||||
'with a memcached server.'),
|
||||
cfg.IntOpt('memcache_pool_unused_timeout',
|
||||
default=60,
|
||||
help='(Optional) Number of seconds a connection to memcached'
|
||||
' is held unused in the pool before it is closed.'),
|
||||
cfg.IntOpt('memcache_pool_conn_get_timeout',
|
||||
default=10,
|
||||
help='(Optional) Number of seconds that an operation will wait '
|
||||
'to get a memcached client connection from the pool.'),
|
||||
cfg.BoolOpt('memcache_use_advanced_pool',
|
||||
default=False,
|
||||
help='(Optional) Use the advanced (eventlet safe) memcached '
|
||||
'client pool. The advanced pool will only work under '
|
||||
'python 2.x.'),
|
||||
cfg.BoolOpt('include_service_catalog',
|
||||
default=True,
|
||||
help='(Optional) Indicate whether to set the X-Service-Catalog'
|
||||
' header. If False, middleware will not ask for service'
|
||||
' catalog on token validation and will not set the'
|
||||
' X-Service-Catalog header.'),
|
||||
cfg.StrOpt('enforce_token_bind',
|
||||
default='permissive',
|
||||
help='Used to control the use and type of token binding. Can'
|
||||
' be set to: "disabled" to not check token binding.'
|
||||
' "permissive" (default) to validate binding information if the'
|
||||
' bind type is of a form known to the server and ignore it if'
|
||||
' not. "strict" like "permissive" but if the bind type is'
|
||||
' unknown the token will be rejected. "required" any form of'
|
||||
' token binding is needed to be allowed. Finally the name of a'
|
||||
' binding method that must be present in tokens.'),
|
||||
cfg.BoolOpt('check_revocations_for_cached', default=False,
|
||||
help='If true, the revocation list will be checked for cached'
|
||||
' tokens. This requires that PKI tokens are configured on the'
|
||||
' identity server.'),
|
||||
cfg.ListOpt('hash_algorithms', default=['md5'],
|
||||
help='Hash algorithms to use for hashing PKI tokens. This may'
|
||||
' be a single algorithm or multiple. The algorithms are those'
|
||||
' supported by Python standard hashlib.new(). The hashes will'
|
||||
' be tried in the order given, so put the preferred one first'
|
||||
' for performance. The result of the first hash will be stored'
|
||||
' in the cache. This will typically be set to multiple values'
|
||||
' only while migrating from a less secure algorithm to a more'
|
||||
' secure one. Once all the old tokens are expired this option'
|
||||
' should be set to a single value for better performance.'),
|
||||
]
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(_OPTS, group=_base.AUTHTOKEN_GROUP)
|
||||
loading.register_auth_conf_options(cfg.CONF, _base.AUTHTOKEN_GROUP)
|
||||
|
||||
|
||||
auth_token_opts = [
|
||||
(_base.AUTHTOKEN_GROUP,
|
||||
keystonemiddleware.auth_token._OPTS +
|
||||
loading.get_auth_common_conf_options())
|
||||
(_base.AUTHTOKEN_GROUP, _OPTS + loading.get_auth_common_conf_options()),
|
||||
]
|
||||
|
||||
__all__ = (
|
||||
@ -49,7 +205,6 @@ def list_opts():
|
||||
|
||||
:returns: a list of (group_name, opts) tuples
|
||||
"""
|
||||
auth_token_opts = (keystonemiddleware.auth_token._OPTS +
|
||||
loading.get_auth_common_conf_options())
|
||||
auth_token_opts = (_OPTS + loading.get_auth_common_conf_options())
|
||||
|
||||
return [(_base.AUTHTOKEN_GROUP, copy.deepcopy(auth_token_opts))]
|
||||
|
19
keystonemiddleware/exceptions.py
Normal file
19
keystonemiddleware/exceptions.py
Normal file
@ -0,0 +1,19 @@
|
||||
# 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.
|
||||
|
||||
|
||||
class KeystoneMiddlewareException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ConfigurationError(KeystoneMiddlewareException):
|
||||
pass
|
@ -20,15 +20,13 @@ import copy
|
||||
|
||||
from keystoneauth1 import loading
|
||||
|
||||
import keystonemiddleware.auth_token
|
||||
from keystonemiddleware.auth_token import _auth
|
||||
from keystonemiddleware.auth_token import _base
|
||||
from keystonemiddleware.auth_token import _opts
|
||||
|
||||
auth_token_opts = [
|
||||
(_base.AUTHTOKEN_GROUP,
|
||||
keystonemiddleware.auth_token._OPTS +
|
||||
_auth.OPTS +
|
||||
loading.get_auth_common_conf_options())
|
||||
_opts._OPTS + _auth.OPTS + loading.get_auth_common_conf_options())
|
||||
]
|
||||
|
||||
|
||||
|
@ -33,7 +33,6 @@ import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils import timeutils
|
||||
from oslotest import createfile
|
||||
import six
|
||||
import testresources
|
||||
import testtools
|
||||
@ -499,7 +498,7 @@ class GeneralAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
|
||||
self.assertEqual(datetime.timedelta(seconds=24),
|
||||
middleware._revocations._cache_timeout)
|
||||
self.assertEqual(False, middleware._include_service_catalog)
|
||||
self.assertEqual('0', middleware._conf['nonexsit_option'])
|
||||
self.assertEqual('0', middleware._conf.get('nonexsit_option'))
|
||||
|
||||
def test_deprecated_conf_values(self):
|
||||
servers = 'localhost:11211'
|
||||
@ -509,7 +508,7 @@ class GeneralAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest,
|
||||
}
|
||||
|
||||
middleware = auth_token.AuthProtocol(self.fake_app, conf)
|
||||
self.assertEqual([servers], middleware._conf_get('memcached_servers'))
|
||||
self.assertEqual([servers], middleware._conf.get('memcached_servers'))
|
||||
|
||||
def test_conf_values_type_convert_with_wrong_value(self):
|
||||
conf = {
|
||||
@ -2534,86 +2533,5 @@ class TestAuthPluginUserAgentGeneration(BaseAuthTokenMiddlewareTest):
|
||||
self.assertThat(sess.user_agent, matchers.StartsWith(expected_ua))
|
||||
|
||||
|
||||
class TestAuthPluginLocalOsloConfig(BaseAuthTokenMiddlewareTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAuthPluginLocalOsloConfig, self).setUp()
|
||||
self.project = uuid.uuid4().hex
|
||||
|
||||
# NOTE(cdent): The options below are selected from those
|
||||
# which are statically registered by auth_token middleware
|
||||
# in the 'keystone_authtoken' group. Additional options, from
|
||||
# plugins, are registered dynamically so must not be used here.
|
||||
self.oslo_options = {
|
||||
'auth_uri': uuid.uuid4().hex,
|
||||
'identity_uri': uuid.uuid4().hex,
|
||||
}
|
||||
|
||||
self.local_oslo_config = cfg.ConfigOpts()
|
||||
self.local_oslo_config.register_group(cfg.OptGroup(
|
||||
name='keystone_authtoken'))
|
||||
self.local_oslo_config.register_opts(auth_token._OPTS,
|
||||
group='keystone_authtoken')
|
||||
self.local_oslo_config.register_opts(auth_token._auth.OPTS,
|
||||
group='keystone_authtoken')
|
||||
for option, value in self.oslo_options.items():
|
||||
self.local_oslo_config.set_override(option, value,
|
||||
'keystone_authtoken')
|
||||
self.local_oslo_config(args=[], project=self.project)
|
||||
|
||||
self.file_options = {
|
||||
'auth_type': 'password',
|
||||
'auth_uri': uuid.uuid4().hex,
|
||||
'password': uuid.uuid4().hex,
|
||||
}
|
||||
|
||||
content = ("[keystone_authtoken]\n"
|
||||
"auth_type=%(auth_type)s\n"
|
||||
"auth_uri=%(auth_uri)s\n"
|
||||
"auth_url=%(auth_uri)s\n"
|
||||
"password=%(password)s\n" % self.file_options)
|
||||
self.conf_file_fixture = self.useFixture(
|
||||
createfile.CreateFileWithContent(self.project, content))
|
||||
|
||||
def test_project_in_local_oslo_configuration(self):
|
||||
conf = {'oslo_config_project': self.project,
|
||||
'oslo_config_file': self.conf_file_fixture.path}
|
||||
app = self._create_app(conf, uuid.uuid4().hex)
|
||||
for option in self.file_options:
|
||||
self.assertEqual(self.file_options[option],
|
||||
app._conf_get(option), option)
|
||||
|
||||
def test_passed_oslo_configuration(self):
|
||||
conf = {'oslo_config_config': self.local_oslo_config}
|
||||
app = self._create_app(conf, uuid.uuid4().hex)
|
||||
for option in self.oslo_options:
|
||||
self.assertEqual(self.oslo_options[option],
|
||||
app._conf_get(option))
|
||||
|
||||
def test_passed_olso_configuration_wins(self):
|
||||
"""oslo_config_config has precedence over oslo_config_project."""
|
||||
conf = {'oslo_config_project': self.project,
|
||||
'oslo_config_config': self.local_oslo_config,
|
||||
'oslo_config_file': self.conf_file_fixture.path}
|
||||
app = self._create_app(conf, uuid.uuid4().hex)
|
||||
for option in self.oslo_options:
|
||||
self.assertEqual(self.oslo_options[option],
|
||||
app._conf_get(option))
|
||||
self.assertNotEqual(self.file_options['auth_uri'],
|
||||
app._conf_get('auth_uri'))
|
||||
|
||||
def _create_app(self, conf, project_version):
|
||||
fake_pkg_resources = mock.Mock()
|
||||
fake_pkg_resources.get_distribution().version = project_version
|
||||
|
||||
body = uuid.uuid4().hex
|
||||
with mock.patch('keystonemiddleware.auth_token.pkg_resources',
|
||||
new=fake_pkg_resources):
|
||||
# use_global_conf is poorly named. What it means is
|
||||
# don't use the config created in test setUp.
|
||||
return self.create_simple_middleware(body=body, conf=conf,
|
||||
use_global_conf=True)
|
||||
|
||||
|
||||
def load_tests(loader, tests, pattern):
|
||||
return testresources.OptimisingTestSuite(tests)
|
||||
|
112
keystonemiddleware/tests/unit/auth_token/test_config.py
Normal file
112
keystonemiddleware/tests/unit/auth_token/test_config.py
Normal file
@ -0,0 +1,112 @@
|
||||
# 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
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslotest import createfile
|
||||
|
||||
from keystonemiddleware.auth_token import _auth
|
||||
from keystonemiddleware.auth_token import _opts
|
||||
from keystonemiddleware.tests.unit.auth_token import base
|
||||
|
||||
|
||||
def conf_get(app, *args, **kwargs):
|
||||
return app._conf.get(*args, **kwargs)
|
||||
|
||||
|
||||
class TestAuthPluginLocalOsloConfig(base.BaseAuthTokenTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAuthPluginLocalOsloConfig, self).setUp()
|
||||
self.project = uuid.uuid4().hex
|
||||
|
||||
# NOTE(cdent): The options below are selected from those
|
||||
# which are statically registered by auth_token middleware
|
||||
# in the 'keystone_authtoken' group. Additional options, from
|
||||
# plugins, are registered dynamically so must not be used here.
|
||||
self.oslo_options = {
|
||||
'auth_uri': uuid.uuid4().hex,
|
||||
'identity_uri': uuid.uuid4().hex,
|
||||
}
|
||||
|
||||
self.local_oslo_config = cfg.ConfigOpts()
|
||||
self.local_oslo_config.register_group(
|
||||
cfg.OptGroup(name='keystone_authtoken'))
|
||||
|
||||
self.local_oslo_config.register_opts(_opts._OPTS,
|
||||
group='keystone_authtoken')
|
||||
self.local_oslo_config.register_opts(_auth.OPTS,
|
||||
group='keystone_authtoken')
|
||||
|
||||
for option, value in self.oslo_options.items():
|
||||
self.local_oslo_config.set_override(option, value,
|
||||
'keystone_authtoken')
|
||||
self.local_oslo_config(args=[], project=self.project)
|
||||
|
||||
self.file_options = {
|
||||
'auth_type': 'password',
|
||||
'auth_uri': uuid.uuid4().hex,
|
||||
'password': uuid.uuid4().hex,
|
||||
}
|
||||
|
||||
content = ("[keystone_authtoken]\n"
|
||||
"auth_type=%(auth_type)s\n"
|
||||
"auth_uri=%(auth_uri)s\n"
|
||||
"auth_url=%(auth_uri)s\n"
|
||||
"password=%(password)s\n" % self.file_options)
|
||||
|
||||
self.conf_file_fixture = self.useFixture(
|
||||
createfile.CreateFileWithContent(self.project, content))
|
||||
|
||||
def _create_app(self, conf, project_version=None):
|
||||
if not project_version:
|
||||
project_version = uuid.uuid4().hex
|
||||
|
||||
fake_pkg_resources = mock.Mock()
|
||||
fake_pkg_resources.get_distribution().version = project_version
|
||||
|
||||
body = uuid.uuid4().hex
|
||||
with mock.patch('keystonemiddleware.auth_token.pkg_resources',
|
||||
new=fake_pkg_resources):
|
||||
# use_global_conf is poorly named. What it means is
|
||||
# don't use the config created in test setUp.
|
||||
return self.create_simple_middleware(body=body, conf=conf,
|
||||
use_global_conf=True)
|
||||
|
||||
def test_project_in_local_oslo_configuration(self):
|
||||
conf = {'oslo_config_project': self.project,
|
||||
'oslo_config_file': self.conf_file_fixture.path}
|
||||
app = self._create_app(conf)
|
||||
for option in self.file_options:
|
||||
self.assertEqual(self.file_options[option],
|
||||
conf_get(app, option), option)
|
||||
|
||||
def test_passed_oslo_configuration(self):
|
||||
conf = {'oslo_config_config': self.local_oslo_config}
|
||||
app = self._create_app(conf)
|
||||
for option in self.oslo_options:
|
||||
self.assertEqual(self.oslo_options[option],
|
||||
conf_get(app, option))
|
||||
|
||||
def test_passed_olso_configuration_wins(self):
|
||||
"""oslo_config_config has precedence over oslo_config_project."""
|
||||
conf = {'oslo_config_project': self.project,
|
||||
'oslo_config_config': self.local_oslo_config,
|
||||
'oslo_config_file': self.conf_file_fixture.path}
|
||||
app = self._create_app(conf)
|
||||
for option in self.oslo_options:
|
||||
self.assertEqual(self.oslo_options[option],
|
||||
conf_get(app, option))
|
||||
self.assertNotEqual(self.file_options['auth_uri'],
|
||||
conf_get(app, 'auth_uri'))
|
Loading…
x
Reference in New Issue
Block a user