Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,11 @@ def __post_request__(self, path, request) :
response.raise_for_status()
return response.json()

except (requests.HTTPError, requests.ConnectionError, requests.Timeout) as e :
logger.warn('network error connecting to service (%s); %s', path, str(e))
except requests.HTTPError as he :
logger.warning('HTTP error [%s]; %s, %s', path, he.response.status_code, he.response.text.strip())
raise MessageException(f'HTTP error [{he.response.status_code}]: {he.response.text.strip()}') from he
except (requests.ConnectionError, requests.Timeout) as e :
logger.warning('network error connecting to service (%s); %s', path, str(e))
raise MessageException(str(e)) from e

# -----------------------------------------------------------------
Expand All @@ -111,7 +114,10 @@ def __get_request__(self, path) :
response.raise_for_status()
return response.json()

except (requests.HTTPError, requests.ConnectionError, requests.Timeout) as e :
except requests.HTTPError as he :
logger.warn('HTTP error [%s]; %s, %s', path, he.response.status_code, he.response.text.strip())
raise MessageException(f'HTTP error [{he.response.status_code}]: {he.response.text.strip()}') from he
except (requests.ConnectionError, requests.Timeout) as e :
logger.warn('network error connecting to service (%s); %s', path, str(e))
raise MessageException(str(e)) from e

Expand Down
23 changes: 0 additions & 23 deletions common-contract/pdo/contracts/guardian/scripts/guardianCLI.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,29 +47,6 @@
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.web.wsgi import WSGIResource

## ----------------------------------------------------------------
def ErrorResponse(request, error_code, msg) :
"""Generate a common error response for broken requests
"""

result = ""
if request.method != 'HEAD' :
result = msg + '\n'
result = result.encode('utf8')

request.setResponseCode(error_code)
request.setHeader(b'Content-Type', b'text/plain')
request.setHeader(b'Content-Length', len(result))
request.write(result)

try :
request.finish()
except :
logger.exception("exception during request finish")
raise

return request

# -----------------------------------------------------------------
# -----------------------------------------------------------------
def __shutdown__(*args) :
Expand Down
62 changes: 45 additions & 17 deletions common-contract/pdo/contracts/guardian/wsgi/process_capability.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,29 @@ class ProcessCapabilityApp(object) :
"session_key_iv" : { "type" : "string" },
"encrypted_message" : { "type" : "string" },
},
"required" : [ "encrypted_session_key", "session_key_iv", "encrypted_message" ]
},
}
},
"required" : [ "minted_identity", "operation" ],
}

__operation_schema__ = {
"type" : "object",
"properties" : {
"nonce" : { "type" : "string" },
"request_identifier" : { "type" : "string" },
"method_name" : { "type" : "string" },
"parameters" : { "type" : "object" },
}
},
"required" : [ "nonce", "method_name", "parameters" ],
}

# -----------------------------------------------------------------
def __init__(self, config, capability_store, endpoint_registry) :
self.config = config
self.capability_store = capability_store
self.endpoint_registry = endpoint_registry
self.request_registry = {} # map of sets, used to check for duplicate requests

try :
operation_module_name = config['GuardianService']['Operations']
Expand All @@ -81,39 +86,62 @@ def __call__(self, environ, start_response) :
try :
request = UnpackJSONRequest(environ)
if not ValidateJSON(request, self.__input_schema__) :
return ErrorResponse(start_response, "invalid JSON")
return ErrorResponse(start_response, "invalid JSON, malformed request")

capability_key = self.capability_store.get_capability_key(request['minted_identity'])
minted_identity = request['minted_identity']
capability_key = self.capability_store.get_capability_key(minted_identity)

operation_message = recv_secret(capability_key, request['operation'])
if not ValidateJSON(operation_message, self.__operation_schema__) :
return ErrorResponse(start_response, "invalid JSON")
return ErrorResponse(start_response, "invalid JSON, malformed operation")

except KeyError as ke :
logger.error(f'missing field in request: {ke}')
logger.info(f'missing field in request: {ke}')
return ErrorResponse(start_response, f'missing field in request: {ke}')
except Exception as e :
logger.error(f'unknown exception unpacking request (ProcessCapability); {e}')
return ErrorResponse(start_response, "unknown exception while unpacking request")

# dispatch the operation
try :
method_name = operation_message['method_name']
parameters = operation_message['parameters']
except KeyError as ke :
logger.error(f'missing field {ke}')
return ErrorResponse(start_response, f'missing field {ke}')
# find the operation, we've already validated the JSON so no errors here
method_name = operation_message['method_name']
parameters = operation_message['parameters']

logger.info("process capability operation %s with parameters %s", method_name, parameters)

try :
operation = self.capability_handler_map[method_name]
except KeyError as ke :
logger.info(f'unknown operation {ke}')
return ErrorResponse(start_response, f'unknown operation {ke}', HTTPStatus.NOT_FOUND)

