Common arguments for fernet payloads assembly

Large sets of if/else statements are an indication that
object-oriented programming should be used instead.

As a first step towards making the fernet payload classes more
object-oriented, this change normalizes the arguments to the
"assemble" method so that create_token() doesn't have to know
details about the payload class.

Since all the methods work the same, the subclass implementations
don't need their own docstrings.

Change-Id: Id71e5cd91496e66849615f6f1ecdb1e5ad1129b2
This commit is contained in:
Brant Knudson 2015-10-01 16:55:24 -05:00
parent c106db9912
commit 1d1c6b0b6d
2 changed files with 148 additions and 151 deletions

View File

@ -303,9 +303,14 @@ class TestPayloads(unit.TestCase):
exp_methods = ['password']
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
exp_audit_ids = [provider.random_urlsafe_str()]
project_id = None
domain_id = None
trust_id = None
federated_info = None
payload = token_formatters.UnscopedPayload.assemble(
exp_user_id, exp_methods, exp_expires_at, exp_audit_ids)
exp_user_id, exp_methods, project_id, domain_id, exp_expires_at,
exp_audit_ids, trust_id, federated_info)
(user_id, methods, expires_at, audit_ids) = (
token_formatters.UnscopedPayload.disassemble(payload))
@ -321,10 +326,13 @@ class TestPayloads(unit.TestCase):
exp_project_id = uuid.uuid4().hex
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
exp_audit_ids = [provider.random_urlsafe_str()]
domain_id = None
trust_id = None
federated_info = None
payload = token_formatters.ProjectScopedPayload.assemble(
exp_user_id, exp_methods, exp_project_id, exp_expires_at,
exp_audit_ids)
exp_user_id, exp_methods, exp_project_id, domain_id,
exp_expires_at, exp_audit_ids, trust_id, federated_info)
(user_id, methods, project_id, expires_at, audit_ids) = (
token_formatters.ProjectScopedPayload.disassemble(payload))
@ -341,10 +349,13 @@ class TestPayloads(unit.TestCase):
exp_domain_id = uuid.uuid4().hex
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
exp_audit_ids = [provider.random_urlsafe_str()]
project_id = None
trust_id = None
federated_info = None
payload = token_formatters.DomainScopedPayload.assemble(
exp_user_id, exp_methods, exp_domain_id, exp_expires_at,
exp_audit_ids)
exp_user_id, exp_methods, project_id, exp_domain_id,
exp_expires_at, exp_audit_ids, trust_id, federated_info)
(user_id, methods, domain_id, expires_at, audit_ids) = (
token_formatters.DomainScopedPayload.disassemble(payload))
@ -361,10 +372,13 @@ class TestPayloads(unit.TestCase):
exp_domain_id = CONF.identity.default_domain_id
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
exp_audit_ids = [provider.random_urlsafe_str()]
project_id = None
trust_id = None
federated_info = None
payload = token_formatters.DomainScopedPayload.assemble(
exp_user_id, exp_methods, exp_domain_id, exp_expires_at,
exp_audit_ids)
exp_user_id, exp_methods, project_id, exp_domain_id,
exp_expires_at, exp_audit_ids, trust_id, federated_info)
(user_id, methods, domain_id, expires_at, audit_ids) = (
token_formatters.DomainScopedPayload.disassemble(payload))
@ -382,10 +396,12 @@ class TestPayloads(unit.TestCase):
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
exp_audit_ids = [provider.random_urlsafe_str()]
exp_trust_id = uuid.uuid4().hex
domain_id = None
federated_info = None
payload = token_formatters.TrustScopedPayload.assemble(
exp_user_id, exp_methods, exp_project_id, exp_expires_at,
exp_audit_ids, exp_trust_id)
exp_user_id, exp_methods, exp_project_id, domain_id,
exp_expires_at, exp_audit_ids, exp_trust_id, federated_info)
(user_id, methods, project_id, expires_at, audit_ids, trust_id) = (
token_formatters.TrustScopedPayload.disassemble(payload))
@ -401,9 +417,14 @@ class TestPayloads(unit.TestCase):
exp_methods = ['password']
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
exp_audit_ids = [provider.random_urlsafe_str()]
project_id = None
domain_id = None
trust_id = None
federated_info = None
payload = token_formatters.UnscopedPayload.assemble(
exp_user_id, exp_methods, exp_expires_at, exp_audit_ids)
exp_user_id, exp_methods, project_id, domain_id, exp_expires_at,
exp_audit_ids, trust_id, federated_info)
(user_id, methods, expires_at, audit_ids) = (
token_formatters.UnscopedPayload.disassemble(payload))
@ -424,10 +445,13 @@ class TestPayloads(unit.TestCase):
exp_methods = ['password']
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
exp_audit_ids = [provider.random_urlsafe_str()]
domain_id = None
trust_id = None
federated_info = None
payload = token_formatters.ProjectScopedPayload.assemble(
exp_user_id, exp_methods, exp_project_id, exp_expires_at,
exp_audit_ids)
exp_user_id, exp_methods, exp_project_id, domain_id,
exp_expires_at, exp_audit_ids, trust_id, federated_info)
(user_id, methods, project_id, expires_at, audit_ids) = (
token_formatters.ProjectScopedPayload.disassemble(payload))
@ -451,10 +475,13 @@ class TestPayloads(unit.TestCase):
exp_domain_id = uuid.uuid4().hex
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
exp_audit_ids = [provider.random_urlsafe_str()]
project_id = None
trust_id = None
federated_info = None
payload = token_formatters.DomainScopedPayload.assemble(
exp_user_id, exp_methods, exp_domain_id, exp_expires_at,
exp_audit_ids)
exp_user_id, exp_methods, project_id, exp_domain_id,
exp_expires_at, exp_audit_ids, trust_id, federated_info)
(user_id, methods, domain_id, expires_at, audit_ids) = (
token_formatters.DomainScopedPayload.disassemble(payload))
@ -476,10 +503,12 @@ class TestPayloads(unit.TestCase):
exp_expires_at = utils.isotime(timeutils.utcnow(), subsecond=True)
exp_audit_ids = [provider.random_urlsafe_str()]
exp_trust_id = uuid.uuid4().hex
domain_id = None
federated_info = None
payload = token_formatters.TrustScopedPayload.assemble(
exp_user_id, exp_methods, exp_project_id, exp_expires_at,
exp_audit_ids, exp_trust_id)
exp_user_id, exp_methods, exp_project_id, domain_id,
exp_expires_at, exp_audit_ids, exp_trust_id, federated_info)
(user_id, methods, project_id, expires_at, audit_ids, trust_id) = (
token_formatters.TrustScopedPayload.disassemble(payload))
@ -506,10 +535,13 @@ class TestPayloads(unit.TestCase):
exp_federated_info = {'group_ids': [{'id': exp_group_id}],
'idp_id': uuid.uuid4().hex,
'protocol_id': uuid.uuid4().hex}
project_id = None
domain_id = None
trust_id = None
payload = token_formatters.FederatedUnscopedPayload.assemble(
exp_user_id, exp_methods, exp_expires_at, exp_audit_ids,
exp_federated_info)
exp_user_id, exp_methods, project_id, domain_id, exp_expires_at,
exp_audit_ids, trust_id, exp_federated_info)
(user_id, methods, expires_at, audit_ids, federated_info) = (
token_formatters.FederatedUnscopedPayload.disassemble(payload))
@ -542,10 +574,12 @@ class TestPayloads(unit.TestCase):
exp_federated_info = {'group_ids': [{'id': 'someNonUuidGroupId'}],
'idp_id': uuid.uuid4().hex,
'protocol_id': uuid.uuid4().hex}
domain_id = None
trust_id = None
payload = token_formatters.FederatedProjectScopedPayload.assemble(
exp_user_id, exp_methods, exp_project_id, exp_expires_at,
exp_audit_ids, exp_federated_info)
exp_user_id, exp_methods, exp_project_id, domain_id,
exp_expires_at, exp_audit_ids, trust_id, exp_federated_info)
(user_id, methods, project_id, expires_at, audit_ids,
federated_info) = (
@ -568,10 +602,12 @@ class TestPayloads(unit.TestCase):
exp_federated_info = {'group_ids': [{'id': 'someNonUuidGroupId'}],
'idp_id': uuid.uuid4().hex,
'protocol_id': uuid.uuid4().hex}
project_id = None
trust_id = None
payload = token_formatters.FederatedDomainScopedPayload.assemble(
exp_user_id, exp_methods, exp_domain_id, exp_expires_at,
exp_audit_ids, exp_federated_info)
exp_user_id, exp_methods, project_id, exp_domain_id,
exp_expires_at, exp_audit_ids, trust_id, exp_federated_info)
(user_id, methods, domain_id, expires_at, audit_ids,
federated_info) = (

View File

@ -138,64 +138,17 @@ class TokenFormatter(object):
domain_id=None, project_id=None, trust_id=None,
federated_info=None):
"""Given a set of payload attributes, generate a Fernet token."""
if trust_id:
version = TrustScopedPayload.version
payload = TrustScopedPayload.assemble(
user_id,
methods,
project_id,
expires_at,
audit_ids,
trust_id)
elif project_id and federated_info:
version = FederatedProjectScopedPayload.version
payload = FederatedProjectScopedPayload.assemble(
user_id,
methods,
project_id,
expires_at,
audit_ids,
federated_info)
elif domain_id and federated_info:
version = FederatedDomainScopedPayload.version
payload = FederatedDomainScopedPayload.assemble(
user_id,
methods,
domain_id,
expires_at,
audit_ids,
federated_info)
elif federated_info:
version = FederatedUnscopedPayload.version
payload = FederatedUnscopedPayload.assemble(
user_id,
methods,
expires_at,
audit_ids,
federated_info)
elif project_id:
version = ProjectScopedPayload.version
payload = ProjectScopedPayload.assemble(
user_id,
methods,
project_id,
expires_at,
audit_ids)
elif domain_id:
version = DomainScopedPayload.version
payload = DomainScopedPayload.assemble(
user_id,
methods,
domain_id,
expires_at,
audit_ids)
else:
version = UnscopedPayload.version
payload = UnscopedPayload.assemble(
user_id,
methods,
expires_at,
audit_ids)
for payload_class in PAYLOAD_CLASSES:
if payload_class.create_arguments_apply(
project_id=project_id, domain_id=domain_id,
trust_id=trust_id, federated_info=federated_info):
break
version = payload_class.version
payload = payload_class.assemble(
user_id, methods, project_id, domain_id, expires_at, audit_ids,
trust_id, federated_info
)
versioned_payload = (version,) + payload
serialized_payload = msgpack.packb(versioned_payload)
@ -275,10 +228,31 @@ class BasePayload(object):
version = None
@classmethod
def assemble(cls, *args):
def create_arguments_apply(cls, **kwargs):
"""Check the arguments to see if they apply to this payload variant.
:returns: True if the arguments indicate that this payload class is
needed for the token otherwise returns False.
:rtype: bool
"""
raise NotImplementedError()
@classmethod
def assemble(cls, user_id, methods, project_id, domain_id, expires_at,
audit_ids, trust_id, federated_info):
"""Assemble the payload of a token.
:param args: whatever data should go into the payload
:param user_id: identifier of the user in the token request
:param methods: list of authentication methods used
:param project_id: ID of the project to scope to
:param domain_id: ID of the domain to scope to
:param expires_at: datetime of the token's expiration
:param audit_ids: list of the token's audit IDs
:param trust_id: ID of the trust in effect
:param federated_info: dictionary containing group IDs, the identity
provider ID, protocol ID, and federated domain
ID
:returns: the payload of a token
"""
@ -379,16 +353,12 @@ class UnscopedPayload(BasePayload):
version = 0
@classmethod
def assemble(cls, user_id, methods, expires_at, audit_ids):
"""Assemble the payload of an unscoped token.
def create_arguments_apply(cls, **kwargs):
return True
:param user_id: identifier of the user in the token request
:param methods: list of authentication methods used
:param expires_at: datetime of the token's expiration
:param audit_ids: list of the token's audit IDs
:returns: the payload of an unscoped token
"""
@classmethod
def assemble(cls, user_id, methods, project_id, domain_id, expires_at,
audit_ids, trust_id, federated_info):
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)
@ -418,17 +388,12 @@ class DomainScopedPayload(BasePayload):
version = 1
@classmethod
def assemble(cls, user_id, methods, domain_id, expires_at, audit_ids):
"""Assemble the payload of a domain-scoped token.
def create_arguments_apply(cls, **kwargs):
return kwargs['domain_id']
:param user_id: ID of the user in the token request
:param methods: list of authentication methods used
:param domain_id: ID of the domain to scope to
:param expires_at: datetime of the token's expiration
:param audit_ids: list of the token's audit IDs
:returns: the payload of a domain-scoped token
"""
@classmethod
def assemble(cls, user_id, methods, project_id, domain_id, expires_at,
audit_ids, trust_id, federated_info):
b_user_id = cls.attempt_convert_uuid_hex_to_bytes(user_id)
methods = auth_plugins.convert_method_list_to_integer(methods)
try:
@ -475,17 +440,12 @@ class ProjectScopedPayload(BasePayload):
version = 2
@classmethod
def assemble(cls, user_id, methods, project_id, expires_at, audit_ids):
"""Assemble the payload of a project-scoped token.
def create_arguments_apply(cls, **kwargs):
return kwargs['project_id']
:param user_id: ID of the user in the token request
:param methods: list of authentication methods used
:param project_id: ID of the project to scope to
:param expires_at: datetime of the token's expiration
:param audit_ids: list of the token's audit IDs
:returns: the payload of a project-scoped token
"""
@classmethod
def assemble(cls, user_id, methods, project_id, domain_id, expires_at,
audit_ids, trust_id, federated_info):
b_user_id = cls.attempt_convert_uuid_hex_to_bytes(user_id)
methods = auth_plugins.convert_method_list_to_integer(methods)
b_project_id = cls.attempt_convert_uuid_hex_to_bytes(project_id)
@ -520,19 +480,12 @@ class TrustScopedPayload(BasePayload):
version = 3
@classmethod
def assemble(cls, user_id, methods, project_id, expires_at, audit_ids,
trust_id):
"""Assemble the payload of a trust-scoped token.
def create_arguments_apply(cls, **kwargs):
return kwargs['trust_id']
:param user_id: ID of the user in the token request
:param methods: list of authentication methods used
:param project_id: ID of the project to scope to
:param expires_at: datetime of the token's expiration
:param audit_ids: list of the token's audit IDs
:param trust_id: ID of the trust in effect
:returns: the payload of a trust-scoped token
"""
@classmethod
def assemble(cls, user_id, methods, project_id, domain_id, expires_at,
audit_ids, trust_id, federated_info):
b_user_id = cls.attempt_convert_uuid_hex_to_bytes(user_id)
methods = auth_plugins.convert_method_list_to_integer(methods)
b_project_id = cls.attempt_convert_uuid_hex_to_bytes(project_id)
@ -571,6 +524,10 @@ class TrustScopedPayload(BasePayload):
class FederatedUnscopedPayload(BasePayload):
version = 4
@classmethod
def create_arguments_apply(cls, **kwargs):
return kwargs['federated_info']
@classmethod
def pack_group_id(cls, group_dict):
return cls.attempt_convert_uuid_hex_to_bytes(group_dict['id'])
@ -583,19 +540,8 @@ class FederatedUnscopedPayload(BasePayload):
return {'id': group_id}
@classmethod
def assemble(cls, user_id, methods, expires_at, audit_ids, federated_info):
"""Assemble the payload of a federated token.
:param user_id: ID of the user in the token request
:param methods: list of authentication methods used
:param expires_at: datetime of the token's expiration
:param audit_ids: list of the token's audit IDs
:param federated_info: dictionary containing group IDs, the identity
provider ID, protocol ID, and federated domain
ID
:returns: the payload of a federated token
"""
def assemble(cls, user_id, methods, project_id, domain_id, expires_at,
audit_ids, trust_id, federated_info):
b_user_id = cls.attempt_convert_uuid_hex_to_bytes(user_id)
methods = auth_plugins.convert_method_list_to_integer(methods)
b_group_ids = list(map(cls.pack_group_id,
@ -641,23 +587,12 @@ class FederatedScopedPayload(FederatedUnscopedPayload):
version = None
@classmethod
def assemble(cls, user_id, methods, scope_id, expires_at, audit_ids,
federated_info):
"""Assemble the project-scoped payload of a federated token.
:param user_id: ID of the user in the token request
:param methods: list of authentication methods used
:param scope_id: ID of the project or domain ID to scope to
:param expires_at: datetime of the token's expiration
:param audit_ids: list of the token's audit IDs
:param federated_info: dictionary containing the identity provider ID,
protocol ID, federated domain ID and group IDs
:returns: the payload of a federated token
"""
def assemble(cls, user_id, methods, project_id, domain_id, expires_at,
audit_ids, trust_id, federated_info):
b_user_id = cls.attempt_convert_uuid_hex_to_bytes(user_id)
methods = auth_plugins.convert_method_list_to_integer(methods)
b_scope_id = cls.attempt_convert_uuid_hex_to_bytes(scope_id)
b_scope_id = cls.attempt_convert_uuid_hex_to_bytes(
project_id or domain_id)
b_group_ids = list(map(cls.pack_group_id,
federated_info['group_ids']))
b_idp_id = cls.attempt_convert_uuid_hex_to_bytes(
@ -705,6 +640,32 @@ class FederatedScopedPayload(FederatedUnscopedPayload):
class FederatedProjectScopedPayload(FederatedScopedPayload):
version = 5
@classmethod
def create_arguments_apply(cls, **kwargs):
return kwargs['project_id'] and kwargs['federated_info']
class FederatedDomainScopedPayload(FederatedScopedPayload):
version = 6
@classmethod
def create_arguments_apply(cls, **kwargs):
return kwargs['domain_id'] and kwargs['federated_info']
# For now, the order of the classes in the following list is important. This
# is because the way they test that the payload applies to them in
# the create_arguments_apply method requires that the previous ones rejected
# the payload arguments. For example, UnscopedPayload must be last since it's
# the catch-all after all the other payloads have been checked.
# TODO(blk-u): Clean up the create_arguments_apply methods so that they don't
# depend on the previous classes then these can be in any order.
PAYLOAD_CLASSES = [
TrustScopedPayload,
FederatedProjectScopedPayload,
FederatedDomainScopedPayload,
FederatedUnscopedPayload,
ProjectScopedPayload,
DomainScopedPayload,
UnscopedPayload,
]