From b04f5c2dc363f4472fe330d205165bac6daa0006 Mon Sep 17 00:00:00 2001 From: wyllys Date: Thu, 4 Sep 2014 14:42:20 -0400 Subject: [PATCH] Added support for LOCATE operation --- kmip/core/enums.py | 10 +++ kmip/core/messages/operations.py | 119 ++++++++++++++++++++++++++++++- kmip/core/repo/mem_repo.py | 7 ++ kmip/core/server.py | 17 +++++ kmip/demos/locate.py | 78 ++++++++++++++++++++ kmip/services/kmip_client.py | 55 ++++++++++++++ kmip/services/processor.py | 22 ++++++ kmip/services/results.py | 13 ++++ kmip/tests/core/test_server.py | 21 +++++- 9 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 kmip/demos/locate.py mode change 100755 => 100644 kmip/services/processor.py diff --git a/kmip/core/enums.py b/kmip/core/enums.py index e29df2f..c481f65 100644 --- a/kmip/core/enums.py +++ b/kmip/core/enums.py @@ -559,3 +559,13 @@ class CryptographicUsageMask(Enum): TRANSLATE_DECRYPT = 0x00020000 TRANSLATE_WRAP = 0x00040000 TRANSLATE_UNWRAP = 0x00080000 + +# 9.1.3.2.33 +class ObjectGroupMember(Enum): + GROUP_MEMBER_FRESH = 0x00000001 + GROUP_MEMBER_DEFAULT = 0x00000002 + +# 9.1.3.3.2 +class StorageStatusMask(Enum): + ONLINE_STORAGE = 0x00000001 + ARCHIVAL_STORAGE = 0x00000002 diff --git a/kmip/core/messages/operations.py b/kmip/core/messages/operations.py index 3cf89e2..a129f5c 100644 --- a/kmip/core/messages/operations.py +++ b/kmip/core/messages/operations.py @@ -24,6 +24,7 @@ from kmip.core.objects import TemplateAttribute from kmip.core.primitives import Struct from kmip.core.primitives import Enumeration +from kmip.core.primitives import Integer from kmip.core.utils import BytearrayStream @@ -429,11 +430,125 @@ class DestroyResponsePayload(Struct): pass +class LocateRequestPayload(Struct): + + # 9.1.3.2.33 + class ObjectGroupMember(Enumeration): + ENUM_TYPE = enums.ObjectGroupMember + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.OBJECT_GROUP_MEMBER) + + class MaximumItems(Integer): + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.MAXIMUM_ITEMS) + + # 9.1.3.3.2 + class StorageStatusMask(Enumeration): + ENUM_TYPE = enums.StorageStatusMask + + def __init__(self, value=None): + super(self.__class__, self).__init__(value, + Tags.STORAGE_STATUS_MASK) + + def __init__(self, maximum_items=None, storage_status_mask=None, + object_group_member=None, attributes=None): + super(self.__class__, self).__init__(enums.Tags.REQUEST_PAYLOAD) + self.maximum_items = maximum_items + self.storage_status_mask = storage_status_mask + self.object_group_member = object_group_member + self.attributes = attributes or [] + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + if self.is_tag_next(Tags.MAXIMUM_ITEMS, tstream): + self.maximum_items = LocateRequestPayload.MaximumItems() + self.maximum_items.read() + if self.is_tag_next(Tags.STORAGE_STATUS_MASK, tstream): + self.storage_status_mask = LocateRequestPayload.StorageStatusMask() + self.storage_status_mask.read() + if self.is_tag_next(Tags.OBJECT_GROUP_MEMBER, tstream): + self.object_group_member = LocateRequestPayload.ObjectGroupMember() + self.object_group_member.read(tstream) + while self.is_tag_next(Tags.TEMPLATE_ATTRIBUTE, tstream): + attr = TemplateAttribute() + attr.read(tstream) + self.attributes.append(attr) + + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + if self.maximum_items is not None: + self.maximum_items.write(tstream) + if self.storage_status_mask is not None: + self.storage_status_mask.write(tstream) + if self.attributes is not None: + for a in self.attributes: + a.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self._validate() + + def _validate(self): + # TODO Finish implementation. + pass + + +class LocateResponsePayload(Struct): + + def __init__(self, unique_identifiers=[]): + super(self.__class__, self).__init__(enums.Tags.RESPONSE_PAYLOAD) + self.unique_identifiers = unique_identifiers or [] + self.validate() + + def read(self, istream): + super(self.__class__, self).read(istream) + tstream = BytearrayStream(istream.read(self.length)) + + while self.is_tag_next(Tags.UNIQUE_IDENTIFIER, tstream): + ui = attributes.UniqueIdentifier() + ui.read(tstream) + self.unique_identifiers.append(ui) + + self.is_oversized(tstream) + self.validate() + + def write(self, ostream): + tstream = BytearrayStream() + + for ui in self.unique_identifier: + ui.write(tstream) + + # Write the length and value of the request payload + self.length = tstream.length() + super(self.__class__, self).write(ostream) + ostream.write(tstream.buffer) + + def validate(self): + self.__validate() + + def __validate(self): + # TODO (peter-hamilton) Finish implementation. + pass + + REQUEST_MAP = {enums.Operation.CREATE: CreateRequestPayload, enums.Operation.GET: GetRequestPayload, enums.Operation.DESTROY: DestroyRequestPayload, - enums.Operation.REGISTER: RegisterRequestPayload} + enums.Operation.REGISTER: RegisterRequestPayload, + enums.Operation.LOCATE: LocateRequestPayload} RESPONSE_MAP = {enums.Operation.CREATE: CreateResponsePayload, enums.Operation.GET: GetResponsePayload, + enums.Operation.REGISTER: RegisterResponsePayload, enums.Operation.DESTROY: DestroyResponsePayload, - enums.Operation.REGISTER: RegisterResponsePayload} + enums.Operation.LOCATE: LocateResponsePayload} diff --git a/kmip/core/repo/mem_repo.py b/kmip/core/repo/mem_repo.py index 8726f38..6d29e6a 100644 --- a/kmip/core/repo/mem_repo.py +++ b/kmip/core/repo/mem_repo.py @@ -45,3 +45,10 @@ class MemRepo(ManagedObjectRepo): return False del self.repo[uuid] return True + + def locate(self, maximum_items, storage_status_mask, + object_group_member, attributes): + # TODO - search objects, find one with matching attrs + if "1" in self.repo: + return [self.repo["1"]] + return None diff --git a/kmip/core/server.py b/kmip/core/server.py index 26b6e2d..1fa847e 100644 --- a/kmip/core/server.py +++ b/kmip/core/server.py @@ -47,6 +47,7 @@ from kmip.services.results import DestroyResult from kmip.services.results import GetResult from kmip.services.results import OperationResult from kmip.services.results import RegisterResult +from kmip.services.results import LocateResult class KMIP(object): @@ -68,6 +69,11 @@ class KMIP(object): def destroy(self, uuid, credential=None): raise NotImplementedError + def locate(self, maximum_items=None, storate_status_mask=None, + object_group_member=None, attributes=None, + credential=None): + raise NotImplementedError + class KMIPImpl(KMIP): @@ -248,6 +254,17 @@ class KMIPImpl(KMIP): ret_value = RS.SUCCESS return DestroyResult(ResultStatus(ret_value), uuid=uuid) + def locate(self, maximum_items=None, storage_status_mask=None, + object_group_member=None, attributes=None, + credential=None): + self.logger.debug('locate() called') + msg = 'locating object(s) from repo' + self.logger.debug(msg) + uuids = self.repo.locate(maximum_items, storage_status_mask, + object_group_member, attributes) + return LocateResult(ResultStatus(RS.SUCCESS), + locate_uuids=uuids) + def _validate_req_field(self, attrs, name, expected, msg, required=True): self.logger.debug('Validating attribute %s' % name) seen = False diff --git a/kmip/demos/locate.py b/kmip/demos/locate.py new file mode 100644 index 0000000..f8376e9 --- /dev/null +++ b/kmip/demos/locate.py @@ -0,0 +1,78 @@ +# Copyright (c) 2014 The Johns Hopkins University/Applied Physics Laboratory +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from kmip.core.enums import AttributeType +from kmip.core.enums import CredentialType +from kmip.core.enums import ObjectType +from kmip.core.enums import CryptographicAlgorithm +from kmip.core.enums import CryptographicUsageMask +from kmip.core.enums import NameType + +from kmip.core.attributes import Name + +from kmip.core.factories.attributes import AttributeFactory +from kmip.core.factories.credentials import CredentialFactory + +from kmip.core.objects import TemplateAttribute, Attribute + +from kmip.services.kmip_client import KMIPProxy + +import logging +import os + +if __name__ == '__main__': + f_log = os.path.join(os.path.dirname(__file__), '..', 'logconfig.ini') + logging.config.fileConfig(f_log) + logger = logging.getLogger(__name__) + + attribute_factory = AttributeFactory() + credential_factory = CredentialFactory() + + credential_type = CredentialType.USERNAME_AND_PASSWORD + credential_value = {'Username': 'Peter', 'Password': 'abc123'} + credential = credential_factory.create_credential(credential_type, + credential_value) + client = KMIPProxy() + client.open() + + object_type = ObjectType.SYMMETRIC_KEY + attribute_type = AttributeType.CRYPTOGRAPHIC_ALGORITHM + algorithm = attribute_factory.create_attribute(attribute_type, + CryptographicAlgorithm.AES) + mask_flags = [CryptographicUsageMask.ENCRYPT, + CryptographicUsageMask.DECRYPT] + attribute_type = AttributeType.CRYPTOGRAPHIC_USAGE_MASK + usage_mask = attribute_factory.create_attribute(attribute_type, + mask_flags) + name = Attribute.AttributeName('Name') + name_value = Name.NameValue('FOOBAR') + name_type = Name.NameType(NameType.UNINTERPRETED_TEXT_STRING) + value = Name(name_value=name_value, name_type=name_type) + nameattr = Attribute(attribute_name=name, attribute_value=value) + + attributes = [algorithm, usage_mask, nameattr] + + template_attribute = TemplateAttribute(attributes=attributes) + + result = client.create(object_type, template_attribute, + credential) + + attrs = [nameattr] + result = client.locate(attributes=attrs, credential=credential) + client.close() + + logger.debug('get() result status: {}'.format(result.result_status.enum)) + logger.debug('retrieved object type: {}'.format(result.object_type.enum)) + logger.debug('Located UUID: {}'.format(result.locate_uuids)) diff --git a/kmip/services/kmip_client.py b/kmip/services/kmip_client.py index 9cbb200..6c4d9aa 100644 --- a/kmip/services/kmip_client.py +++ b/kmip/services/kmip_client.py @@ -17,6 +17,7 @@ from kmip.services.results import CreateResult from kmip.services.results import GetResult from kmip.services.results import DestroyResult from kmip.services.results import RegisterResult +from kmip.services.results import LocateResult from kmip.core import attributes as attr @@ -93,6 +94,13 @@ class KMIPProxy(KMIP): secret=secret, credential=credential) + def locate(self, maximum_items=None, storage_status_mask=None, + object_group_member=None, attributes=None, credential=None): + return self._locate(maximum_items=maximum_items, + storage_status_mask=storage_status_mask, + object_group_member=object_group_member, + attributes=attributes, credential=credential) + def _create(self, object_type=None, template_attribute=None, @@ -263,6 +271,53 @@ class KMIPProxy(KMIP): payload_template_attribute) return result + def _locate(self, maximum_items=None, storage_status_mask=None, + object_group_member=None, attributes=[], credential=None): + + operation = Operation(OperationEnum.LOCATE) + + mxi = None + ssmask = None + objgrp = None + + if maximum_items is not None: + mxi = operations.LocateRequestPayload.MaximumItems(maximum_items) + if storage_status_mask is not None: + m = storage_status_mask + ssmask = operations.LocateRequestPayload.StorageStatusMask(m) + if object_group_member is not None: + o = object_group_member + objgrp = operations.LocateRequestPayload.ObjectGroupMember(o) + + payload = operations.LocateRequestPayload(maximum_items=mxi, + storage_status_mask=ssmask, + object_group_member=objgrp, + attributes=attributes) + + batch_item = messages.RequestBatchItem(operation=operation, + request_payload=payload) + + message = self._build_request_message(credential, [batch_item]) + self._send_message(message) + message = messages.ResponseMessage() + data = self._receive_message() + + message.read(data) + batch_items = message.batch_items + batch_item = batch_items[0] + payload = batch_item.response_payload + + if payload is None: + locate_uuids = None + else: + locate_uuids = payload.unique_identifiers + + result = LocateResult(batch_item.result_status, + batch_item.result_reason, + batch_item.result_message, + locate_uuids) + return result + def _build_request_message(self, credential, batch_items): protocol_version = ProtocolVersion.create(1, 1) diff --git a/kmip/services/processor.py b/kmip/services/processor.py old mode 100755 new mode 100644 index 8d2ed6b..bbdea42 --- a/kmip/services/processor.py +++ b/kmip/services/processor.py @@ -31,6 +31,7 @@ from kmip.core.messages.operations import CreateResponsePayload from kmip.core.messages.operations import GetResponsePayload from kmip.core.messages.operations import DestroyResponsePayload from kmip.core.messages.operations import RegisterResponsePayload +from kmip.core.messages.operations import LocateResponsePayload from kmip.core.enums import Operation from kmip.core.enums import ResultStatus as RS @@ -168,6 +169,8 @@ class Processor(object): return self._process_destroy_request(payload) elif op is Operation.REGISTER: return self._process_register_request(payload) + elif op is Operation.LOCATE: + return self._process_locate_request(payload) else: raise NotImplementedError() @@ -252,3 +255,22 @@ class Processor(object): template_attribute=template_attr) return (result_status, result_reason, result_message, resp_pl) + + def _process_locate_request(self, payload): + max_items = payload.maximum_items + storage_mask = payload.status_storage_mask + objgrp_member = payload.object_group_member + attributes = payload.attributes + + result = self._handler.locate(max_items, storage_mask, + objgrp_member, attributes) + + result_status = result.result_status + result_reason = result.result_reason + result_message = result.result_message + + uuids = result.locate_uuids + + resp_pl = LocateResponsePayload(unique_identifiers=uuids) + + return (result_status, result_reason, result_message, resp_pl) diff --git a/kmip/services/results.py b/kmip/services/results.py index 2ee6134..efa4b1d 100644 --- a/kmip/services/results.py +++ b/kmip/services/results.py @@ -125,3 +125,16 @@ class DestroyResult(OperationResult): self.uuid = uuid else: self.uuid = None + + +class LocateResult(OperationResult): + + def __init__(self, + result_status, + result_reason=None, + result_message=None, + locate_uuids=None): + super(self.__class__, self).__init__(result_status, + result_reason, + result_message) + self.locate_uuids = locate_uuids diff --git a/kmip/tests/core/test_server.py b/kmip/tests/core/test_server.py index 7a1ad46..c4dd248 100644 --- a/kmip/tests/core/test_server.py +++ b/kmip/tests/core/test_server.py @@ -20,6 +20,7 @@ from kmip.core.attributes import CryptographicLength from kmip.core.attributes import CryptographicUsageMask from kmip.core.attributes import UniqueIdentifier from kmip.core.attributes import ObjectType +from kmip.core.attributes import Name from kmip.core.enums import AttributeType from kmip.core.enums import CryptographicAlgorithm as CryptoAlgorithmEnum from kmip.core.enums import CryptographicUsageMask as CryptoUsageMaskEnum @@ -28,6 +29,7 @@ from kmip.core.enums import KeyFormatType as KeyFormatTypeEnum from kmip.core.enums import ObjectType as ObjectTypeEnum from kmip.core.enums import ResultReason from kmip.core.enums import ResultStatus +from kmip.core.enums import NameType from kmip.core.factories.attributes import AttributeFactory from kmip.core.keys import RawKey from kmip.core.messages.contents import KeyCompressionType @@ -35,6 +37,7 @@ from kmip.core.messages.contents import KeyFormatType from kmip.core.objects import KeyBlock from kmip.core.objects import KeyValueStruct from kmip.core.objects import TemplateAttribute +from kmip.core.objects import Attribute from kmip.core.secrets import SymmetricKey from kmip.core.server import KMIPImpl @@ -474,6 +477,14 @@ class TestKMIPServer(TestCase): crypto_length, usage) return SymmetricKey(key_block) + def _make_nameattr(self): + name = Attribute.AttributeName('Name') + name_value = Name.NameValue('TESTNAME') + name_type = Name.NameType(NameType.UNINTERPRETED_TEXT_STRING) + value = Name(name_value=name_value, name_type=name_type) + nameattr = Attribute(attribute_name=name, attribute_value=value) + return nameattr + def _get_attrs(self): attr_factory = AttributeFactory() algorithm = self._get_alg_attr(self.algorithm_name) @@ -483,7 +494,8 @@ class TestKMIPServer(TestCase): CryptoUsageMaskEnum.DECRYPT] usage_mask = attr_factory.create_attribute(attribute_type, mask_flags) - return [algorithm, usage_mask, length] + nameattr = self._make_nameattr() + return [algorithm, usage_mask, length, nameattr] def _get_alg_attr(self, alg=None): if alg is None: @@ -506,3 +518,10 @@ class TestKMIPServer(TestCase): return attribute.attribute_value.value ==\ attr_expected.attribute_value.value return False + + def test_locate(self): + self._create() + attrs = [self._make_nameattr()] + res = self.kmip.locate(attributes=attrs) + self.assertEqual(ResultStatus.SUCCESS, res.result_status.enum, + 'locate result status did not return success')