Update the Create payloads

This change updates the Create payloads to the current payload
format, adding properties for different payload attributes and
adding comparison and string operators. Changes are also made to
the PyKMIP clients and the surrounding testing infrastructure to
reflect the payload changes. An official unit test suite for the
Create payloads has also been included, which will eventually
replace the existing Create message tests elsewhere in the test
suite.

This change prepares the Create payloads for future updates to
support KMIP 2.0.
This commit is contained in:
Peter Hamilton 2019-02-26 16:16:43 -05:00 committed by Peter Hamilton
parent a58a3a3bea
commit 30d7773d96
11 changed files with 1871 additions and 104 deletions

View File

@ -13,115 +13,443 @@
# License for the specific language governing permissions and limitations
# under the License.
from kmip.core import attributes
import six
from kmip.core import enums
from kmip.core.enums import Tags
from kmip.core.objects import TemplateAttribute
from kmip.core.primitives import Struct
from kmip.core.utils import BytearrayStream
from kmip.core import exceptions
from kmip.core import objects
from kmip.core import primitives
from kmip.core import utils
class CreateRequestPayload(Struct):
class CreateRequestPayload(primitives.Struct):
"""
A request payload for the Create operation.
Attributes:
object_type: The type of the object to create.
template_attribute: A group of attributes to set on the new object.
"""
def __init__(self,
object_type=None,
template_attribute=None):
"""
Construct a Create request payload structure.
Args:
object_type (enum): An ObjectType enumeration specifying the type
of object to create. Optional, defaults to None. Required for
read/write.
template_attribute (TemplateAttribute): A TemplateAttribute
structure containing a set of attributes to set on the new
object. Optional, defaults to None. Required for read/write.
"""
super(CreateRequestPayload, self).__init__(
tag=enums.Tags.REQUEST_PAYLOAD)
tag=enums.Tags.REQUEST_PAYLOAD
)
self._object_type = None
self._template_attribute = None
self.object_type = object_type
self.template_attribute = template_attribute
self.validate()
def read(self, istream, kmip_version=enums.KMIPVersion.KMIP_1_0):
@property
def object_type(self):
if self._object_type:
return self._object_type.value
else:
return None
@object_type.setter
def object_type(self, value):
if value is None:
self._object_type = None
elif isinstance(value, enums.ObjectType):
self._object_type = primitives.Enumeration(
enums.ObjectType,
value=value,
tag=enums.Tags.OBJECT_TYPE
)
else:
raise TypeError(
"Object type must be an ObjectType enumeration."
)
@property
def template_attribute(self):
return self._template_attribute
@template_attribute.setter
def template_attribute(self, value):
if value is None:
self._template_attribute = None
elif isinstance(value, objects.TemplateAttribute):
self._template_attribute = value
else:
raise TypeError(
"Template attribute must be a TemplateAttribute structure."
)
def read(self, input_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
"""
Read the data encoding the Create request payload and decode it into
its constituent parts.
Args:
input_buffer (stream): A data buffer containing encoded object
data, supporting a read method.
kmip_version (KMIPVersion): An enumeration defining the KMIP
version with which the object will be decoded. Optional,
defaults to KMIP 1.0.
Raises:
InvalidKmipEncoding: Raised if the object type or template
attribute is missing from the encoded payload.
"""
super(CreateRequestPayload, self).read(
istream,
input_buffer,
kmip_version=kmip_version
)
tstream = BytearrayStream(istream.read(self.length))
local_buffer = utils.BytearrayStream(input_buffer.read(self.length))
self.object_type = attributes.ObjectType()
self.template_attribute = TemplateAttribute()
if self.is_tag_next(enums.Tags.OBJECT_TYPE, local_buffer):
self._object_type = primitives.Enumeration(
enums.ObjectType,
tag=enums.Tags.OBJECT_TYPE
)
self._object_type.read(local_buffer, kmip_version=kmip_version)
else:
raise exceptions.InvalidKmipEncoding(
"The Create request payload encoding is missing the object "
"type."
)
self.object_type.read(tstream, kmip_version=kmip_version)
self.template_attribute.read(tstream, kmip_version=kmip_version)
if self.is_tag_next(enums.Tags.TEMPLATE_ATTRIBUTE, local_buffer):
self._template_attribute = objects.TemplateAttribute()
self._template_attribute.read(
local_buffer,
kmip_version=kmip_version
)
else:
raise exceptions.InvalidKmipEncoding(
"The Create request payload encoding is missing the template "
"attribute."
)
self.is_oversized(tstream)
self.validate()
self.is_oversized(local_buffer)
def write(self, ostream, kmip_version=enums.KMIPVersion.KMIP_1_0):
tstream = BytearrayStream()
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
"""
Write the data encoding the Create request payload to a buffer.
# Write the object type and template attribute of the request payload
self.object_type.write(tstream, kmip_version=kmip_version)
self.template_attribute.write(tstream, kmip_version=kmip_version)
Args:
output_buffer (stream): A data buffer in which to encode object
data, supporting a write method.
kmip_version (KMIPVersion): An enumeration defining the KMIP
version with which the object will be encoded. Optional,
defaults to KMIP 1.0.
# Write the length and value of the request payload
self.length = tstream.length()
Raises:
InvalidField: Raised if the object type attribute or template
attribute is not defined.
"""
local_buffer = utils.BytearrayStream()
if self._object_type:
self._object_type.write(local_buffer, kmip_version=kmip_version)
else:
raise exceptions.InvalidField(
"The Create request payload is missing the object type field."
)
if self._template_attribute:
self._template_attribute.write(
local_buffer,
kmip_version=kmip_version
)
else:
raise exceptions.InvalidField(
"The Create request payload is missing the template attribute "
"field."
)
self.length = local_buffer.length()
super(CreateRequestPayload, self).write(
ostream,
output_buffer,
kmip_version=kmip_version
)
ostream.write(tstream.buffer)
output_buffer.write(local_buffer.buffer)
def validate(self):
# TODO (peter-hamilton) Finish implementation.
pass
def __eq__(self, other):
if isinstance(other, CreateRequestPayload):
if self.object_type != other.object_type:
return False
elif self.template_attribute != other.template_attribute:
return False
else:
return True
else:
return NotImplemented
def __ne__(self, other):
if isinstance(other, CreateRequestPayload):
return not (self == other)
else:
return NotImplemented
def __repr__(self):
args = ", ".join([
"object_type={}".format(self.object_type),
"template_attribute={}".format(repr(self.template_attribute))
])
return "CreateRequestPayload({})".format(args)
def __str__(self):
value = ", ".join(
[
'"object_type": {}'.format(self.object_type),
'"template_attribute": {}'.format(self.template_attribute)
]
)
return '{' + value + '}'
class CreateResponsePayload(Struct):
class CreateResponsePayload(primitives.Struct):
"""
A response payload for the Create operation.
Attributes:
object_type: The type of the object created.
unique_identifier: The unique ID of the new object.
template_attribute: A group of attributes that were set on the new
object.
"""
def __init__(self,
object_type=None,
unique_identifier=None,
template_attribute=None):
"""
Construct a Create response payload structure.
Args:
object_type (enum): An ObjectType enumeration specifying the type
of object created. Optional, defaults to None. Required for
read/write.
unique_identifier (string): The ID of the new object. Optional,
defaults to None. Required for read/write.
template_attribute (TemplateAttribute): A TemplateAttribute
structure containing a set of attributes that were set on the
new object. Optional, defaults to None.
"""
super(CreateResponsePayload, self).__init__(
tag=enums.Tags.RESPONSE_PAYLOAD)
tag=enums.Tags.RESPONSE_PAYLOAD
)
self._object_type = None
self._unique_identifier = None
self._template_attribute = None
self.object_type = object_type
self.unique_identifier = unique_identifier
self.template_attribute = template_attribute
self.validate()
def read(self, istream, kmip_version=enums.KMIPVersion.KMIP_1_0):
@property
def object_type(self):
if self._object_type:
return self._object_type.value
else:
return None
@object_type.setter
def object_type(self, value):
if value is None:
self._object_type = None
elif isinstance(value, enums.ObjectType):
self._object_type = primitives.Enumeration(
enums.ObjectType,
value=value,
tag=enums.Tags.OBJECT_TYPE
)
else:
raise TypeError(
"Object type must be an ObjectType enumeration."
)
@property
def unique_identifier(self):
if self._unique_identifier:
return self._unique_identifier.value
else:
return None
@unique_identifier.setter
def unique_identifier(self, value):
if value is None:
self._unique_identifier = None
elif isinstance(value, six.string_types):
self._unique_identifier = primitives.TextString(
value=value,
tag=enums.Tags.UNIQUE_IDENTIFIER
)
else:
raise TypeError("Unique identifier must be a string.")
@property
def template_attribute(self):
return self._template_attribute
@template_attribute.setter
def template_attribute(self, value):
if value is None:
self._template_attribute = None
elif isinstance(value, objects.TemplateAttribute):
self._template_attribute = value
else:
raise TypeError(
"Template attribute must be a TemplateAttribute structure."
)
def read(self, input_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
"""
Read the data encoding the Create response payload and decode it into
its constituent parts.
Args:
input_buffer (stream): A data buffer containing encoded object
data, supporting a read method.
kmip_version (KMIPVersion): An enumeration defining the KMIP
version with which the object will be decoded. Optional,
defaults to KMIP 1.0.
Raises:
InvalidKmipEncoding: Raised if the object type or unique
identifier is missing from the encoded payload.
"""
super(CreateResponsePayload, self).read(
istream,
input_buffer,
kmip_version=kmip_version
)
tstream = BytearrayStream(istream.read(self.length))
local_buffer = utils.BytearrayStream(input_buffer.read(self.length))
self.object_type = attributes.ObjectType()
self.unique_identifier = attributes.UniqueIdentifier()
if self.is_tag_next(enums.Tags.OBJECT_TYPE, local_buffer):
self._object_type = primitives.Enumeration(
enums.ObjectType,
tag=enums.Tags.OBJECT_TYPE
)
self._object_type.read(local_buffer, kmip_version=kmip_version)
else:
raise exceptions.InvalidKmipEncoding(
"The Create response payload encoding is missing the object "
"type."
)
self.object_type.read(tstream, kmip_version=kmip_version)
self.unique_identifier.read(tstream, kmip_version=kmip_version)
if self.is_tag_next(enums.Tags.UNIQUE_IDENTIFIER, local_buffer):
self._unique_identifier = primitives.TextString(
tag=enums.Tags.UNIQUE_IDENTIFIER
)
self._unique_identifier.read(
local_buffer,
kmip_version=kmip_version
)
else:
raise exceptions.InvalidKmipEncoding(
"The Create response payload encoding is missing the unique "
"identifier."
)
if self.is_tag_next(Tags.TEMPLATE_ATTRIBUTE, tstream):
self.template_attribute = TemplateAttribute()
self.template_attribute.read(tstream, kmip_version=kmip_version)
if self.is_tag_next(enums.Tags.TEMPLATE_ATTRIBUTE, local_buffer):
self._template_attribute = objects.TemplateAttribute()
self._template_attribute.read(
local_buffer,
kmip_version=kmip_version
)
self.is_oversized(tstream)
self.validate()
self.is_oversized(local_buffer)
def write(self, ostream, kmip_version=enums.KMIPVersion.KMIP_1_0):
tstream = BytearrayStream()
def write(self, output_buffer, kmip_version=enums.KMIPVersion.KMIP_1_0):
"""
Write the data encoding the Create response payload to a buffer.
# Write the contents of the request payload
self.object_type.write(tstream, kmip_version=kmip_version)
self.unique_identifier.write(tstream, kmip_version=kmip_version)
Args:
output_buffer (stream): A data buffer in which to encode object
data, supporting a write method.
kmip_version (KMIPVersion): An enumeration defining the KMIP
version with which the object will be encoded. Optional,
defaults to KMIP 1.0.
if self.template_attribute is not None:
self.template_attribute.write(tstream, kmip_version=kmip_version)
Raises:
InvalidField: Raised if the object type attribute or unique
identifier is not defined.
"""
local_buffer = utils.BytearrayStream()
# Write the length and value of the request payload
self.length = tstream.length()
if self._object_type:
self._object_type.write(local_buffer, kmip_version=kmip_version)
else:
raise exceptions.InvalidField(
"The Create response payload is missing the object type field."
)
if self._unique_identifier:
self._unique_identifier.write(
local_buffer,
kmip_version=kmip_version
)
else:
raise exceptions.InvalidField(
"The Create response payload is missing the unique identifier "
"field."
)
if self._template_attribute:
self._template_attribute.write(
local_buffer,
kmip_version=kmip_version
)
self.length = local_buffer.length()
super(CreateResponsePayload, self).write(
ostream,
output_buffer,
kmip_version=kmip_version
)
ostream.write(tstream.buffer)
output_buffer.write(local_buffer.buffer)
def validate(self):
# TODO (peter-hamilton) Finish implementation.
pass
def __eq__(self, other):
if isinstance(other, CreateResponsePayload):
if self.object_type != other.object_type:
return False
elif self.unique_identifier != other.unique_identifier:
return False
elif self.template_attribute != other.template_attribute:
return False
else:
return True
else:
return NotImplemented
def __ne__(self, other):
if isinstance(other, CreateResponsePayload):
return not (self == other)
else:
return NotImplemented
def __repr__(self):
args = ", ".join([
"object_type={}".format(self.object_type),
"unique_identifier='{}'".format(self.unique_identifier),
"template_attribute={}".format(repr(self.template_attribute))
])
return "CreateResponsePayload({})".format(args)
def __str__(self):
value = ", ".join(
[
'"object_type": {}'.format(self.object_type),
'"unique_identifier": "{}"'.format(self.unique_identifier),
'"template_attribute": {}'.format(self.template_attribute)
]
)
return '{' + value + '}'

View File

@ -130,8 +130,8 @@ if __name__ == '__main__':
if result.result_status.value == ResultStatus.SUCCESS:
logger.info('created object type: {0}'.format(
result.object_type.value))
logger.info('created UUID: {0}'.format(result.uuid.value))
result.object_type))
logger.info('created UUID: {0}'.format(result.uuid))
logger.info('created template attribute: {0}'.
format(result.template_attribute))
else:

