def get_ticket(self, source, target, crypto, key): # prepare metadata md = {'requestor': source, 'target': target, 'timestamp': time.time(), 'nonce': struct.unpack('Q', os.urandom(8))[0]} metadata = base64.b64encode(jsonutils.dumps(md)) # sign metadata signature = crypto.sign(key, metadata) # HTTP request reply = self._get_ticket({'metadata': metadata, 'signature': signature}) # verify reply signature = crypto.sign(key, (reply['metadata'] + reply['ticket'])) if signature != reply['signature']: raise InvalidEncryptedTicket(md['source'], md['destination']) md = jsonutils.loads(base64.b64decode(reply['metadata'])) if ((md['source'] != source or md['destination'] != target or md['expiration'] < time.time())): raise InvalidEncryptedTicket(md['source'], md['destination']) # return ticket data tkt = jsonutils.loads(crypto.decrypt(key, reply['ticket'])) return tkt, md['expiration']
def decrypt(self, decrypt_dto, kek_meta_dto, kek_meta_extended, project_id): session = self._create_working_session() key = self._unwrap_key(kek_meta_dto.plugin_meta, session) meta_extended = json.loads(kek_meta_extended) iv = base64.b64decode(meta_extended['iv']) iv = self.ffi.new("CK_BYTE[]", iv) ck_mechanism = self._build_gcm_mech(iv) rv = self.lib.C_DecryptInit(session, ck_mechanism.mech, key) self._check_error(rv) pt = self.ffi.new( "CK_BYTE[{0}]".format(len(decrypt_dto.encrypted)) ) pt_len = self.ffi.new("CK_ULONG *", len(decrypt_dto.encrypted)) rv = self.lib.C_Decrypt( session, decrypt_dto.encrypted, len(decrypt_dto.encrypted), pt, pt_len ) self._check_error(rv) self._close_session(session) return self._unpad(self.ffi.buffer(pt, pt_len[0])[:])
def test_should_get_list_orders(self): self.resource.on_get(self.req, self.resp, self.keystone_id) self.order_repo.get_by_create_date \ .assert_called_once_with(self.keystone_id, offset_arg=self.params.get('offset', self.offset), limit_arg=self.params.get('limit', self.limit), suppress_exception=True) resp_body = jsonutils.loads(self.resp.body) self.assertTrue('previous' in resp_body) self.assertTrue('next' in resp_body) url_nav_next = self._create_url(self.keystone_id, self.offset + self.limit, self.limit) self.assertTrue(self.resp.body.count(url_nav_next) == 1) url_nav_prev = self._create_url(self.keystone_id, 0, self.limit) self.assertTrue(self.resp.body.count(url_nav_prev) == 1) url_hrefs = self._create_url(self.keystone_id) self.assertTrue(self.resp.body.count(url_hrefs) == (self.num_orders + 2))
def load_json(cls, data, default_rule=None): """Allow loading of JSON rule data.""" # Suck in the JSON data and parse the rules rules = dict((k, parse_rule(v)) for k, v in jsonutils.loads(data).items()) return cls(rules, default_rule)
def load_body(req, resp=None, validator=None): """ Helper function for loading an HTTP request body from JSON into a Python dictionary """ try: raw_json = req.stream.read(MAX_BYTES_REQUEST_INPUT_ACCEPTED) except IOError: LOG.exception("Problem reading request JSON stream.") abort(falcon.HTTP_500, 'Read Error', req, resp) try: #TODO: Investigate how to get UTF8 format via openstack jsonutils: # parsed_body = json.loads(raw_json, 'utf-8') parsed_body = json.loads(raw_json) except ValueError: LOG.exception("Problem loading request JSON.") abort(falcon.HTTP_400, 'Malformed JSON', req, resp) if validator: try: parsed_body = validator.validate(parsed_body) except exception.InvalidObject as e: LOG.exception("Failed to validate JSON information") abort(falcon.HTTP_400, str(e), req, resp) except exception.UnsupportedField as e: LOG.exception("Provided field value is not supported") abort(falcon.HTTP_400, str(e), req, resp) except exception.LimitExceeded as e: LOG.exception("Data limit exceeded") abort(falcon.HTTP_413, str(e), req, resp) return parsed_body
def _decode_esek(self, key, source, target, timestamp, esek): """This function decrypts the esek buffer passed in and returns a KeyStore to be used to check and decrypt the received message. :param key: The key to use to decrypt the ticket (esek) :param source: The name of the source service :param traget: The name of the target service :param timestamp: The incoming message timestamp :param esek: a base64 encoded encrypted block containing a JSON string """ rkey = None try: s = self._crypto.decrypt(key, esek) j = jsonutils.loads(s) rkey = base64.b64decode(j['key']) expiration = j['timestamp'] + j['ttl'] if j['timestamp'] > timestamp or timestamp > expiration: raise InvalidExpiredTicket(source, target) except Exception: raise InvalidEncryptedTicket(source, target) info = '%s,%s,%s' % (source, target, str(j['timestamp'])) sek = self._hkdf.expand(rkey, info, len(key) * 2) return self._split_key(sek, len(key))
def load_body(req, resp=None, validator=None): """ Helper function for loading an HTTP request body from JSON into a Python dictionary """ try: raw_json = req.stream.read(MAX_BYTES_REQUEST_INPUT_ACCEPTED) except IOError: LOG.exception("Problem reading request JSON stream.") abort(falcon.HTTP_500, 'Read Error', req, resp) try: #TODO: Investigate how to get UTF8 format via openstack jsonutils: # parsed_body = json.loads(raw_json, 'utf-8') parsed_body = json.loads(raw_json) except ValueError: LOG.exception("Problem loading request JSON.") abort(falcon.HTTP_400, 'Malformed JSON', req, resp) if validator: try: parsed_body = validator.validate(parsed_body) except exception.InvalidObject as e: LOG.exception("Failed to validate JSON information") abort(falcon.HTTP_400, str(e), req, resp) except exception.LimitExceeded as e: LOG.exception("Data limit exceeded") abort(falcon.HTTP_413, str(e), req, resp) return parsed_body
def _get_authenticated_context(self, req): # NOTE(bcwaldon): X-Roles is a csv string, but we need to parse # it into a list to be useful roles_header = req.headers.get('X-Roles', '') roles = [r.strip().lower() for r in roles_header.split(',')] # NOTE(bcwaldon): This header is deprecated in favor of X-Auth-Token # NOTE(mkbhanda): keeping this just-in-case for swift deprecated_token = req.headers.get('X-Storage-Token') service_catalog = None if req.headers.get('X-Service-Catalog') is not None: try: catalog_header = req.headers.get('X-Service-Catalog') service_catalog = json.loads(catalog_header) except ValueError: msg = u._('Problem processing X-Service-Catalog') LOG.exception(msg) raise webob.exc.HTTPInternalServerError(msg) kwargs = { 'user': req.headers.get('X-User-Id'), 'project': req.headers.get('X-Project-Id'), 'roles': roles, 'is_admin': CONF.admin_role.strip().lower() in roles, 'auth_tok': req.headers.get('X-Auth-Token', deprecated_token), 'owner_is_project': CONF.owner_is_project, 'service_catalog': service_catalog, 'policy_enforcer': self.policy_enforcer, } return barbican.context.RequestContext(**kwargs)
def test_should_get_list_orders(self): self.resource.on_get(self.req, self.resp, self.keystone_id) self.order_repo.get_by_create_date \ .assert_called_once_with(self.keystone_id, offset_arg=self.params.get('offset', self.offset), limit_arg=self.params.get('limit', self.limit), suppress_exception=True) resp_body = jsonutils.loads(self.resp.body) self.assertTrue('previous' in resp_body) self.assertTrue('next' in resp_body) url_nav_next = self._create_url(self.keystone_id, self.offset + self.limit, self.limit) self.assertTrue(self.resp.body.count(url_nav_next) == 1) url_nav_prev = self._create_url(self.keystone_id, 0, self.limit) self.assertTrue(self.resp.body.count(url_nav_prev) == 1) url_hrefs = self._create_url(self.keystone_id) self.assertTrue( self.resp.body.count(url_hrefs) == (self.num_orders + 2))
def load_body(req, resp=None, validator=None): """Helper function for loading an HTTP request body from JSON. This body is placed into into a Python dictionary. :param req: The HTTP request instance to load the body from. :param resp: The HTTP response instance. :param validator: The JSON validator to enforce. :return: A dict of values from the JSON request. """ try: body = req.body_file.read(CONF.max_allowed_request_size_in_bytes) req.body_file.seek(0) except IOError: LOG.exception(u._LE("Problem reading request JSON stream.")) pecan.abort(500, u._('Read Error')) try: # TODO(jwood): Investigate how to get UTF8 format via openstack # jsonutils: # parsed_body = json.loads(raw_json, 'utf-8') parsed_body = json.loads(body) strip_whitespace(parsed_body) except ValueError: LOG.exception(u._LE("Problem loading request JSON.")) pecan.abort(400, u._('Malformed JSON')) if validator: try: parsed_body = validator.validate(parsed_body) except exception.BarbicanHTTPException as e: LOG.exception(e.message) pecan.abort(e.status_code, e.client_message) return parsed_body
def _get_authenticated_context(self, req): # NOTE(bcwaldon): X-Roles is a csv string, but we need to parse # it into a list to be useful roles_header = req.headers.get('X-Roles', '') roles = [r.strip().lower() for r in roles_header.split(',')] # NOTE(bcwaldon): This header is deprecated in favor of X-Auth-Token # NOTE(mkbhanda): keeping this just-in-case for swift deprecated_token = req.headers.get('X-Storage-Token') service_catalog = None if req.headers.get('X-Service-Catalog') is not None: try: catalog_header = req.headers.get('X-Service-Catalog') service_catalog = json.loads(catalog_header) except ValueError: msg = u._('Problem processing X-Service-Catalog') LOG.exception(msg) raise webob.exc.HTTPInternalServerError(msg) kwargs = { 'user': req.headers.get('X-User-Id'), 'project': req.headers.get('X-Tenant-Id'), 'roles': roles, 'is_admin': CONF.admin_role.strip().lower() in roles, 'auth_tok': req.headers.get('X-Auth-Token', deprecated_token), 'owner_is_project': CONF.owner_is_project, 'service_catalog': service_catalog, 'policy_enforcer': self.policy_enforcer, } return barbican.context.RequestContext(**kwargs)
def load_json(cls, data, default_rule=None): """Allow loading of JSON rule data.""" # Suck in the JSON data and parse the rules rules = dict( (k, parse_rule(v)) for k, v in jsonutils.loads(data).items()) return cls(rules, default_rule)
def decrypt(self, encrypted, kek_meta_dto, kek_meta_extended, keystone_id): key = self._get_key_by_label(kek_meta_dto.kek_label) meta_extended = json.loads(kek_meta_extended) iv = base64.b64decode(meta_extended['iv']) gcm = self._build_gcm_params(iv) mech = PyKCS11.Mechanism(self.algorithm, gcm) decrypted = self.session.decrypt(key, encrypted, mech) secret = b''.join(chr(i) for i in decrypted) return secret
def decrypt(self, decrypt_dto, kek_meta_dto, kek_meta_extended, keystone_id): key = self._get_key_by_label(kek_meta_dto.kek_label) meta_extended = json.loads(kek_meta_extended) iv = base64.b64decode(meta_extended['iv']) gcm = self._build_gcm_params(iv) mech = PyKCS11.Mechanism(self.algorithm, gcm) decrypted = self.session.decrypt(key, decrypt_dto.encrypted, mech) secret = b''.join(chr(i) for i in decrypted) return secret
def _unwrap_key(self, plugin_meta): """Unwraps byte string to key handle in HSM. :param plugin_meta: kek_meta_dto plugin meta (json string) :returns: Key handle from HSM. No unencrypted bytes. """ meta = json.loads(plugin_meta) iv = base64.b64decode(meta['iv']) hmac = base64.b64decode(meta['hmac']) wrapped_key = base64.b64decode(meta['wrapped_key']) mkek = self._get_key_handle(meta['mkek_label']) hmac_key = self._get_key_handle(meta['hmac_label']) LOG.debug("Unwrapping key with %s mkek label", meta['mkek_label']) hmac_bytelist = PyKCS11.ckbytelist() hmac_bytelist.reserve(len(hmac)) for x in hmac: hmac_bytelist.append(ord(x)) wrapped_bytelist = PyKCS11.ckbytelist() wrapped_bytelist.reserve(len(wrapped_key)) for x in wrapped_key: wrapped_bytelist.append(ord(x)) LOG.debug("Verifying key with %s hmac label", meta['hmac_label']) self._verify_hmac(hmac_key, hmac_bytelist, wrapped_bytelist) unwrapped = PyKCS11.LowLevel.CK_OBJECT_HANDLE() m = PyKCS11.LowLevel.CK_MECHANISM() m.mechanism = PyKCS11.LowLevel.CKM_AES_CBC_PAD m.pParameter = iv template = ( (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY), (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES), (PyKCS11.CKA_ENCRYPT, True), (PyKCS11.CKA_DECRYPT, True), (PyKCS11.CKA_TOKEN, False), (PyKCS11.CKA_WRAP, True), (PyKCS11.CKA_UNWRAP, True), (PyKCS11.CKA_EXTRACTABLE, True) ) ckattr = self.session._template2ckattrlist(template) self._check_error( self.pkcs11.lib.C_UnwrapKey( self.rw_session.session, m, mkek, wrapped_bytelist, ckattr, unwrapped ) ) return unwrapped
def test_should_get_secret_as_json(self): self.resource.on_get(self.req, self.resp, self.tenant_id, self.secret.id) self.secret_repo.get.assert_called_once_with(entity_id=self.secret.id, suppress_exception=True) self.assertEquals(self.resp.status, falcon.HTTP_200) resp_body = jsonutils.loads(self.resp.body) self.assertTrue('content_types' in resp_body) self.assertTrue(self.datum.mime_type in resp_body['content_types'].itervalues())
def _get_reply(self, url, resp): if resp.text: try: body = jsonutils.loads(resp.text) reply = body['reply'] except (KeyError, TypeError, ValueError): msg = "Failed to decode reply: %s" % resp.text raise CommunicationError(url, msg) else: msg = "No reply data was returned." raise CommunicationError(url, msg) return reply
def test_should_handle_no_secrets(self): del self.secrets[:] self.resource.on_get(self.req, self.resp, self.keystone_id) self.secret_repo.get_by_create_date \ .assert_called_once_with(self.keystone_id, offset_arg=self.offset, limit_arg=self.limit, suppress_exception=True) resp_body = jsonutils.loads(self.resp.body) self.assertFalse('previous' in resp_body) self.assertFalse('next' in resp_body)
def test_should_get_secret_as_json(self): self.resource.on_get(self.req, self.resp, self.keystone_id, self.secret.id) self.secret_repo\ .get.assert_called_once_with(entity_id=self.secret.id, keystone_id=self.keystone_id, suppress_exception=True) self.assertEquals(self.resp.status, falcon.HTTP_200) resp_body = jsonutils.loads(self.resp.body) self.assertTrue('content_types' in resp_body) self.assertTrue( self.datum.mime_type in resp_body['content_types'].itervalues())
def _unpack_json_msg(self, msg): """Load the JSON data in msg if msg.content_type indicates that it is necessary. Put the loaded data back into msg.content and update msg.content_type appropriately. A Qpid Message containing a dict will have a content_type of 'amqp/map', whereas one containing a string that needs to be converted back from JSON will have a content_type of JSON_CONTENT_TYPE. :param msg: a Qpid Message object :returns: None """ if msg.content_type == JSON_CONTENT_TYPE: msg.content = jsonutils.loads(msg.content) msg.content_type = 'amqp/map'
def test_should_handle_no_orders(self): del self.orders[:] self.resource.on_get(self.req, self.resp, self.keystone_id) self.order_repo.get_by_create_date \ .assert_called_once_with(self.keystone_id, offset_arg=self.params.get('offset', self.offset), limit_arg=self.params.get('limit', self.limit), suppress_exception=True) resp_body = jsonutils.loads(self.resp.body) self.assertFalse('previous' in resp_body) self.assertFalse('next' in resp_body)
def test_should_get_secret_as_json(self): self.resource.on_get(self.req, self.resp, self.keystone_id, self.secret.id) self.secret_repo \ .get.assert_called_once_with(entity_id=self.secret.id, keystone_id=self.keystone_id, suppress_exception=True) self.assertEquals(self.resp.status, falcon.HTTP_200) resp_body = jsonutils.loads(self.resp.body) self.assertNotIn('content_encodings', resp_body) self.assertIn('content_types', resp_body) self.assertIn(self.datum.content_type, resp_body['content_types'].itervalues()) self.assertNotIn('mime_type', resp_body)
def decode(self, version, metadata, message, signature): """This is the main decoding function. It takes a version, metadata, message and signature strings and returns a tuple with a (decrypted) message and metadata or raises an exception in case of error. :param version: the current envelope version :param metadata: a JSON serialized object with metadata for validation :param message: a JSON serialized (base64 encoded encrypted) message :param signature: a base64 encoded signature """ md = jsonutils.loads(metadata) check_args = ('source', 'destination', 'timestamp', 'nonce', 'esek', 'encryption') for arg in check_args: if arg not in md: raise InvalidMetadata('Missing metadata "%s"' % arg) if md['destination'] != self._name: # TODO(simo) handle group keys by checking target raise UnknownDestinationName(md['destination']) try: skey, ekey = self._decode_esek(self._key, md['source'], md['destination'], md['timestamp'], md['esek']) except InvalidExpiredTicket: raise except Exception: raise InvalidMetadata('Failed to decode ESEK for %s/%s' % ( md['source'], md['destination'])) sig = self._crypto.sign(skey, version + metadata + message) if sig != signature: raise InvalidSignature(md['source'], md['destination']) if md['encryption'] is True: msg = self._crypto.decrypt(ekey, message) else: msg = message return (md, msg)
def decrypt(self, decrypt_dto, kek_meta_dto, kek_meta_extended, project_id): key = self._unwrap_key(kek_meta_dto.plugin_meta) meta_extended = json.loads(kek_meta_extended) iv = base64.b64decode(meta_extended['iv']) iv = self.ffi.new("CK_BYTE[]", iv) mech = self._build_gcm_mech(iv) with self.dec_sem: rv = self.lib.C_DecryptInit(self.session, mech, key) self._check_error(rv) pt = self.ffi.new("CK_BYTE[{0}]".format(len( decrypt_dto.encrypted))) pt_len = self.ffi.new("CK_ULONG *", len(decrypt_dto.encrypted)) rv = self.lib.C_Decrypt(self.session, decrypt_dto.encrypted, len(decrypt_dto.encrypted), pt, pt_len) self._check_error(rv) return self._unpad(self.ffi.buffer(pt, pt_len[0])[:])
def load_body(req): """ Helper function for loading an HTTP request body from JSON into a Python dictionary """ try: raw_json = req.stream.read() except IOError: abort(falcon.HTTP_500, 'Read Error') try: #TODO: Investigate how to get UTF8 format via openstack jsonutils: # parsed_body = json.loads(raw_json, 'utf-8') parsed_body = json.loads(raw_json) except ValueError: abort(falcon.HTTP_400, 'Malformed JSON') return parsed_body
def test_should_abort_with_validation_unsupported_field( self, mock_pecan_abort): mock_pecan_abort.side_effect = ValueError('Abort!') body = json.dumps({'key1': 'value1'}) req = mock.MagicMock() req.body_file = mock.MagicMock() req.body_file.read.return_value = body validator = mock.MagicMock() validator.validate.side_effect = exception.UnsupportedField('Field') exception_result = self.assertRaises( ValueError, api.load_body, req, validator=validator) self.assertEqual('Abort!', exception_result.message) validator.validate.assert_called_once_with(json.loads(body))
def unwrap_key(self, plugin_meta, session): """Unwraps byte string to key handle in HSM. :param plugin_meta: kek_meta_dto plugin meta (json string) :returns: Key handle from HSM. No unencrypted bytes. """ meta = json.loads(plugin_meta) iv = base64.b64decode(meta['iv']) hmac = base64.b64decode(meta['hmac']) wrapped_key = base64.b64decode(meta['wrapped_key']) mkek = self.get_key_handle(meta['mkek_label'], session) hmac_key = self.get_key_handle(meta['hmac_label'], session) LOG.debug("Unwrapping key with %s mkek label", meta['mkek_label']) LOG.debug("Verifying key with %s hmac label", meta['hmac_label']) self.verify_hmac(hmac_key, hmac, wrapped_key, session) unwrapped = self.ffi.new("CK_OBJECT_HANDLE *") mech = self.ffi.new("CK_MECHANISM *") mech.mechanism = CKM_AES_CBC_PAD iv = self.ffi.new("CK_BYTE[]", iv) mech.parameter = iv mech.parameter_len = 16 ck_attributes = self.build_attributes([ Attribute(CKA_CLASS, CKO_SECRET_KEY), Attribute(CKA_KEY_TYPE, CKK_AES), Attribute(CKA_ENCRYPT, True), Attribute(CKA_DECRYPT, True), Attribute(CKA_TOKEN, False), Attribute(CKA_WRAP, True), Attribute(CKA_UNWRAP, True), Attribute(CKA_EXTRACTABLE, True) ]) rv = self.lib.C_UnwrapKey( session, mech, mkek, wrapped_key, len(wrapped_key), ck_attributes.template, len(ck_attributes.template), unwrapped ) self.check_error(rv) return unwrapped[0]
def deserialize_msg(msg): # NOTE(russellb): Hang on to your hats, this road is about to # get a little bumpy. # # Robustness Principle: # "Be strict in what you send, liberal in what you accept." # # At this point we have to do a bit of guessing about what it # is we just received. Here is the set of possibilities: # # 1) We received a dict. This could be 2 things: # # a) Inspect it to see if it looks like a standard message envelope. # If so, great! # # b) If it doesn't look like a standard message envelope, it could either # be a notification, or a message from before we added a message # envelope (referred to as version 1.0). # Just return the message as-is. # # 2) It's any other non-dict type. Just return it and hope for the best. # This case covers return values from rpc.call() from before message # envelopes were used. (messages to call a method were always a dict) if not isinstance(msg, dict): # See #2 above. return msg base_envelope_keys = (_VERSION_KEY, _MESSAGE_KEY) if not all(map(lambda key: key in msg, base_envelope_keys)): # See #1.b above. return msg # At this point we think we have the message envelope # format we were expecting. (#1.a above) if not version_is_compatible(_RPC_ENVELOPE_VERSION, msg[_VERSION_KEY]): raise UnsupportedRpcEnvelopeVersion(version=msg[_VERSION_KEY]) raw_msg = jsonutils.loads(msg[_MESSAGE_KEY]) return raw_msg
def deserialize_msg(msg): # NOTE(russellb): Hang on to your hats, this road is about to # get a little bumpy. # # Robustness Principle: # "Be strict in what you send, liberal in what you accept." # # At this point we have to do a bit of guessing about what it # is we just received. Here is the set of possibilities: # # 1) We received a dict. This could be 2 things: # # a) Inspect it to see if it looks like a standard message envelope. # If so, great! # # b) If it doesn't look like a standard message envelope, it could either # be a notification, or a message from before we added a message # envelope (referred to as version 1.0). # Just return the message as-is. # # 2) It's any other non-dict type. Just return it and hope for the best. # This case covers return values from rpc.call() from before message # envelopes were used. (messages to call a method were always a dict) if not isinstance(msg, dict): # See #2 above. return msg base_envelope_keys = (_VERSION_KEY, _MESSAGE_KEY) if not all(map(lambda key: key in msg, base_envelope_keys)): # See #1.b above. return msg # At this point we think we have the message envelope # format we were expecting. (#1.a above) if not version_is_compatible(_RPC_ENVELOPE_VERSION, msg[_VERSION_KEY]): raise UnsupportedRpcEnvelopeVersion(version=msg[_VERSION_KEY]) raw_msg = jsonutils.loads(msg[_MESSAGE_KEY]) return raw_msg
def test_should_get_secret_meta_for_binary(self): self.req.accept = 'application/json' self.datum.content_type = "application/octet-stream" self.datum.cypher_text = 'aaaa' self.resource.on_get(self.req, self.resp, self.keystone_id, self.secret.id) self.secret_repo \ .get.assert_called_once_with(entity_id=self.secret.id, keystone_id=self.keystone_id, suppress_exception=True) self.assertEquals(self.resp.status, falcon.HTTP_200) resp_body = jsonutils.loads(self.resp.body) self.assertIsNotNone(resp_body) self.assertIn('content_types', resp_body) self.assertIn(self.datum.content_type, resp_body['content_types'].itervalues())
def load_body(req, resp=None, validator=None): """Helper function for loading an HTTP request body from JSON. This body is placed into into a Python dictionary. :param req: The HTTP request instance to load the body from. :param resp: The HTTP response instance. :param validator: The JSON validator to enforce. :return: A dict of values from the JSON request. """ try: body = req.body_file.read(CONF.max_allowed_request_size_in_bytes) req.body_file.seek(0) except IOError: LOG.exception(u._LE("Problem reading request JSON stream.")) pecan.abort(500, u._('Read Error')) try: # TODO(jwood): Investigate how to get UTF8 format via openstack # jsonutils: # parsed_body = json.loads(raw_json, 'utf-8') parsed_body = json.loads(body) strip_whitespace(parsed_body) except ValueError: LOG.exception(u._LE("Problem loading request JSON.")) pecan.abort(400, u._('Malformed JSON')) if validator: try: parsed_body = validator.validate(parsed_body) except exception.InvalidObject as e: LOG.exception(u._LE("Failed to validate JSON information")) pecan.abort(400, str(e)) except exception.UnsupportedField as e: LOG.exception(u._LE("Provided field value is not supported")) pecan.abort(400, str(e)) except exception.LimitExceeded as e: LOG.exception(u._LE("Data limit exceeded")) pecan.abort(413, str(e)) return parsed_body
def _unwrap_key(self, plugin_meta): """Unwraps byte string to key handle in HSM. :param plugin_meta: kek_meta_dto plugin meta (json string) :returns: Key handle from HSM. No unencrypted bytes. """ meta = json.loads(plugin_meta) iv = base64.b64decode(meta['iv']) hmac = base64.b64decode(meta['hmac']) wrapped_key = base64.b64decode(meta['wrapped_key']) mkek = self._get_key_handle(meta['mkek_label']) hmac_key = self._get_key_handle(meta['hmac_label']) LOG.debug("Unwrapping key with %s mkek label", meta['mkek_label']) LOG.debug("Verifying key with %s hmac label", meta['hmac_label']) self._verify_hmac(hmac_key, hmac, wrapped_key) unwrapped = self.ffi.new("CK_OBJECT_HANDLE *") mech = self.ffi.new("CK_MECHANISM *") mech.mechanism = CKM_AES_CBC_PAD iv = self.ffi.new("CK_BYTE[]", iv) mech.parameter = iv mech.parameter_len = 16 template, val_list = self._build_attributes([ Attribute(CKA_CLASS, CKO_SECRET_KEY), Attribute(CKA_KEY_TYPE, CKK_AES), Attribute(CKA_ENCRYPT, True), Attribute(CKA_DECRYPT, True), Attribute(CKA_TOKEN, False), Attribute(CKA_WRAP, True), Attribute(CKA_UNWRAP, True), Attribute(CKA_EXTRACTABLE, True) ]) rv = self.lib.C_UnwrapKey(self.rw_session, mech, mkek, wrapped_key, len(wrapped_key), template, len(template), unwrapped) self._check_error(rv) return unwrapped[0]
def deserialize_remote_exception(conf, data): failure = jsonutils.loads(str(data)) trace = failure.get('tb', []) message = failure.get('message', "") + "\n" + "\n".join(trace) name = failure.get('class') module = failure.get('module') # NOTE(ameade): We DO NOT want to allow just any module to be imported, in # order to prevent arbitrary code execution. if module not in conf.allowed_rpc_exception_modules: return RemoteError(name, failure.get('message'), trace) try: mod = importutils.import_module(module) klass = getattr(mod, name) if not issubclass(klass, Exception): raise TypeError("Can only deserialize Exceptions") failure = klass(*failure.get('args', []), **failure.get('kwargs', {})) except (AttributeError, TypeError, ImportError): return RemoteError(name, failure.get('message'), trace) ex_type = type(failure) str_override = lambda self: message new_ex_type = type(ex_type.__name__ + "_Remote", (ex_type, ), { '__str__': str_override, '__unicode__': str_override }) try: # NOTE(ameade): Dynamically create a new exception type and swap it in # as the new type for the exception. This only works on user defined # Exceptions and not core python exceptions. This is important because # we cannot necessarily change an exception message so we must override # the __str__ method. failure.__class__ = new_ex_type except TypeError: # NOTE(ameade): If a core exception then just add the traceback to the # first exception argument. failure.args = (message, ) + failure.args[1:] return failure
def deserialize_remote_exception(conf, data): failure = jsonutils.loads(str(data)) trace = failure.get('tb', []) message = failure.get('message', "") + "\n" + "\n".join(trace) name = failure.get('class') module = failure.get('module') # NOTE(ameade): We DO NOT want to allow just any module to be imported, in # order to prevent arbitrary code execution. if module not in conf.allowed_rpc_exception_modules: return RemoteError(name, failure.get('message'), trace) try: mod = importutils.import_module(module) klass = getattr(mod, name) if not issubclass(klass, Exception): raise TypeError("Can only deserialize Exceptions") failure = klass(*failure.get('args', []), **failure.get('kwargs', {})) except (AttributeError, TypeError, ImportError): return RemoteError(name, failure.get('message'), trace) ex_type = type(failure) str_override = lambda self: message new_ex_type = type(ex_type.__name__ + _REMOTE_POSTFIX, (ex_type,), {'__str__': str_override, '__unicode__': str_override}) new_ex_type.__module__ = '%s%s' % (module, _REMOTE_POSTFIX) try: # NOTE(ameade): Dynamically create a new exception type and swap it in # as the new type for the exception. This only works on user defined # Exceptions and not core python exceptions. This is important because # we cannot necessarily change an exception message so we must override # the __str__ method. failure.__class__ = new_ex_type except TypeError: # NOTE(ameade): If a core exception then just add the traceback to the # first exception argument. failure.args = (message,) + failure.args[1:] return failure
def load_body(req, resp=None, validator=None): """Helper function for loading an HTTP request body from JSON. This body is placed into into a Python dictionary. :param req: The HTTP request instance to load the body from. :param resp: The HTTP response instance. :param validator: The JSON validator to enforce. :return: A dict of values from the JSON request. """ try: body = req.body_file.read(CONF.max_allowed_request_size_in_bytes) except IOError: LOG.exception("Problem reading request JSON stream.") pecan.abort(500, 'Read Error') try: # TODO(jwood): Investigate how to get UTF8 format via openstack # jsonutils: # parsed_body = json.loads(raw_json, 'utf-8') parsed_body = json.loads(body) strip_whitespace(parsed_body) except ValueError: LOG.exception("Problem loading request JSON.") pecan.abort(400, 'Malformed JSON') if validator: try: parsed_body = validator.validate(parsed_body) except exception.InvalidObject as e: LOG.exception("Failed to validate JSON information") pecan.abort(400, str(e)) except exception.UnsupportedField as e: LOG.exception("Provided field value is not supported") pecan.abort(400, str(e)) except exception.LimitExceeded as e: LOG.exception("Data limit exceeded") pecan.abort(413, str(e)) return parsed_body
def load_body(req, resp=None, validator=None): """Helper function for loading an HTTP request body from JSON. This body is placed into into a Python dictionary. :param req: The HTTP request instance to load the body from. :param resp: The HTTP response instance. :param validator: The JSON validator to enforce. :return: A dict of values from the JSON request. """ try: raw_json = req.stream.read(MAX_BYTES_REQUEST_INPUT_ACCEPTED) except IOError: LOG.exception("Problem reading request JSON stream.") abort(falcon.HTTP_500, 'Read Error', req, resp) try: #TODO(jwood): Investigate how to get UTF8 format via openstack # jsonutils: # parsed_body = json.loads(raw_json, 'utf-8') parsed_body = json.loads(raw_json) strip_whitespace(parsed_body) except ValueError: LOG.exception("Problem loading request JSON.") abort(falcon.HTTP_400, 'Malformed JSON', req, resp) if validator: try: parsed_body = validator.validate(parsed_body) except exception.InvalidObject as e: LOG.exception("Failed to validate JSON information") abort(falcon.HTTP_400, str(e), req, resp) except exception.UnsupportedField as e: LOG.exception("Provided field value is not supported") abort(falcon.HTTP_400, str(e), req, resp) except exception.LimitExceeded as e: LOG.exception("Data limit exceeded") abort(falcon.HTTP_413, str(e), req, resp) return parsed_body
def on_put(self, keystone_id, **kwargs): raw_body = pecan.request.body order_type = None if raw_body: order_type = json.loads(raw_body).get('type') if not order_type: _order_type_not_in_order() order_model = self.order_repo.get(entity_id=self.order_id, keystone_id=keystone_id, suppress_exception=True) if not order_model: _order_not_found() if order_model.type != order_type: order_cannot_modify_order_type() if models.OrderType.CERTIFICATE != order_model.type: _order_update_not_supported_for_type(order_type) if models.States.PENDING != order_model.status: _order_cannot_be_updated_if_not_pending(order_model.status) body = api.load_body(pecan.request, validator=self.type_order_validator) updated_meta = body.get('meta') if not updated_meta: _order_meta_not_in_update() # TODO(chellygel): Put updated_meta into a separate order association # entity. self.queue.update_order(order_id=self.order_id, keystone_id=keystone_id, updated_meta=updated_meta)
def on_post(self, keystone_id, **kwargs): tenant = res.get_or_create_tenant(keystone_id, self.tenant_repo) #Note(atiwari): trying to preserve backward compatibility #This will be removed as part of bug1335171 raw_body = pecan.request.body order_type = None if raw_body: order_type = json.loads(raw_body).get('type') if order_type: body = api.load_body(pecan.request, validator=self.type_order_validator) LOG.debug('Processing order type %s', order_type) new_order = models.Order() new_order.meta = body.get('meta') new_order.type = order_type #TODO(john-wood-w) These are required attributes currently, but # will eventually be removed once we drop the legacy orders # request. new_order.secret_name = 'N/A' new_order.secret_algorithm = 'N/A' new_order.secret_bit_length = 0 new_order.secret_mode = 'N/A' new_order.secret_payload_content_type = 'N/A' else: body = api.load_body(pecan.request, validator=self.validator) LOG.debug('Start on_post...%s', body) if 'secret' not in body: _secret_not_in_order() secret_info = body['secret'] name = secret_info.get('name') LOG.debug('Secret to create is %s', name) new_order = models.Order() new_order.secret_name = secret_info.get('name') new_order.secret_algorithm = secret_info.get('algorithm') new_order.secret_bit_length = secret_info.get('bit_length', 0) new_order.secret_mode = secret_info.get('mode') new_order.secret_payload_content_type = secret_info.get( 'payload_content_type') new_order.secret_expiration = secret_info.get('expiration') new_order.tenant_id = tenant.id self.order_repo.create_from(new_order) # Send to workers to process. #TODO(atiwari) - bug 1335171 if order_type: self.queue.process_type_order(order_id=new_order.id, keystone_id=keystone_id) else: self.queue.process_order(order_id=new_order.id, keystone_id=keystone_id) pecan.response.status = 202 pecan.response.headers['Location'] = '/{0}/orders/{1}'.format( keystone_id, new_order.id ) url = hrefs.convert_order_to_href(new_order.id) return {'order_ref': url}
def _deserialize(data): """ Deserialization wrapper """ LOG.debug(_("Deserializing: %s"), data) return jsonutils.loads(data)
def process_result_value(self, value, dialect): if value is not None: value = json.loads(value) return value
def test_response_should_include_total(self): self.resource.on_get(self.req, self.resp, self.keystone_id) resp_body = jsonutils.loads(self.resp.body) self.assertIn('total', resp_body) self.assertEqual(resp_body['total'], self.total)