# check for request replays
try :
if hasattr(operation, 'unique_requests') and operation.unique_requests is True :
request_identifier = operation_message.get('request_identifier')
if request_identifier is None :
logger.info('missing request identifier for unique operation')
return ErrorResponse(start_response, "missing request identifier for unique operation")

# add the minted identity to the registry if it does not exist
if self.request_registry.get(minted_identity) is None :
self.request_registry[minted_identity] = set()

# check if the request identifier is already in the registry for this minted identity
if request_identifier in self.request_registry[minted_identity] :
logger.info('duplicate request for unique operation')
return ErrorResponse(start_response, 'duplicate request for unique operation', HTTPStatus.UNAUTHORIZED)

# add the request identifier to the registry for this minted identity
self.request_registry[minted_identity].add(request_identifier)
except Exception as e :
logger.error(f'unexpected error checking for duplicate request; {e}')
return ErrorResponse(start_response, "unexpected error checking for duplicate request")

# dispatch the operation
try :
operation_result = operation(parameters)
if operation_result is None :
return ErrorResponse(start_response, "operation failed")
except KeyError as ke :
logger.error(f'unknown operation {ke}')
return ErrorResponse(start_response, f'unknown operation {ke}')
return ErrorResponse(start_response, "operation failed", HTTPStatus.UNPROCESSABLE_ENTITY)
except Exception as e :
logger.error(f'unknown exception performing operation (ProcessCapability); {e}')
return ErrorResponse(start_response, "unknown exception while performing operation")
Expand Down
39 changes: 34 additions & 5 deletions exchange-contract/exchange/contracts/token_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,25 @@ bool ww::exchange::token_object::get_token_metadata(
ERROR_IF_NOT(deserialized_token_metadata.deserialize(serialized_token_metadata.c_str()),
"unexpected error: failed to deserialize token metadata");

ww::value::Object token_metadata_schema;
ERROR_IF_NOT(token_metadata_schema.deserialize(schema.c_str()),
"unexpected error: failed to deserialize token metadata schema");

ERROR_IF_NOT(deserialized_token_metadata.validate_schema(token_metadata_schema),
ERROR_IF_NOT(deserialized_token_metadata.validate_schema(schema.c_str()),
"unexpected error: token metadata does not match schema");

token_metadata.set(deserialized_token_metadata);
return true;
}

// -----------------------------------------------------------------
// get_token_identity
//
// Return the minted identity for this token object.
// -----------------------------------------------------------------
bool ww::exchange::token_object::get_token_identity(
std::string& token_identity)
{
ERROR_IF_NOT(token_object_store.get(minted_identity_key, token_identity),
"unexpected error: failed to get minted identity");
return true;
}

// -----------------------------------------------------------------
// METHOD: initialize_contract
Expand Down Expand Up @@ -463,6 +471,24 @@ bool ww::exchange::token_object::create_operation_package(
const std::string& method_name,
const ww::value::Object& parameters,
ww::value::Object& capability_result)
{
// No request identifier is specified so we'll generate a new one
ww::types::ByteArray identifier_raw;
if (! ww::crypto::random_identifier(identifier_raw))
return false;

std::string identifier;
if (! ww::crypto::b64_encode(identifier_raw, identifier))
return false;

return create_operation_package(identifier, method_name, parameters, capability_result);
}

bool ww::exchange::token_object::create_operation_package(
const std::string& request_identifier,
const std::string& method_name,
const ww::value::Object& parameters,
ww::value::Object& capability_result)
{
// Create the operation package, this will be the message in the
// secret that we are going to create for the capability
Expand All @@ -479,6 +505,9 @@ bool ww::exchange::token_object::create_operation_package(
if (! operation.set_string("nonce", nonce.c_str()))
return false;

if (! operation.set_string("request_identifier", request_identifier.c_str()))
return false;

if (! operation.set_string("method_name", method_name.c_str()))
return false;

Expand Down
11 changes: 11 additions & 0 deletions exchange-contract/exchange/token_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#define TO_OPERATION_SCHEMA \
"{" \
SCHEMA_KW(nonce,"") "," \
SCHEMA_KW(request_identifier,"") "," \
SCHEMA_KW(method_name, "") "," \
SCHEMA_KWS(parameters, "{}") \
"}"
Expand All @@ -71,6 +72,13 @@ namespace token_object

// utility functions
bool initialize_contract(const Environment& env);

bool create_operation_package(
const std::string& request_identifier,
const std::string& method_name,
const ww::value::Object& parameters,
ww::value::Object& capability_result);

bool create_operation_package(
const std::string& method_name,
const ww::value::Object& parameters,
Expand All @@ -80,6 +88,9 @@ namespace token_object
const std::string& schema,
ww::value::Object& token_metadata);

bool get_token_identity(
std::string& token_identity);

}; // token_object
}; // exchange
}; // ww