From b8ca94b5180d9cf16e1faa72b9b0c5d9739d74fe Mon Sep 17 00:00:00 2001 From: Peter Hamilton Date: Thu, 28 Feb 2019 17:35:55 -0500 Subject: [PATCH] Add utilities for converting TemplateAttributes and Attributes This change adds several attribute-related utilities that support converting TemplateAttribute structures to the new Attributes structure and back. These utilities will be used in the updated operation payloads to support seamless KMIP 2.0 encodings without requiring broad payload internal and external usage changes. Unit tests have been included to cover the new utility functions. --- kmip/core/enums.py | 146 +++++++++ kmip/core/objects.py | 50 ++++ kmip/tests/unit/core/objects/test_objects.py | 295 +++++++++++++++++++ kmip/tests/unit/core/test_enums.py | 44 +++ 4 files changed, 535 insertions(+) diff --git a/kmip/core/enums.py b/kmip/core/enums.py index fb3fdce..dd0d888 100644 --- a/kmip/core/enums.py +++ b/kmip/core/enums.py @@ -18,6 +18,7 @@ import copy import enum +import six class OrderedEnum(enum.Enum): @@ -1669,6 +1670,151 @@ class WrappingMethod(enum.Enum): TR_31 = 0x00000005 +attribute_name_tag_table = [ + ("Activation Date", Tags.ACTIVATION_DATE), + ("Alternative Name", Tags.ALTERNATIVE_NAME), + ("Always Sensitive", Tags.ALWAYS_SENSITIVE), + ("Application Specific Information", Tags.APPLICATION_SPECIFIC_INFORMATION), + ("Archive Date", Tags.ARCHIVE_DATE), + ("Attribute", Tags.ATTRIBUTE), + ("Certificate Identifier", Tags.CERTIFICATE_IDENTIFIER), + ("Certificate Issuer", Tags.CERTIFICATE_ISSUER), + ("Certificate Issuer C", Tags.CERTIFICATE_ISSUER_C), + ("Certificate Issuer CN", Tags.CERTIFICATE_ISSUER_CN), + ("Certificate Issuer DC", Tags.CERTIFICATE_ISSUER_DC), + ("Certificate Issuer DN Qualifier", Tags.CERTIFICATE_ISSUER_DN_QUALIFIER), + ("Certificate Issuer Email", Tags.CERTIFICATE_ISSUER_EMAIL), + ("Certificate Issuer L", Tags.CERTIFICATE_ISSUER_L), + ("Certificate Issuer O", Tags.CERTIFICATE_ISSUER_O), + ("Certificate Issuer OU", Tags.CERTIFICATE_ISSUER_OU), + ("Certificate Issuer Serial Number", Tags.CERTIFICATE_ISSUER_SERIAL_NUMBER), + ("Certificate Issuer ST", Tags.CERTIFICATE_ISSUER_ST), + ("Certificate Issuer Title", Tags.CERTIFICATE_ISSUER_TITLE), + ("Certificate Issuer UID", Tags.CERTIFICATE_ISSUER_UID), + ("Certificate Length", Tags.CERTIFICATE_LENGTH), + ("Certificate Subject", Tags.CERTIFICATE_SUBJECT), + ("Certificate Subject C", Tags.CERTIFICATE_SUBJECT_C), + ("Certificate Subject CN", Tags.CERTIFICATE_SUBJECT_CN), + ("Certificate Subject DC", Tags.CERTIFICATE_SUBJECT_DC), + ("Certificate Subject DN Qualifier", Tags.CERTIFICATE_SUBJECT_DN_QUALIFIER), + ("Certificate Subject Email", Tags.CERTIFICATE_SUBJECT_EMAIL), + ("Certificate Subject L", Tags.CERTIFICATE_SUBJECT_L), + ("Certificate Subject O", Tags.CERTIFICATE_SUBJECT_O), + ("Certificate Subject OU", Tags.CERTIFICATE_SUBJECT_OU), + ("Certificate Subject Serial Number", Tags.CERTIFICATE_SUBJECT_SERIAL_NUMBER), + ("Certificate Subject ST", Tags.CERTIFICATE_SUBJECT_ST), + ("Certificate Subject Title", Tags.CERTIFICATE_SUBJECT_TITLE), + ("Certificate Subject UID", Tags.CERTIFICATE_SUBJECT_UID), + ("Certificate Type", Tags.CERTIFICATE_TYPE), + ("Comment", Tags.COMMENT), + ("Compromise Date", Tags.COMPROMISE_DATE), + ("Compromise Occurrence Date", Tags.COMPROMISE_OCCURRENCE_DATE), + ("Contact Information", Tags.CONTACT_INFORMATION), + ("Cryptographic Algorithm", Tags.CRYPTOGRAPHIC_ALGORITHM), + ("Cryptographic Domain Parameters", Tags.CRYPTOGRAPHIC_DOMAIN_PARAMETERS), + ("Cryptographic Length", Tags.CRYPTOGRAPHIC_LENGTH), + ("Cryptographic Parameters", Tags.CRYPTOGRAPHIC_PARAMETERS), + ("Cryptographic Usage Mask", Tags.CRYPTOGRAPHIC_USAGE_MASK), + ("Custom Attribute", Tags.CUSTOM_ATTRIBUTE), + ("Deactivation Date", Tags.DEACTIVATION_DATE), + ("Description", Tags.DESCRIPTION), + ("Destroy Date", Tags.DESTROY_DATE), + ("Digest", Tags.DIGEST), + ("Digital Signature Algorithm", Tags.DIGITAL_SIGNATURE_ALGORITHM), + ("Extractable", Tags.EXTRACTABLE), + ("Fresh", Tags.FRESH), + ("Initial Date", Tags.INITIAL_DATE), + ("Key Format Type", Tags.KEY_FORMAT_TYPE), + ("Key Value Location", Tags.KEY_VALUE_LOCATION), + ("Key Value Present", Tags.KEY_VALUE_PRESENT), + ("Last Change Date", Tags.LAST_CHANGE_DATE), + ("Lease Time", Tags.LEASE_TIME), + ("Link", Tags.LINK), + ("Name", Tags.NAME), + ("Never Extractable", Tags.NEVER_EXTRACTABLE), + ("NIST Key Type", Tags.NIST_KEY_TYPE), + ("Object Group", Tags.OBJECT_GROUP), + ("Object Type", Tags.OBJECT_TYPE), + ("Opaque Data Type", Tags.OPAQUE_DATA_TYPE), + ("Operation Policy Name", Tags.OPERATION_POLICY_NAME), + ("Original Creation Date", Tags.ORIGINAL_CREATION_DATE), + ("PKCS#12 Friendly Name", Tags.PKCS12_FRIENDLY_NAME), + ("Process Start Date", Tags.PROCESS_START_DATE), + ("Protect Stop Date", Tags.PROTECT_STOP_DATE), + ("Protection Level", Tags.PROTECTION_LEVEL), + ("Protection Period", Tags.PROTECTION_PERIOD), + ("Protection Storage Mask", Tags.PROTECTION_STORAGE_MASK), + ("Quantum Safe", Tags.QUANTUM_SAFE), + ("Random Number Generator", Tags.RANDOM_NUMBER_GENERATOR), + ("Revocation Reason", Tags.REVOCATION_REASON), + ("Sensitive", Tags.SENSITIVE), + ("Short Unique Identifier", Tags.SHORT_UNIQUE_IDENTIFIER), + ("State", Tags.STATE), + ("Unique Identifier", Tags.UNIQUE_IDENTIFIER), + ("Usage Limits", Tags.USAGE_LIMITS), + ("X.509 Certificate Identifier", Tags.X_509_CERTIFICATE_IDENTIFIER), + ("X.509 Certificate Issuer", Tags.X_509_CERTIFICATE_ISSUER), + ("X.509 Certificate Subject", Tags.X_509_CERTIFICATE_SUBJECT) +] + + +def convert_attribute_name_to_tag(value): + """ + A utility function that converts an attribute name string into the + corresponding attribute tag. + + For example: 'State' -> enums.Tags.STATE + + Args: + value (string): The string name of the attribute. + + Returns: + enum: The Tags enumeration value that corresponds to the attribute + name string. + + Raises: + ValueError: if the attribute name string is not a string or if it is + an unrecognized attribute name + """ + if not isinstance(value, six.string_types): + raise ValueError("The attribute name must be a string.") + + for entry in attribute_name_tag_table: + if value == entry[0]: + return entry[1] + + raise ValueError("Unrecognized attribute name: '{}'".format(value)) + + +def convert_attribute_tag_to_name(value): + """ + A utility function that converts an attribute tag into the corresponding + attribute name string. + + For example: enums.Tags.STATE -> 'State' + + Args: + value (enum): The Tags enumeration value of the attribute. + + Returns: + string: The attribute name string that corresponds to the attribute + tag. + + Raises: + ValueError: if the attribute tag is not a Tags enumeration or if it + is unrecognized attribute tag + """ + if not isinstance(value, Tags): + raise ValueError("The attribute tag must be a Tags enumeration.") + + for entry in attribute_name_tag_table: + if value == entry[1]: + return entry[0] + + raise ValueError("Unrecognized attribute tag: {}".format(value)) + + + def is_enum_value(enum_class, potential_value): """ A utility function that checks if the enumeration class contains the diff --git a/kmip/core/objects.py b/kmip/core/objects.py index 76ac378..2287b95 100644 --- a/kmip/core/objects.py +++ b/kmip/core/objects.py @@ -2974,6 +2974,56 @@ class PublicKeyTemplateAttribute(TemplateAttribute): names, attributes, Tags.PUBLIC_KEY_TEMPLATE_ATTRIBUTE) +def convert_template_attribute_to_attributes(value): + if not isinstance(value, TemplateAttribute): + raise TypeError("Input must be a TemplateAttribute structure.") + + tag = enums.Tags.ATTRIBUTES + if isinstance(value, CommonTemplateAttribute): + tag = enums.Tags.COMMON_ATTRIBUTES + elif isinstance(value, PrivateKeyTemplateAttribute): + tag = enums.Tags.PRIVATE_KEY_ATTRIBUTES + elif isinstance(value, PublicKeyTemplateAttribute): + tag = enums.Tags.PUBLIC_KEY_ATTRIBUTES + + attribute_values = [] + for attribute in value.attributes: + attribute_tag = enums.convert_attribute_name_to_tag( + attribute.attribute_name.value + ) + attribute_value = attribute.attribute_value + attribute_value.tag = attribute_tag + attribute_values.append(attribute_value) + + return Attributes(attributes=attribute_values, tag=tag) + + +def convert_attributes_to_template_attribute(value): + if not isinstance(value, Attributes): + raise TypeError("Input must be an Attributes structure.") + + attribute_structures = [] + for attribute_value in value.attributes: + attribute_name = enums.convert_attribute_tag_to_name( + attribute_value.tag + ) + attribute_structures.append( + Attribute( + attribute_name=Attribute.AttributeName(attribute_name), + attribute_value=attribute_value + ) + ) + + if value.tag == enums.Tags.COMMON_ATTRIBUTES: + return CommonTemplateAttribute(attributes=attribute_structures) + elif value.tag == enums.Tags.PRIVATE_KEY_ATTRIBUTES: + return PrivateKeyTemplateAttribute(attributes=attribute_structures) + elif value.tag == enums.Tags.PUBLIC_KEY_ATTRIBUTES: + return PublicKeyTemplateAttribute(attributes=attribute_structures) + else: + return TemplateAttribute(attributes=attribute_structures) + + # 2.1.9 class ExtensionName(TextString): """ diff --git a/kmip/tests/unit/core/objects/test_objects.py b/kmip/tests/unit/core/objects/test_objects.py index 1a98a5a..30e90a9 100644 --- a/kmip/tests/unit/core/objects/test_objects.py +++ b/kmip/tests/unit/core/objects/test_objects.py @@ -781,6 +781,301 @@ class TestAttributes(TestCase): self.assertTrue(b != a) +class TestAttributeUtilities(testtools.TestCase): + + def setUp(self): + super(TestAttributeUtilities, self).setUp() + + def tearDown(self): + super(TestAttributeUtilities, self).tearDown() + + def test_convert_template_attribute_to_attributes(self): + template_attribute = objects.TemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + "State" + ), + attribute_value=primitives.Enumeration( + enums.State, + value=enums.State.PRE_ACTIVE, + tag=enums.Tags.STATE + ) + ) + ] + ) + + value = objects.convert_template_attribute_to_attributes( + template_attribute + ) + + self.assertIsInstance(value, objects.Attributes) + self.assertEqual(enums.Tags.ATTRIBUTES, value.tag) + self.assertIsInstance(value.attributes, list) + self.assertEqual(1, len(value.attributes)) + self.assertEqual( + primitives.Enumeration( + enums.State, + value=enums.State.PRE_ACTIVE, + tag=enums.Tags.STATE + ), + value.attributes[0] + ) + + def test_convert_common_template_attribute_to_attributes(self): + template_attribute = objects.CommonTemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + "Cryptographic Algorithm" + ), + attribute_value=primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ) + ] + ) + + value = objects.convert_template_attribute_to_attributes( + template_attribute + ) + + self.assertIsInstance(value, objects.Attributes) + self.assertEqual(enums.Tags.COMMON_ATTRIBUTES, value.tag) + self.assertIsInstance(value.attributes, list) + self.assertEqual(1, len(value.attributes)) + self.assertEqual( + primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ), + value.attributes[0] + ) + + def test_convert_private_key_template_attribute_to_attributes(self): + template_attribute = objects.PrivateKeyTemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + "Key Format Type" + ), + attribute_value=primitives.Enumeration( + enums.KeyFormatType, + value=enums.KeyFormatType.RAW, + tag=enums.Tags.KEY_FORMAT_TYPE + ) + ) + ] + ) + + value = objects.convert_template_attribute_to_attributes( + template_attribute + ) + + self.assertIsInstance(value, objects.Attributes) + self.assertEqual(enums.Tags.PRIVATE_KEY_ATTRIBUTES, value.tag) + self.assertIsInstance(value.attributes, list) + self.assertEqual(1, len(value.attributes)) + self.assertEqual( + primitives.Enumeration( + enums.KeyFormatType, + value=enums.KeyFormatType.RAW, + tag=enums.Tags.KEY_FORMAT_TYPE + ), + value.attributes[0] + ) + + def test_convert_public_key_template_attribute_to_attributes(self): + template_attribute = objects.PublicKeyTemplateAttribute( + attributes=[ + objects.Attribute( + attribute_name=objects.Attribute.AttributeName( + "Object Type" + ), + attribute_value=primitives.Enumeration( + enums.ObjectType, + value=enums.ObjectType.PUBLIC_KEY, + tag=enums.Tags.OBJECT_TYPE + ) + ) + ] + ) + + value = objects.convert_template_attribute_to_attributes( + template_attribute + ) + + self.assertIsInstance(value, objects.Attributes) + self.assertEqual(enums.Tags.PUBLIC_KEY_ATTRIBUTES, value.tag) + self.assertIsInstance(value.attributes, list) + self.assertEqual(1, len(value.attributes)) + self.assertEqual( + primitives.Enumeration( + enums.ObjectType, + value=enums.ObjectType.PUBLIC_KEY, + tag=enums.Tags.OBJECT_TYPE + ), + value.attributes[0] + ) + + def test_convert_template_attribute_to_attributes_invalid(self): + args = ("invalid", ) + self.assertRaisesRegex( + TypeError, + "Input must be a TemplateAttribute structure.", + objects.convert_template_attribute_to_attributes, + *args + ) + + def test_convert_attributes_to_template_attribute(self): + attributes = objects.Attributes( + attributes=[ + primitives.Enumeration( + enums.State, + value=enums.State.PRE_ACTIVE, + tag=enums.Tags.STATE + ) + ], + tag=enums.Tags.ATTRIBUTES + ) + + value = objects.convert_attributes_to_template_attribute(attributes) + + self.assertIsInstance(value, objects.TemplateAttribute) + self.assertIsInstance(value.attributes, list) + self.assertEqual(1, len(value.attributes)) + self.assertIsInstance( + value.attributes[0], + objects.Attribute + ) + self.assertEqual( + "State", + value.attributes[0].attribute_name.value + ) + self.assertEqual( + primitives.Enumeration( + enums.State, + value=enums.State.PRE_ACTIVE, + tag=enums.Tags.ATTRIBUTE_VALUE + ), + value.attributes[0].attribute_value + ) + + def test_convert_attributes_to_common_template_attribute(self): + attributes = objects.Attributes( + attributes=[ + primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.CRYPTOGRAPHIC_ALGORITHM + ) + ], + tag=enums.Tags.COMMON_ATTRIBUTES + ) + + value = objects.convert_attributes_to_template_attribute(attributes) + + self.assertIsInstance(value, objects.CommonTemplateAttribute) + self.assertIsInstance(value.attributes, list) + self.assertEqual(1, len(value.attributes)) + self.assertIsInstance( + value.attributes[0], + objects.Attribute + ) + self.assertEqual( + "Cryptographic Algorithm", + value.attributes[0].attribute_name.value + ) + self.assertEqual( + primitives.Enumeration( + enums.CryptographicAlgorithm, + value=enums.CryptographicAlgorithm.AES, + tag=enums.Tags.ATTRIBUTE_VALUE + ), + value.attributes[0].attribute_value + ) + + def test_convert_attributes_to_private_key_template_attribute(self): + attributes = objects.Attributes( + attributes=[ + primitives.Enumeration( + enums.KeyFormatType, + value=enums.KeyFormatType.RAW, + tag=enums.Tags.KEY_FORMAT_TYPE + ) + ], + tag=enums.Tags.PRIVATE_KEY_ATTRIBUTES + ) + + value = objects.convert_attributes_to_template_attribute(attributes) + + self.assertIsInstance(value, objects.PrivateKeyTemplateAttribute) + self.assertIsInstance(value.attributes, list) + self.assertEqual(1, len(value.attributes)) + self.assertIsInstance( + value.attributes[0], + objects.Attribute + ) + self.assertEqual( + "Key Format Type", + value.attributes[0].attribute_name.value + ) + self.assertEqual( + primitives.Enumeration( + enums.KeyFormatType, + value=enums.KeyFormatType.RAW, + tag=enums.Tags.ATTRIBUTE_VALUE + ), + value.attributes[0].attribute_value + ) + + def test_convert_attributes_to_public_key_template_attribute(self): + attributes = objects.Attributes( + attributes=[ + primitives.Enumeration( + enums.ObjectType, + value=enums.ObjectType.PUBLIC_KEY, + tag=enums.Tags.OBJECT_TYPE + ) + ], + tag=enums.Tags.PUBLIC_KEY_ATTRIBUTES + ) + + value = objects.convert_attributes_to_template_attribute(attributes) + + self.assertIsInstance(value, objects.PublicKeyTemplateAttribute) + self.assertIsInstance(value.attributes, list) + self.assertEqual(1, len(value.attributes)) + self.assertIsInstance( + value.attributes[0], + objects.Attribute + ) + self.assertEqual( + "Object Type", + value.attributes[0].attribute_name.value + ) + self.assertEqual( + primitives.Enumeration( + enums.ObjectType, + value=enums.ObjectType.PUBLIC_KEY, + tag=enums.Tags.ATTRIBUTE_VALUE + ), + value.attributes[0].attribute_value + ) + + def test_convert_attributes_to_template_attribute_invalid(self): + args = ("invalid", ) + self.assertRaisesRegex( + TypeError, + "Input must be an Attributes structure.", + objects.convert_attributes_to_template_attribute, + *args + ) + + class TestKeyMaterialStruct(TestCase): """ A test suite for the KeyMaterialStruct. diff --git a/kmip/tests/unit/core/test_enums.py b/kmip/tests/unit/core/test_enums.py index 517af20..db67943 100644 --- a/kmip/tests/unit/core/test_enums.py +++ b/kmip/tests/unit/core/test_enums.py @@ -100,6 +100,50 @@ class TestEnumUtilityFunctions(testtools.TestCase): ) self.assertFalse(result) + def test_convert_attribute_name_to_tag(self): + self.assertEqual( + enums.Tags.OBJECT_TYPE, + enums.convert_attribute_name_to_tag("Object Type") + ) + + args = (enums.Tags.COMMON_ATTRIBUTES, ) + self.assertRaisesRegex( + ValueError, + "The attribute name must be a string.", + enums.convert_attribute_name_to_tag, + *args + ) + + args = ("invalid", ) + self.assertRaisesRegex( + ValueError, + "Unrecognized attribute name: 'invalid'".format(args[0]), + enums.convert_attribute_name_to_tag, + *args + ) + + def test_convert_attribute_tag_to_name(self): + self.assertEqual( + "Always Sensitive", + enums.convert_attribute_tag_to_name(enums.Tags.ALWAYS_SENSITIVE) + ) + + args = ("invalid", ) + self.assertRaisesRegex( + ValueError, + "The attribute tag must be a Tags enumeration.", + enums.convert_attribute_tag_to_name, + *args + ) + + args = (enums.Tags.COMMON_ATTRIBUTES, ) + self.assertRaisesRegex( + ValueError, + "Unrecognized attribute tag: {}".format(args[0]), + enums.convert_attribute_tag_to_name, + *args + ) + def test_is_attribute(self): # Test an attribute introduced in KMIP 1.0 result = enums.is_attribute(enums.Tags.UNIQUE_IDENTIFIER)