Remove needs_persistence property from token providers
Since the sql token storage mechanism was removed in Rocky, we no longer need hooks in the token Manager to determine if a token needs to be retrieved from or written to a backend somewhere. Instead, token providers will need to handle storage requirements if they need them. This will result in a cleaner token provider interface. Change-Id: Icc095987d41e9c08de2f34dc657b08b98bd944e4
This commit is contained in:
parent
be644b773e
commit
1b8d0589ce
@ -54,9 +54,6 @@ class TestFernetTokenProvider(unit.TestCase):
|
|||||||
def test_supports_bind_authentication_returns_false(self):
|
def test_supports_bind_authentication_returns_false(self):
|
||||||
self.assertFalse(self.provider._supports_bind_authentication)
|
self.assertFalse(self.provider._supports_bind_authentication)
|
||||||
|
|
||||||
def test_needs_persistence_returns_false(self):
|
|
||||||
self.assertFalse(self.provider.needs_persistence())
|
|
||||||
|
|
||||||
def test_invalid_v3_token_raises_token_not_found(self):
|
def test_invalid_v3_token_raises_token_not_found(self):
|
||||||
token_id = uuid.uuid4().hex
|
token_id = uuid.uuid4().hex
|
||||||
e = self.assertRaises(
|
e = self.assertRaises(
|
||||||
|
@ -15,11 +15,9 @@
|
|||||||
"""Token provider interface."""
|
"""Token provider interface."""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import sys
|
|
||||||
|
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
import six
|
|
||||||
|
|
||||||
from keystone.common import cache
|
from keystone.common import cache
|
||||||
from keystone.common import manager
|
from keystone.common import manager
|
||||||
@ -62,7 +60,6 @@ class Manager(manager.Manager):
|
|||||||
|
|
||||||
V3 = V3
|
V3 = V3
|
||||||
VERSIONS = VERSIONS
|
VERSIONS = VERSIONS
|
||||||
_persistence_manager = None
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Manager, self).__init__(CONF.token.provider)
|
super(Manager, self).__init__(CONF.token.provider)
|
||||||
@ -99,35 +96,6 @@ class Manager(manager.Manager):
|
|||||||
notifications.register_event_callback(event, resource_type,
|
notifications.register_event_callback(event, resource_type,
|
||||||
callback_fns)
|
callback_fns)
|
||||||
|
|
||||||
@property
|
|
||||||
def _needs_persistence(self):
|
|
||||||
return self.driver.needs_persistence()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _persistence(self):
|
|
||||||
# NOTE(morganfainberg): This should not be handled via __init__ to
|
|
||||||
# avoid dependency injection oddities circular dependencies (where
|
|
||||||
# the provider manager requires the token persistence manager, which
|
|
||||||
# requires the token provider manager).
|
|
||||||
if self._persistence_manager is None:
|
|
||||||
self._persistence_manager = self._token_persistence_manager
|
|
||||||
return self._persistence_manager
|
|
||||||
|
|
||||||
def _create_token(self, token_id, token_data):
|
|
||||||
try:
|
|
||||||
if isinstance(token_data['expires'], six.string_types):
|
|
||||||
token_data['expires'] = timeutils.normalize_time(
|
|
||||||
timeutils.parse_isotime(token_data['expires']))
|
|
||||||
self._persistence.create_token(token_id, token_data)
|
|
||||||
except Exception:
|
|
||||||
exc_info = sys.exc_info()
|
|
||||||
# an identical token may have been created already.
|
|
||||||
# if so, return the token_data as it is also identical
|
|
||||||
try:
|
|
||||||
self._persistence.get_token(token_id)
|
|
||||||
except exception.TokenNotFound:
|
|
||||||
six.reraise(*exc_info)
|
|
||||||
|
|
||||||
def check_revocation_v3(self, token):
|
def check_revocation_v3(self, token):
|
||||||
try:
|
try:
|
||||||
token_data = token['token']
|
token_data = token['token']
|
||||||
@ -144,15 +112,6 @@ class Manager(manager.Manager):
|
|||||||
raise exception.TokenNotFound(_('No token in the request'))
|
raise exception.TokenNotFound(_('No token in the request'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# NOTE(lbragstad): Only go to persistent storage if we have a token
|
|
||||||
# to fetch from the backend (the driver persists the token).
|
|
||||||
# Otherwise the information about the token must be in the token
|
|
||||||
# id.
|
|
||||||
if self._needs_persistence:
|
|
||||||
token_ref = self._persistence.get_token(token_id)
|
|
||||||
# Overload the token_id variable to be a token reference
|
|
||||||
# instead.
|
|
||||||
token_id = token_ref
|
|
||||||
token_ref = self._validate_token(token_id)
|
token_ref = self._validate_token(token_id)
|
||||||
self._is_valid_token(token_ref, window_seconds=window_seconds)
|
self._is_valid_token(token_ref, window_seconds=window_seconds)
|
||||||
return token_ref
|
return token_ref
|
||||||
@ -207,18 +166,6 @@ class Manager(manager.Manager):
|
|||||||
app_cred_id=app_cred_id, include_catalog=include_catalog,
|
app_cred_id=app_cred_id, include_catalog=include_catalog,
|
||||||
parent_audit_id=parent_audit_id)
|
parent_audit_id=parent_audit_id)
|
||||||
|
|
||||||
if self._needs_persistence:
|
|
||||||
data = dict(key=token_id,
|
|
||||||
id=token_id,
|
|
||||||
expires=token_data['token']['expires_at'],
|
|
||||||
user=token_data['token']['user'],
|
|
||||||
tenant=token_data['token'].get('project'),
|
|
||||||
is_domain=is_domain,
|
|
||||||
token_data=token_data,
|
|
||||||
trust_id=trust['id'] if trust else None,
|
|
||||||
token_version=self.V3)
|
|
||||||
self._create_token(token_id, data)
|
|
||||||
|
|
||||||
if CONF.token.cache_on_issue:
|
if CONF.token.cache_on_issue:
|
||||||
# NOTE(amakarov): here and above TOKENS_REGION is to be passed
|
# NOTE(amakarov): here and above TOKENS_REGION is to be passed
|
||||||
# to serve as required positional "self" argument. It's ignored,
|
# to serve as required positional "self" argument. It's ignored,
|
||||||
@ -255,9 +202,6 @@ class Manager(manager.Manager):
|
|||||||
else:
|
else:
|
||||||
PROVIDERS.revoke_api.revoke_by_audit_id(token_ref.audit_id)
|
PROVIDERS.revoke_api.revoke_by_audit_id(token_ref.audit_id)
|
||||||
|
|
||||||
if CONF.token.revoke_by_id and self._needs_persistence:
|
|
||||||
self._persistence.delete_token(token_id=token_id)
|
|
||||||
|
|
||||||
# FIXME(morganfainberg): Does this cache actually need to be
|
# FIXME(morganfainberg): Does this cache actually need to be
|
||||||
# invalidated? We maintain a cached revocation list, which should be
|
# invalidated? We maintain a cached revocation list, which should be
|
||||||
# consulted before accepting a token as valid. For now we will
|
# consulted before accepting a token as valid. For now we will
|
||||||
|
@ -23,16 +23,6 @@ from keystone import exception
|
|||||||
class Provider(object):
|
class Provider(object):
|
||||||
"""Interface description for a Token provider."""
|
"""Interface description for a Token provider."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
|
||||||
def needs_persistence(self):
|
|
||||||
"""Determine if the token should be persisted.
|
|
||||||
|
|
||||||
If the token provider requires that the token be persisted to a
|
|
||||||
backend this should return True, otherwise return False.
|
|
||||||
|
|
||||||
"""
|
|
||||||
raise exception.NotImplemented() # pragma: no cover
|
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_token_version(self, token_data):
|
def get_token_version(self, token_data):
|
||||||
"""Return the version of the given token data.
|
"""Return the version of the given token data.
|
||||||
|
@ -641,78 +641,45 @@ class BaseProvider(provider_api.ProviderAPIMixin, base.Provider):
|
|||||||
return token_ref
|
return token_ref
|
||||||
|
|
||||||
def validate_token(self, token_id):
|
def validate_token(self, token_id):
|
||||||
if self.needs_persistence():
|
try:
|
||||||
token_ref = token_id
|
(user_id, methods, audit_ids, system, domain_id,
|
||||||
token_data = token_ref.get('token_data')
|
project_id, trust_id, federated_info, access_token_id,
|
||||||
user_id = token_ref['user_id']
|
issued_at, expires_at) = (
|
||||||
methods = token_data['token']['methods']
|
self.token_formatter.validate_token(token_id))
|
||||||
bind = token_data['token'].get('bind')
|
except exception.ValidationError as e:
|
||||||
issued_at = token_data['token']['issued_at']
|
raise exception.TokenNotFound(e)
|
||||||
expires_at = token_data['token']['expires_at']
|
|
||||||
audit_ids = token_data['token'].get('audit_ids')
|
|
||||||
system = token_data['token'].get('system', {}).get('all')
|
|
||||||
if system:
|
|
||||||
system = 'all'
|
|
||||||
domain_id = token_data['token'].get('domain', {}).get('id')
|
|
||||||
project_id = token_data['token'].get('project', {}).get('id')
|
|
||||||
access_token = None
|
|
||||||
if token_data['token'].get('OS-OAUTH1'):
|
|
||||||
access_token = {
|
|
||||||
'id': token_data['token'].get('OS-OAUTH1', {}).get(
|
|
||||||
'access_token_id'
|
|
||||||
),
|
|
||||||
'consumer_id': token_data['token'].get(
|
|
||||||
'OS-OAUTH1', {}
|
|
||||||
).get('consumer_id')
|
|
||||||
}
|
|
||||||
trust_ref = None
|
|
||||||
trust_id = token_ref.get('trust_id')
|
|
||||||
if trust_id:
|
|
||||||
trust_ref = PROVIDERS.trust_api.get_trust(trust_id)
|
|
||||||
token_dict = None
|
|
||||||
if token_data['token']['user'].get(
|
|
||||||
federation_constants.FEDERATION):
|
|
||||||
token_dict = {'user': token_ref['user']}
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
(user_id, methods, audit_ids, system, domain_id,
|
|
||||||
project_id, trust_id, federated_info, access_token_id,
|
|
||||||
issued_at, expires_at) = (
|
|
||||||
self.token_formatter.validate_token(token_id))
|
|
||||||
except exception.ValidationError as e:
|
|
||||||
raise exception.TokenNotFound(e)
|
|
||||||
|
|
||||||
bind = None
|
bind = None
|
||||||
token_dict = None
|
token_dict = None
|
||||||
trust_ref = None
|
trust_ref = None
|
||||||
if federated_info:
|
if federated_info:
|
||||||
# NOTE(lbragstad): We need to rebuild information about the
|
# NOTE(lbragstad): We need to rebuild information about the
|
||||||
# federated token as well as the federated token roles. This is
|
# federated token as well as the federated token roles. This is
|
||||||
# because when we validate a non-persistent token, we don't
|
# because when we validate a non-persistent token, we don't
|
||||||
# have a token reference to pull the federated token
|
# have a token reference to pull the federated token
|
||||||
# information out of. As a result, we have to extract it from
|
# information out of. As a result, we have to extract it from
|
||||||
# the token itself and rebuild the federated context. These
|
# the token itself and rebuild the federated context. These
|
||||||
# private methods currently live in the
|
# private methods currently live in the
|
||||||
# keystone.token.providers.fernet.Provider() class.
|
# keystone.token.providers.fernet.Provider() class.
|
||||||
token_dict = self._rebuild_federated_info(
|
token_dict = self._rebuild_federated_info(
|
||||||
federated_info, user_id
|
federated_info, user_id
|
||||||
|
)
|
||||||
|
if project_id or domain_id:
|
||||||
|
self._rebuild_federated_token_roles(
|
||||||
|
token_dict,
|
||||||
|
federated_info,
|
||||||
|
user_id,
|
||||||
|
project_id,
|
||||||
|
domain_id
|
||||||
)
|
)
|
||||||
if project_id or domain_id:
|
if trust_id:
|
||||||
self._rebuild_federated_token_roles(
|
trust_ref = PROVIDERS.trust_api.get_trust(trust_id)
|
||||||
token_dict,
|
|
||||||
federated_info,
|
|
||||||
user_id,
|
|
||||||
project_id,
|
|
||||||
domain_id
|
|
||||||
)
|
|
||||||
if trust_id:
|
|
||||||
trust_ref = PROVIDERS.trust_api.get_trust(trust_id)
|
|
||||||
|
|
||||||
access_token = None
|
access_token = None
|
||||||
if access_token_id:
|
if access_token_id:
|
||||||
access_token = PROVIDERS.oauth_api.get_access_token(
|
access_token = PROVIDERS.oauth_api.get_access_token(
|
||||||
access_token_id
|
access_token_id
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.v3_token_data_helper.get_token_data(
|
return self.v3_token_data_helper.get_token_data(
|
||||||
user_id,
|
user_id,
|
||||||
|
@ -44,10 +44,6 @@ class Provider(common.BaseProvider):
|
|||||||
|
|
||||||
self.token_formatter = tf.TokenFormatter()
|
self.token_formatter = tf.TokenFormatter()
|
||||||
|
|
||||||
def needs_persistence(self):
|
|
||||||
"""Should the token be written to a backend."""
|
|
||||||
return False
|
|
||||||
|
|
||||||
def issue_token(self, *args, **kwargs):
|
def issue_token(self, *args, **kwargs):
|
||||||
token_id, token_data = super(Provider, self).issue_token(
|
token_id, token_data = super(Provider, self).issue_token(
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The token provider API has removed the ``needs_persistence`` property from
|
||||||
|
the abstract interface. Token providers are expected to handle persistence
|
||||||
|
requirement if needed. This will require out-of-tree token providers to
|
||||||
|
remove the unused property and handle token storage.
|
Loading…
Reference in New Issue
Block a user