View File

@ -247,8 +247,7 @@ class ProxyKmipClient(object):
status = result.result_status.value
if status == enums.ResultStatus.SUCCESS:
uid = result.uuid.value
return uid
return result.uuid
else:
reason = result.result_reason.value
message = result.result_message.value

View File

@ -310,7 +310,6 @@ class KMIPProxy(object):
self.socket = None
def create(self, object_type, template_attribute, credential=None):
object_type = attr.ObjectType(object_type)
return self._create(object_type=object_type,
template_attribute=template_attribute,
credential=credential)

View File

@ -1017,7 +1017,7 @@ class KmipEngine(object):
def _process_create(self, payload):
self._logger.info("Processing operation: Create")
object_type = payload.object_type.value
object_type = payload.object_type
template_attribute = payload.template_attribute
if object_type != enums.ObjectType.SYMMETRIC_KEY:
@ -1097,9 +1097,7 @@ class KmipEngine(object):
response_payload = payloads.CreateResponsePayload(
object_type=payload.object_type,
unique_identifier=attributes.UniqueIdentifier(
str(managed_object.unique_identifier)
),
unique_identifier=str(managed_object.unique_identifier),
template_attribute=None
)

View File

@ -338,11 +338,11 @@ class TestIntegration(TestCase):
result = self._create_symmetric_key(key_name=key_name)
self._check_result_status(result, ResultStatus, ResultStatus.SUCCESS)
self._check_object_type(result.object_type.value, ObjectType,
self._check_object_type(result.object_type, ObjectType,
ObjectType.SYMMETRIC_KEY)
self._check_uuid(result.uuid.value, str)
self._check_uuid(result.uuid, str)
result = self.client.get(uuid=result.uuid.value, credential=None)
result = self.client.get(uuid=result.uuid, credential=None)
self._check_result_status(result, ResultStatus, ResultStatus.SUCCESS)
self._check_object_type(result.object_type, ObjectType,
@ -1160,10 +1160,10 @@ class TestIntegration(TestCase):
"""
key_name = 'Integration Test - Create-GetAttributeList-Destroy Key'
result = self._create_symmetric_key(key_name=key_name)
uid = result.uuid.value
uid = result.uuid
self.assertEqual(ResultStatus.SUCCESS, result.result_status.value)
self.assertEqual(ObjectType.SYMMETRIC_KEY, result.object_type.value)
self.assertEqual(ObjectType.SYMMETRIC_KEY, result.object_type)
self.assertIsInstance(uid, str)
try:

View File

@ -104,9 +104,9 @@ class TestKMIPClientIntegration(TestCase):
self._check_result_status(result.result_status.value, ResultStatus,
ResultStatus.SUCCESS)
self._check_object_type(result.object_type.value, ObjectType,
self._check_object_type(result.object_type, ObjectType,
ObjectType.SYMMETRIC_KEY)
self._check_uuid(result.uuid.value, str)
self._check_uuid(result.uuid, str)
# Check the template attribute type
self._check_template_attribute(result.template_attribute,
@ -122,7 +122,7 @@ class TestKMIPClientIntegration(TestCase):
credential = self.cred_factory.create_credential(credential_type,
credential_value)
result = self._create_symmetric_key()
uuid = result.uuid.value
uuid = result.uuid
result = self.client.get(uuid=uuid, credential=credential)
@ -146,7 +146,7 @@ class TestKMIPClientIntegration(TestCase):
credential = self.cred_factory.create_credential(credential_type,
credential_value)
result = self._create_symmetric_key()
uuid = result.uuid.value
uuid = result.uuid
# Verify the secret was created
result = self.client.get(uuid=uuid, credential=credential)

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@
from testtools import TestCase
import binascii
import six
from kmip.core.factories.secrets import SecretFactory
from kmip.core.factories.attributes import AttributeFactory
@ -245,13 +246,13 @@ class TestRequestMessage(TestCase):
object_type = request_payload.object_type
msg = "Bad object type type: expected {0}, received {1}"
self.assertIsInstance(object_type, attr.ObjectType,
msg.format(attr.ObjectType,
self.assertIsInstance(object_type, enums.ObjectType,
msg.format(enums.ObjectType,
type(object_type)))
msg = "Bad object type value: expected {0}, received {1}"
self.assertEqual(enums.ObjectType.SYMMETRIC_KEY, object_type.value,
self.assertEqual(enums.ObjectType.SYMMETRIC_KEY, object_type,
msg.format(enums.ObjectType.SYMMETRIC_KEY,
object_type.value))
object_type))
template_attribute = request_payload.template_attribute
msg = "Bad template attribute type: expected {0}, received {1}"
@ -359,7 +360,7 @@ class TestRequestMessage(TestCase):
batch_count=batch_count)
operation = contents.Operation(enums.Operation.CREATE)
object_type = attr.ObjectType(enums.ObjectType.SYMMETRIC_KEY)
object_type = enums.ObjectType.SYMMETRIC_KEY
name = AttributeType.CRYPTOGRAPHIC_ALGORITHM
value = CryptoAlgorithmEnum.AES
@ -1363,24 +1364,24 @@ class TestResponseMessage(TestCase):
exp_type, rcv_type))
object_type = response_payload.object_type
self.assertIsInstance(object_type, attr.ObjectType,
self.assertIsInstance(object_type, enums.ObjectType,
self.msg.format('object type', 'type',
attr.ObjectType,
enums.ObjectType,
type(object_type)))
self.assertEqual(enums.ObjectType.SYMMETRIC_KEY, object_type.value,
self.assertEqual(enums.ObjectType.SYMMETRIC_KEY, object_type,
self.msg.format('object type', 'value',
enums.ObjectType.SYMMETRIC_KEY,
object_type.value))
object_type))
unique_identifier = response_payload.unique_identifier
value = 'fb4b5b9c-6188-4c63-8142-fe9c328129fc'
self.assertIsInstance(unique_identifier, attr.UniqueIdentifier,
self.assertIsInstance(unique_identifier, six.string_types,
self.msg.format('unique identifier', 'type',
attr.UniqueIdentifier,
six.string_types,
type(unique_identifier)))
self.assertEqual(value, unique_identifier.value,
self.assertEqual(value, unique_identifier,
self.msg.format('unique identifier', 'value',
unique_identifier.value, value))
unique_identifier, value))
def test_create_response_write(self):
prot_ver = contents.ProtocolVersion(1, 1)
@ -1394,13 +1395,12 @@ class TestResponseMessage(TestCase):
batch_count=batch_count)
operation = contents.Operation(enums.Operation.CREATE)
result_status = contents.ResultStatus(enums.ResultStatus.SUCCESS)
object_type = attr.ObjectType(enums.ObjectType.SYMMETRIC_KEY)
object_type = enums.ObjectType.SYMMETRIC_KEY
uuid = 'fb4b5b9c-6188-4c63-8142-fe9c328129fc'
uniq_id = attr.UniqueIdentifier(uuid)
resp_pl = payloads.CreateResponsePayload(
object_type=object_type,
unique_identifier=uniq_id
unique_identifier=uuid
)
batch_item = messages.ResponseBatchItem(operation=operation,
result_status=result_status,

View File

@ -229,7 +229,8 @@ class TestProxyKmipClient(testtools.TestCase):
status = enums.ResultStatus.SUCCESS
result = results.CreateResult(
contents.ResultStatus(status),
uuid=attr.UniqueIdentifier(key_id))
uuid=key_id
)
with ProxyKmipClient() as client:
client.proxy.create.return_value = result

View File

@ -2395,7 +2395,7 @@ class TestKmipEngine(testtools.TestCase):
attribute_factory = factory.AttributeFactory()
# Build Create request
object_type = attributes.ObjectType(enums.ObjectType.SYMMETRIC_KEY)
object_type = enums.ObjectType.SYMMETRIC_KEY
template_attribute = objects.TemplateAttribute(
attributes=[
attribute_factory.create_attribute(
@ -2439,7 +2439,7 @@ class TestKmipEngine(testtools.TestCase):
"Processing operation: Create"
)
uid = response_payload.unique_identifier.value
uid = response_payload.unique_identifier
self.assertEqual('1', uid)
# Retrieve the stored object and verify all attributes were set
@ -2487,7 +2487,7 @@ class TestKmipEngine(testtools.TestCase):
e._data_session = e._data_store_session_factory()
e._logger = mock.MagicMock()
object_type = attributes.ObjectType(enums.ObjectType.PUBLIC_KEY)
object_type = enums.ObjectType.PUBLIC_KEY
payload = payloads.CreateRequestPayload(
object_type
)
@ -2520,7 +2520,7 @@ class TestKmipEngine(testtools.TestCase):
attribute_factory = factory.AttributeFactory()
# Test the error for omitting the Cryptographic Algorithm
object_type = attributes.ObjectType(enums.ObjectType.SYMMETRIC_KEY)
object_type = enums.ObjectType.SYMMETRIC_KEY
template_attribute = objects.TemplateAttribute(
attributes=[
attribute_factory.create_attribute(
@ -2566,7 +2566,7 @@ class TestKmipEngine(testtools.TestCase):
e._logger.reset_mock()
# Test the error for omitting the Cryptographic Length
object_type = attributes.ObjectType(enums.ObjectType.SYMMETRIC_KEY)
object_type = enums.ObjectType.SYMMETRIC_KEY
template_attribute = objects.TemplateAttribute(
attributes=[
attribute_factory.create_attribute(
@ -2612,7 +2612,7 @@ class TestKmipEngine(testtools.TestCase):
e._logger.reset_mock()
# Test the error for omitting the Cryptographic Usage Mask
object_type = attributes.ObjectType(enums.ObjectType.SYMMETRIC_KEY)
object_type = enums.ObjectType.SYMMETRIC_KEY
template_attribute = objects.TemplateAttribute(
attributes=[
attribute_factory.create_attribute(
@ -8000,7 +8000,7 @@ class TestKmipEngine(testtools.TestCase):
attribute_factory = factory.AttributeFactory()
# Build a SymmetricKey for registration.
object_type = attributes.ObjectType(enums.ObjectType.SYMMETRIC_KEY)
object_type = enums.ObjectType.SYMMETRIC_KEY
template_attribute = objects.TemplateAttribute(
attributes=[
attribute_factory.create_attribute(
@ -8042,7 +8042,7 @@ class TestKmipEngine(testtools.TestCase):
"Processing operation: Create"
)
uid = response_payload.unique_identifier.value
uid = response_payload.unique_identifier
self.assertEqual('1', uid)
e._logger.reset_mock()