c096099416
The repo is Python 3 now, so update hacking to version 3.0 which supports Python 3. Fix problems found. Update local hacking checks for new flake8. Remove hacking and friends from lower-constraints, those are not needed for co-installing. Change-Id: I59f0854c089a6ed4f0c4dad7755f946dc95ada3a
226 lines
8.9 KiB
Python
226 lines
8.9 KiB
Python
# 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 abc
|
|
|
|
import six
|
|
import six.moves.urllib.parse as urlparse
|
|
|
|
from keystoneauth1 import _utils as utils
|
|
from keystoneauth1 import discover
|
|
from keystoneauth1 import exceptions
|
|
from keystoneauth1.identity import base
|
|
|
|
|
|
LOG = utils.get_logger(__name__)
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class BaseGenericPlugin(base.BaseIdentityPlugin):
|
|
"""An identity plugin that is not version dependent.
|
|
|
|
Internally we will construct a version dependent plugin with the resolved
|
|
URL and then proxy all calls from the base plugin to the versioned one.
|
|
"""
|
|
|
|
def __init__(self, auth_url,
|
|
tenant_id=None,
|
|
tenant_name=None,
|
|
project_id=None,
|
|
project_name=None,
|
|
project_domain_id=None,
|
|
project_domain_name=None,
|
|
domain_id=None,
|
|
domain_name=None,
|
|
system_scope=None,
|
|
trust_id=None,
|
|
default_domain_id=None,
|
|
default_domain_name=None,
|
|
reauthenticate=True):
|
|
super(BaseGenericPlugin, self).__init__(auth_url=auth_url,
|
|
reauthenticate=reauthenticate)
|
|
|
|
self._project_id = project_id or tenant_id
|
|
self._project_name = project_name or tenant_name
|
|
self._project_domain_id = project_domain_id
|
|
self._project_domain_name = project_domain_name
|
|
self._domain_id = domain_id
|
|
self._domain_name = domain_name
|
|
self._system_scope = system_scope
|
|
self._trust_id = trust_id
|
|
self._default_domain_id = default_domain_id
|
|
self._default_domain_name = default_domain_name
|
|
|
|
self._plugin = None
|
|
|
|
@abc.abstractmethod
|
|
def create_plugin(self, session, version, url, raw_status=None):
|
|
"""Create a plugin from the given parameters.
|
|
|
|
This function will be called multiple times with the version and url
|
|
of a potential endpoint. If a plugin can be constructed that fits the
|
|
params then it should return it. If not return None and then another
|
|
call will be made with other available URLs.
|
|
|
|
:param session: A session object.
|
|
:type session: keystoneauth1.session.Session
|
|
:param tuple version: A tuple of the API version at the URL.
|
|
:param str url: The base URL for this version.
|
|
:param str raw_status: The status that was in the discovery field.
|
|
|
|
:returns: A plugin that can match the parameters or None if nothing.
|
|
"""
|
|
return None
|
|
|
|
@property
|
|
def _has_domain_scope(self):
|
|
"""Are there domain parameters.
|
|
|
|
Domain parameters are v3 only so returns if any are set.
|
|
|
|
:returns: True if a domain parameter is set, false otherwise.
|
|
"""
|
|
return any([self._domain_id, self._domain_name,
|
|
self._project_domain_id, self._project_domain_name])
|
|
|
|
@property
|
|
def _v2_params(self):
|
|
"""Return the parameters that are common to v2 plugins."""
|
|
return {'trust_id': self._trust_id,
|
|
'tenant_id': self._project_id,
|
|
'tenant_name': self._project_name,
|
|
'reauthenticate': self.reauthenticate}
|
|
|
|
@property
|
|
def _v3_params(self):
|
|
"""Return the parameters that are common to v3 plugins."""
|
|
return {'trust_id': self._trust_id,
|
|
'system_scope': self._system_scope,
|
|
'project_id': self._project_id,
|
|
'project_name': self._project_name,
|
|
'project_domain_id': self.project_domain_id,
|
|
'project_domain_name': self.project_domain_name,
|
|
'domain_id': self._domain_id,
|
|
'domain_name': self._domain_name,
|
|
'reauthenticate': self.reauthenticate}
|
|
|
|
@property
|
|
def project_domain_id(self):
|
|
return self._project_domain_id or self._default_domain_id
|
|
|
|
@project_domain_id.setter
|
|
def project_domain_id(self, value):
|
|
self._project_domain_id = value
|
|
|
|
@property
|
|
def project_domain_name(self):
|
|
return self._project_domain_name or self._default_domain_name
|
|
|
|
@project_domain_name.setter
|
|
def project_domain_name(self, value):
|
|
self._project_domain_name = value
|
|
|
|
def _do_create_plugin(self, session):
|
|
plugin = None
|
|
|
|
try:
|
|
disc = self.get_discovery(session,
|
|
self.auth_url,
|
|
authenticated=False)
|
|
except (exceptions.DiscoveryFailure,
|
|
exceptions.HttpError,
|
|
exceptions.SSLError,
|
|
exceptions.ConnectionError) as e:
|
|
LOG.warning('Failed to discover available identity versions when '
|
|
'contacting %s. Attempting to parse version from URL.',
|
|
self.auth_url)
|
|
|
|
url_parts = urlparse.urlparse(self.auth_url)
|
|
path = url_parts.path.lower()
|
|
|
|
if path.startswith('/v2.0'):
|
|
if self._has_domain_scope:
|
|
raise exceptions.DiscoveryFailure(
|
|
'Cannot use v2 authentication with domain scope')
|
|
plugin = self.create_plugin(session, (2, 0), self.auth_url)
|
|
elif path.startswith('/v3'):
|
|
plugin = self.create_plugin(session, (3, 0), self.auth_url)
|
|
else:
|
|
raise exceptions.DiscoveryFailure(
|
|
'Could not find versioned identity endpoints when '
|
|
'attempting to authenticate. Please check that your '
|
|
'auth_url is correct. %s' % e)
|
|
|
|
else:
|
|
# NOTE(jamielennox): version_data is always in oldest to newest
|
|
# order. This is fine normally because we explicitly skip v2 below
|
|
# if there is domain data present. With default_domain params
|
|
# though we want a v3 plugin if available and fall back to v2 so we
|
|
# have to process in reverse order. FIXME(jamielennox): if we ever
|
|
# go for another version we should reverse this logic as we always
|
|
# want to favour the newest available version.
|
|
reverse = self._default_domain_id or self._default_domain_name
|
|
disc_data = disc.version_data(reverse=bool(reverse))
|
|
|
|
v2_with_domain_scope = False
|
|
for data in disc_data:
|
|
version = data['version']
|
|
|
|
if (discover.version_match((2,), version) and
|
|
self._has_domain_scope):
|
|
# NOTE(jamielennox): if there are domain parameters there
|
|
# is no point even trying against v2 APIs.
|
|
v2_with_domain_scope = True
|
|
continue
|
|
|
|
plugin = self.create_plugin(session,
|
|
version,
|
|
data['url'],
|
|
raw_status=data['raw_status'])
|
|
|
|
if plugin:
|
|
break
|
|
if not plugin and v2_with_domain_scope:
|
|
raise exceptions.DiscoveryFailure(
|
|
'Cannot use v2 authentication with domain scope')
|
|
|
|
if plugin:
|
|
return plugin
|
|
|
|
# so there were no URLs that i could use for auth of any version.
|
|
raise exceptions.DiscoveryFailure(
|
|
'Could not find versioned identity endpoints when attempting '
|
|
'to authenticate. Please check that your auth_url is correct.')
|
|
|
|
def get_auth_ref(self, session, **kwargs):
|
|
if not self._plugin:
|
|
self._plugin = self._do_create_plugin(session)
|
|
|
|
return self._plugin.get_auth_ref(session, **kwargs)
|
|
|
|
def get_cache_id_elements(self, _implemented=False):
|
|
# NOTE(jamielennox): implemented here is just a way to make sure that
|
|
# something overrides this method. We don't want the base
|
|
# implementation to respond with a dict without the subclass modifying
|
|
# it to add their own data in case the subclass doesn't support caching
|
|
if not _implemented:
|
|
raise NotImplementedError()
|
|
|
|
return {'auth_url': self.auth_url,
|
|
'project_id': self._project_id,
|
|
'project_name': self._project_name,
|
|
'project_domain_id': self.project_domain_id,
|
|
'project_domain_name': self.project_domain_name,
|
|
'domain_id': self._domain_id,
|
|
'domain_name': self._domain_name,
|
|
'trust_id': self._trust_id}
|