diff --git a/keystone/tests/unit/test_revoke.py b/keystone/tests/unit/test_revoke.py index 355bcdedaf..dce4979af7 100644 --- a/keystone/tests/unit/test_revoke.py +++ b/keystone/tests/unit/test_revoke.py @@ -24,7 +24,7 @@ from keystone.models import revoke_model from keystone.revoke.backends import sql from keystone.tests import unit from keystone.tests.unit import test_backend_sql -from keystone.token import provider +from keystone.token.providers import common def _new_id(): @@ -245,7 +245,7 @@ class RevokeTests(object): # check to make sure that list_events matches the token to the event we # just revoked. first_token = _sample_blank_token() - first_token['audit_id'] = provider.random_urlsafe_str() + first_token['audit_id'] = common.random_urlsafe_str() add_event(events, revoke_model.RevokeEvent( audit_id=first_token['audit_id'])) self.revoke_api.revoke_by_audit_id( @@ -258,7 +258,7 @@ class RevokeTests(object): # sure that list events only finds 1 match since there are 2 and they # dont both have different populated audit_id fields second_token = _sample_blank_token() - second_token['audit_id'] = provider.random_urlsafe_str() + second_token['audit_id'] = common.random_urlsafe_str() add_event(events, revoke_model.RevokeEvent( audit_id=second_token['audit_id'])) self.revoke_api.revoke_by_audit_id( @@ -294,7 +294,7 @@ class RevokeTests(object): first_token = _sample_blank_token() first_token['user_id'] = uuid.uuid4().hex first_token['project_id'] = uuid.uuid4().hex - first_token['audit_id'] = provider.random_urlsafe_str() + first_token['audit_id'] = common.random_urlsafe_str() # revoke event and then verify that that there is only one revocation # and verify the only revoked event is the token add_event(events, revoke_model.RevokeEvent( @@ -327,7 +327,7 @@ class RevokeTests(object): fourth_token = _sample_blank_token() fourth_token['user_id'] = uuid.uuid4().hex fourth_token['project_id'] = uuid.uuid4().hex - fourth_token['audit_id'] = provider.random_urlsafe_str() + fourth_token['audit_id'] = common.random_urlsafe_str() add_event(events, revoke_model.RevokeEvent( project_id=fourth_token['project_id'], audit_id=fourth_token['audit_id'])) @@ -544,7 +544,7 @@ class RevokeListTests(unit.TestCase): self._user_field_test('trustor_id') def test_revoke_by_audit_id(self): - audit_id = provider.audit_info(parent_audit_id=None)[0] + audit_id = common.build_audit_info(parent_audit_id=None)[0] token_data_1 = _sample_blank_token() # Audit ID and Audit Chain ID are populated with the same value # if the token is an original token @@ -553,7 +553,7 @@ class RevokeListTests(unit.TestCase): event = self._revoke_by_audit_id(audit_id) self._assertTokenRevoked(token_data_1) - audit_id_2 = provider.audit_info(parent_audit_id=audit_id)[0] + audit_id_2 = common.build_audit_info(parent_audit_id=audit_id)[0] token_data_2 = _sample_blank_token() token_data_2['audit_id'] = audit_id_2 token_data_2['audit_chain_id'] = audit_id @@ -563,7 +563,7 @@ class RevokeListTests(unit.TestCase): self._assertTokenNotRevoked(token_data_1) def test_revoke_by_audit_chain_id(self): - audit_id = provider.audit_info(parent_audit_id=None)[0] + audit_id = common.build_audit_info(parent_audit_id=None)[0] token_data_1 = _sample_blank_token() # Audit ID and Audit Chain ID are populated with the same value # if the token is an original token @@ -572,7 +572,7 @@ class RevokeListTests(unit.TestCase): event = self._revoke_by_audit_chain_id(audit_id) self._assertTokenRevoked(token_data_1) - audit_id_2 = provider.audit_info(parent_audit_id=audit_id)[0] + audit_id_2 = common.build_audit_info(parent_audit_id=audit_id)[0] token_data_2 = _sample_blank_token() token_data_2['audit_id'] = audit_id_2 token_data_2['audit_chain_id'] = audit_id diff --git a/keystone/tests/unit/token/test_provider.py b/keystone/tests/unit/token/test_common.py similarity index 65% rename from keystone/tests/unit/token/test_provider.py rename to keystone/tests/unit/token/test_common.py index 7093f3ba86..717cd23489 100644 --- a/keystone/tests/unit/token/test_provider.py +++ b/keystone/tests/unit/token/test_common.py @@ -10,21 +10,16 @@ # License for the specific language governing permissions and limitations # under the License. -import six from six.moves import urllib from keystone.tests import unit -from keystone.token import provider +from keystone.token.providers import common -class TestRandomStrings(unit.BaseTestCase): +class TestTokenProvidersCommon(unit.TestCase): + def setUp(self): + super(TestTokenProvidersCommon, self).setUp() + def test_strings_are_url_safe(self): - s = provider.random_urlsafe_str() + s = common.random_urlsafe_str() self.assertEqual(s, urllib.parse.quote_plus(s)) - - def test_strings_can_be_converted_to_bytes(self): - s = provider.random_urlsafe_str() - self.assertIsInstance(s, six.text_type) - - b = provider.random_urlsafe_str_to_bytes(s) - self.assertIsInstance(b, six.binary_type) diff --git a/keystone/tests/unit/token/test_fernet_provider.py b/keystone/tests/unit/token/test_fernet_provider.py index cb9dc8fe2d..70a194d2b0 100644 --- a/keystone/tests/unit/token/test_fernet_provider.py +++ b/keystone/tests/unit/token/test_fernet_provider.py @@ -18,6 +18,7 @@ import uuid import msgpack from oslo_utils import timeutils +import six from six.moves import urllib from keystone.common import fernet_utils @@ -28,7 +29,7 @@ from keystone.federation import constants as federation_constants from keystone.tests import unit from keystone.tests.unit import ksfixtures from keystone.tests.unit.ksfixtures import database -from keystone.token import provider +from keystone.token.providers import common from keystone.token.providers import fernet from keystone.token.providers.fernet import token_formatters @@ -309,6 +310,13 @@ class TestPayloads(unit.TestCase): return self.assertCloseEnoughForGovernmentWork(exp_time, actual_time, delta=1e-05) + def test_strings_can_be_converted_to_bytes(self): + s = common.random_urlsafe_str() + self.assertIsInstance(s, six.text_type) + + b = token_formatters.BasePayload.random_urlsafe_str_to_bytes(s) + self.assertIsInstance(b, six.binary_type) + def test_uuid_hex_to_byte_conversions(self): payload_cls = token_formatters.BasePayload @@ -359,7 +367,7 @@ class TestPayloads(unit.TestCase): exp_user_id = exp_user_id or uuid.uuid4().hex exp_methods = exp_methods or ['password'] exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True) - exp_audit_ids = [provider.random_urlsafe_str()] + exp_audit_ids = [common.random_urlsafe_str()] payload = payload_class.assemble( exp_user_id, exp_methods, exp_project_id, exp_domain_id, diff --git a/keystone/token/controllers.py b/keystone/token/controllers.py index 4b164fdf0f..64f233a6ad 100644 --- a/keystone/token/controllers.py +++ b/keystone/token/controllers.py @@ -29,8 +29,7 @@ import keystone.conf from keystone import exception from keystone.i18n import _ from keystone.models import token_model -from keystone.token import provider -from keystone.token import providers +from keystone.token.providers import common CONF = keystone.conf.CONF @@ -188,7 +187,7 @@ class Auth(controller.V2Controller): # v3_to_v2_token, because federated tokens aren't supported by # v2.0 (the same applies to OAuth tokens, domain-scoped tokens, # etc..). - v2_helper = providers.common.V2TokenDataHelper() + v2_helper = common.V2TokenDataHelper() v2_helper.v3_to_v2_token(v3_token_data, old_token) token_model_ref = token_model.KeystoneToken( token_id=old_token, @@ -321,7 +320,7 @@ class Auth(controller.V2Controller): tenant_ref, metadata_ref['roles'] = self._get_project_roles_and_ref( user_id, tenant_id) - expiry = provider.default_expire_time() + expiry = common.default_expire_time() bind = None audit_id = None return (user_ref, tenant_ref, metadata_ref, expiry, bind, audit_id) @@ -348,7 +347,7 @@ class Auth(controller.V2Controller): tenant_ref, metadata_ref['roles'] = self._get_project_roles_and_ref( user_id, tenant_id) - expiry = provider.default_expire_time() + expiry = common.default_expire_time() bind = None if ('kerberos' in CONF.token.bind and request.environ.get('AUTH_TYPE', '').lower() == 'negotiate'): @@ -439,7 +438,7 @@ class Auth(controller.V2Controller): """ v3_token_response = self.token_provider_api.validate_token(token_id) - v2_helper = providers.common.V2TokenDataHelper() + v2_helper = common.V2TokenDataHelper() token = v2_helper.v3_to_v2_token(v3_token_response, token_id) belongs_to = request.params.get('belongsTo') if belongs_to: @@ -458,7 +457,7 @@ class Auth(controller.V2Controller): """ # TODO(ayoung) validate against revocation API v3_token_response = self.token_provider_api.validate_token(token_id) - v2_helper = providers.common.V2TokenDataHelper() + v2_helper = common.V2TokenDataHelper() token = v2_helper.v3_to_v2_token(v3_token_response, token_id) belongs_to = request.params.get('belongsTo') if belongs_to: diff --git a/keystone/token/persistence/backends/kvs.py b/keystone/token/persistence/backends/kvs.py index 051030f572..3d2a5018e9 100644 --- a/keystone/token/persistence/backends/kvs.py +++ b/keystone/token/persistence/backends/kvs.py @@ -27,7 +27,7 @@ import keystone.conf from keystone import exception from keystone.i18n import _, _LE, _LW from keystone import token -from keystone.token import provider +from keystone.token.providers import common CONF = keystone.conf.CONF @@ -109,7 +109,7 @@ class Token(token.persistence.TokenDriverBase): data_copy = copy.deepcopy(data) ptk = self._prefix_token_id(token_id) if not data_copy.get('expires'): - data_copy['expires'] = provider.default_expire_time() + data_copy['expires'] = common.default_expire_time() if not data_copy.get('user_id'): data_copy['user_id'] = data_copy['user']['id'] diff --git a/keystone/token/persistence/backends/sql.py b/keystone/token/persistence/backends/sql.py index 1853ccbf1e..920dceeb11 100644 --- a/keystone/token/persistence/backends/sql.py +++ b/keystone/token/persistence/backends/sql.py @@ -23,7 +23,7 @@ import keystone.conf from keystone import exception from keystone.i18n import _LI from keystone import token -from keystone.token import provider +from keystone.token.providers import common CONF = keystone.conf.CONF @@ -95,7 +95,7 @@ class Token(token.persistence.TokenDriverBase): def create_token(self, token_id, data): data_copy = copy.deepcopy(data) if not data_copy.get('expires'): - data_copy['expires'] = provider.default_expire_time() + data_copy['expires'] = common.default_expire_time() if not data_copy.get('user_id'): data_copy['user_id'] = data_copy['user']['id'] diff --git a/keystone/token/provider.py b/keystone/token/provider.py index 4886191d8a..26a5691f60 100644 --- a/keystone/token/provider.py +++ b/keystone/token/provider.py @@ -14,10 +14,7 @@ """Token provider interface.""" -import base64 -import datetime import sys -import uuid from oslo_log import log from oslo_utils import timeutils @@ -53,76 +50,6 @@ V3 = token_model.V3 VERSIONS = token_model.VERSIONS -def base64_encode(s): - """Encode a URL-safe string. - - :type s: six.text_type - :rtype: six.text_type - - """ - # urlsafe_b64encode() returns six.binary_type so need to convert to - # six.text_type, might as well do it before stripping. - return base64.urlsafe_b64encode(s).decode('utf-8').rstrip('=') - - -def random_urlsafe_str(): - """Generate a random URL-safe string. - - :rtype: six.text_type - - """ - # chop the padding (==) off the end of the encoding to save space - return base64.urlsafe_b64encode(uuid.uuid4().bytes)[:-2].decode('utf-8') - - -def random_urlsafe_str_to_bytes(s): - """Convert a string from :func:`random_urlsafe_str()` to six.binary_type. - - :type s: six.text_type - :rtype: six.binary_type - - """ - # urlsafe_b64decode() requires str, unicode isn't accepted. - s = str(s) - - # restore the padding (==) at the end of the string - return base64.urlsafe_b64decode(s + '==') - - -def default_expire_time(): - """Determine when a fresh token should expire. - - Expiration time varies based on configuration (see ``[token] expiration``). - - :returns: a naive UTC datetime.datetime object - - """ - expire_delta = datetime.timedelta(seconds=CONF.token.expiration) - expires_at = timeutils.utcnow() + expire_delta - return expires_at.replace(microsecond=0) - - -def audit_info(parent_audit_id): - """Build the audit data for a token. - - If ``parent_audit_id`` is None, the list will be one element in length - containing a newly generated audit_id. - - If ``parent_audit_id`` is supplied, the list will be two elements in length - containing a newly generated audit_id and the ``parent_audit_id``. The - ``parent_audit_id`` will always be element index 1 in the resulting - list. - - :param parent_audit_id: the audit of the original token in the chain - :type parent_audit_id: str - :returns: Keystone token audit data - """ - audit_id = random_urlsafe_str() - if parent_audit_id is not None: - return [audit_id, parent_audit_id] - return [audit_id] - - @dependency.provider('token_provider_api') @dependency.requires('assignment_api', 'revoke_api') class Manager(manager.Manager): diff --git a/keystone/token/providers/common.py b/keystone/token/providers/common.py index fefb9c9b4b..f01d41fe19 100644 --- a/keystone/token/providers/common.py +++ b/keystone/token/providers/common.py @@ -12,8 +12,15 @@ # License for the specific language governing permissions and limitations # under the License. +from __future__ import absolute_import + +import base64 +import datetime +import uuid + from oslo_log import log from oslo_serialization import jsonutils +from oslo_utils import timeutils import six from six.moves.urllib import parse @@ -24,8 +31,7 @@ import keystone.conf from keystone import exception from keystone.federation import constants as federation_constants from keystone.i18n import _ -from keystone import token -from keystone.token import provider +from keystone.models import token_model from keystone.token.providers import base @@ -33,6 +39,50 @@ LOG = log.getLogger(__name__) CONF = keystone.conf.CONF +def default_expire_time(): + """Determine when a fresh token should expire. + + Expiration time varies based on configuration (see ``[token] expiration``). + + :returns: a naive UTC datetime.datetime object + + """ + expire_delta = datetime.timedelta(seconds=CONF.token.expiration) + expires_at = timeutils.utcnow() + expire_delta + return expires_at.replace(microsecond=0) + + +def random_urlsafe_str(): + """Generate a random URL-safe string. + + :rtype: six.text_type + + """ + # chop the padding (==) off the end of the encoding to save space + return base64.urlsafe_b64encode(uuid.uuid4().bytes)[:-2].decode('utf-8') + + +def build_audit_info(parent_audit_id=None): + """Build the audit data for a token. + + If ``parent_audit_id`` is None, the list will be one element in length + containing a newly generated audit_id. + + If ``parent_audit_id`` is supplied, the list will be two elements in length + containing a newly generated audit_id and the ``parent_audit_id``. The + ``parent_audit_id`` will always be element index 1 in the resulting + list. + + :param parent_audit_id: the audit of the original token in the chain + :type parent_audit_id: str + :returns: Keystone token audit data + """ + audit_id = random_urlsafe_str() + if parent_audit_id is not None: + return [audit_id, parent_audit_id] + return [audit_id] + + @dependency.requires('catalog_api', 'resource_api', 'assignment_api', 'trust_api', 'identity_api') class V2TokenDataHelper(object): @@ -165,7 +215,7 @@ class V2TokenDataHelper(object): metadata_ref = token_ref['metadata'] if roles_ref is None: roles_ref = [] - expires = token_ref.get('expires', provider.default_expire_time()) + expires = token_ref.get('expires', default_expire_time()) if expires is not None: if not isinstance(expires, six.text_type): expires = utils.isotime(expires) @@ -177,7 +227,7 @@ class V2TokenDataHelper(object): audit_info = token_audit if audit_info is None: - audit_info = provider.audit_info(token_ref.get('parent_audit_id')) + audit_info = build_audit_info(token_ref.get('parent_audit_id')) o = {'access': {'token': {'id': token_ref['id'], 'expires': expires, @@ -550,7 +600,7 @@ class V3TokenDataHelper(object): def _populate_token_dates(self, token_data, expires=None, issued_at=None): if not expires: - expires = provider.default_expire_time() + expires = default_expire_time() if not isinstance(expires, six.string_types): expires = utils.isotime(expires, subsecond=True) token_data['expires_at'] = expires @@ -559,7 +609,7 @@ class V3TokenDataHelper(object): def _populate_audit_info(self, token_data, audit_info=None): if audit_info is None or isinstance(audit_info, six.string_types): - token_data['audit_ids'] = provider.audit_info(audit_info) + token_data['audit_ids'] = build_audit_info(audit_info) elif isinstance(audit_info, list): token_data['audit_ids'] = audit_info else: @@ -612,16 +662,16 @@ class BaseProvider(base.Provider): def get_token_version(self, token_data): if token_data and isinstance(token_data, dict): if 'token_version' in token_data: - if token_data['token_version'] in token.provider.VERSIONS: + if token_data['token_version'] in token_model.VERSIONS: return token_data['token_version'] # FIXME(morganfainberg): deprecate the following logic in future # revisions. It is better to just specify the token_version in # the token_data itself. This way we can support future versions # that might have the same fields. if 'access' in token_data: - return token.provider.V2 + return token_model.V2 if 'token' in token_data and 'methods' in token_data['token']: - return token.provider.V3 + return token_model.V3 raise exception.UnsupportedTokenVersionException() def issue_v2_token(self, token_ref, roles_ref=None, diff --git a/keystone/token/providers/fernet/token_formatters.py b/keystone/token/providers/fernet/token_formatters.py index a749334d9c..0696d909e9 100644 --- a/keystone/token/providers/fernet/token_formatters.py +++ b/keystone/token/providers/fernet/token_formatters.py @@ -29,7 +29,6 @@ from keystone.common import utils as ks_utils import keystone.conf from keystone import exception from keystone.i18n import _, _LI -from keystone.token import provider CONF = keystone.conf.CONF @@ -333,6 +332,32 @@ class BasePayload(object): # federation) return (False, value) + @classmethod + def base64_encode(cls, s): + """Encode a URL-safe string. + + :type s: six.text_type + :rtype: six.text_type + + """ + # urlsafe_b64encode() returns six.binary_type so need to convert to + # six.text_type, might as well do it before stripping. + return base64.urlsafe_b64encode(s).decode('utf-8').rstrip('=') + + @classmethod + def random_urlsafe_str_to_bytes(cls, s): + """Convert a string from :func:`random_urlsafe_str()` to six.binary_type. + + :type s: six.text_type + :rtype: six.binary_type + + """ + # urlsafe_b64decode() requires str, unicode isn't accepted. + s = str(s) + + # restore the padding (==) at the end of the string + return base64.urlsafe_b64decode(s + '==') + class UnscopedPayload(BasePayload): version = 0 @@ -347,7 +372,7 @@ class UnscopedPayload(BasePayload): b_user_id = cls.attempt_convert_uuid_hex_to_bytes(user_id) methods = auth_plugins.convert_method_list_to_integer(methods) expires_at_int = cls._convert_time_string_to_float(expires_at) - b_audit_ids = list(map(provider.random_urlsafe_str_to_bytes, + b_audit_ids = list(map(cls.random_urlsafe_str_to_bytes, audit_ids)) return (b_user_id, methods, expires_at_int, b_audit_ids) @@ -358,7 +383,7 @@ class UnscopedPayload(BasePayload): user_id = cls.convert_uuid_bytes_to_hex(user_id) methods = auth_plugins.convert_integer_to_method_list(payload[1]) expires_at_str = cls._convert_float_to_time_string(payload[2]) - audit_ids = list(map(provider.base64_encode, payload[3])) + audit_ids = list(map(cls.base64_encode, payload[3])) project_id = None domain_id = None trust_id = None @@ -389,7 +414,7 @@ class DomainScopedPayload(BasePayload): else: raise expires_at_int = cls._convert_time_string_to_float(expires_at) - b_audit_ids = list(map(provider.random_urlsafe_str_to_bytes, + b_audit_ids = list(map(cls.random_urlsafe_str_to_bytes, audit_ids)) return (b_user_id, methods, b_domain_id, expires_at_int, b_audit_ids) @@ -408,7 +433,7 @@ class DomainScopedPayload(BasePayload): else: raise expires_at_str = cls._convert_float_to_time_string(payload[3]) - audit_ids = list(map(provider.base64_encode, payload[4])) + audit_ids = list(map(cls.base64_encode, payload[4])) project_id = None trust_id = None federated_info = None @@ -431,7 +456,7 @@ class ProjectScopedPayload(BasePayload): methods = auth_plugins.convert_method_list_to_integer(methods) b_project_id = cls.attempt_convert_uuid_hex_to_bytes(project_id) expires_at_int = cls._convert_time_string_to_float(expires_at) - b_audit_ids = list(map(provider.random_urlsafe_str_to_bytes, + b_audit_ids = list(map(cls.random_urlsafe_str_to_bytes, audit_ids)) return (b_user_id, methods, b_project_id, expires_at_int, b_audit_ids) @@ -445,7 +470,7 @@ class ProjectScopedPayload(BasePayload): if is_stored_as_bytes: project_id = cls.convert_uuid_bytes_to_hex(project_id) expires_at_str = cls._convert_float_to_time_string(payload[3]) - audit_ids = list(map(provider.base64_encode, payload[4])) + audit_ids = list(map(cls.base64_encode, payload[4])) domain_id = None trust_id = None federated_info = None @@ -469,7 +494,7 @@ class TrustScopedPayload(BasePayload): b_project_id = cls.attempt_convert_uuid_hex_to_bytes(project_id) b_trust_id = cls.convert_uuid_hex_to_bytes(trust_id) expires_at_int = cls._convert_time_string_to_float(expires_at) - b_audit_ids = list(map(provider.random_urlsafe_str_to_bytes, + b_audit_ids = list(map(cls.random_urlsafe_str_to_bytes, audit_ids)) return (b_user_id, methods, b_project_id, expires_at_int, b_audit_ids, @@ -485,7 +510,7 @@ class TrustScopedPayload(BasePayload): if is_stored_as_bytes: project_id = cls.convert_uuid_bytes_to_hex(project_id) expires_at_str = cls._convert_float_to_time_string(payload[3]) - audit_ids = list(map(provider.base64_encode, payload[4])) + audit_ids = list(map(cls.base64_encode, payload[4])) trust_id = cls.convert_uuid_bytes_to_hex(payload[5]) domain_id = None federated_info = None @@ -523,7 +548,7 @@ class FederatedUnscopedPayload(BasePayload): federated_info['idp_id']) protocol_id = federated_info['protocol_id'] expires_at_int = cls._convert_time_string_to_float(expires_at) - b_audit_ids = list(map(provider.random_urlsafe_str_to_bytes, + b_audit_ids = list(map(cls.random_urlsafe_str_to_bytes, audit_ids)) return (b_user_id, methods, b_group_ids, b_idp_id, protocol_id, @@ -545,7 +570,7 @@ class FederatedUnscopedPayload(BasePayload): if isinstance(protocol_id, six.binary_type): protocol_id = protocol_id.decode('utf-8') expires_at_str = cls._convert_float_to_time_string(payload[5]) - audit_ids = list(map(provider.base64_encode, payload[6])) + audit_ids = list(map(cls.base64_encode, payload[6])) federated_info = dict(group_ids=group_ids, idp_id=idp_id, protocol_id=protocol_id) project_id = None @@ -572,7 +597,7 @@ class FederatedScopedPayload(FederatedUnscopedPayload): federated_info['idp_id']) protocol_id = federated_info['protocol_id'] expires_at_int = cls._convert_time_string_to_float(expires_at) - b_audit_ids = list(map(provider.random_urlsafe_str_to_bytes, + b_audit_ids = list(map(cls.random_urlsafe_str_to_bytes, audit_ids)) return (b_user_id, methods, b_scope_id, b_group_ids, b_idp_id, @@ -599,7 +624,7 @@ class FederatedScopedPayload(FederatedUnscopedPayload): idp_id = cls.convert_uuid_bytes_to_hex(idp_id) protocol_id = payload[5] expires_at_str = cls._convert_float_to_time_string(payload[6]) - audit_ids = list(map(provider.base64_encode, payload[7])) + audit_ids = list(map(cls.base64_encode, payload[7])) federated_info = dict(idp_id=idp_id, protocol_id=protocol_id, group_ids=group_ids) trust_id = None @@ -638,7 +663,7 @@ class OauthScopedPayload(BasePayload): methods = auth_plugins.convert_method_list_to_integer(methods) b_project_id = cls.attempt_convert_uuid_hex_to_bytes(project_id) expires_at_int = cls._convert_time_string_to_float(expires_at) - b_audit_ids = list(map(provider.random_urlsafe_str_to_bytes, + b_audit_ids = list(map(cls.random_urlsafe_str_to_bytes, audit_ids)) b_access_token_id = cls.attempt_convert_uuid_hex_to_bytes( access_token_id) @@ -658,7 +683,7 @@ class OauthScopedPayload(BasePayload): if is_stored_as_bytes: access_token_id = cls.convert_uuid_bytes_to_hex(access_token_id) expires_at_str = cls._convert_float_to_time_string(payload[4]) - audit_ids = list(map(provider.base64_encode, payload[5])) + audit_ids = list(map(cls.base64_encode, payload[5])) domain_id = None trust_id = None federated_info = None