From d77fac4d391513f69af2ee122c04d15a19e1efc7 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 30 Aug 2022 18:40:10 -0400 Subject: [PATCH] Allow context to be specified as JSON and not base64 JSON. Remove retrieve tests that utilize url parameters since it is a POST endpoint. --- .../web_development.rst | 6 ++--- .../control/specifications/fields/base.py | 17 +++++-------- .../control/specifications/porter_schema.py | 2 +- .../test_porter_web_control_blockchain.py | 24 +++++-------------- .../test_porter_web_control_federated.py | 24 +++++-------------- tests/unit/test_control.py | 10 ++++---- tests/utils/policy.py | 6 ++--- 7 files changed, 30 insertions(+), 59 deletions(-) diff --git a/docs/source/application_development/web_development.rst b/docs/source/application_development/web_development.rst index 80011cc6e..3c3454a98 100644 --- a/docs/source/application_development/web_development.rst +++ b/docs/source/application_development/web_development.rst @@ -674,9 +674,9 @@ Parameters +-------------------------------------------+---------------+----------------------------------------+ | ``bob_verifying_key`` | String | Bob's verifying key encoded as hex. | +-------------------------------------------+---------------+----------------------------------------+ -| ``context`` *(Optional)* | String | | Associated data required during | +| ``context`` *(Optional)* | String | | Associated JSON data required during | | | | | re-encryption e.g. data to satisfy | -| | | | conditions. | +| | | | re-encryption conditions. | +-------------------------------------------+---------------+----------------------------------------+ @@ -703,7 +703,7 @@ Parameters because some Ursulas may have experienced a blip in connectivity. This is an optional optimization that provides retry functionality that skips previously successful reencryption operations. - * A *context* is associated data required during re-encryption. One such example is when a condition for re-encryption + * A *context* is associated JSON data required during re-encryption. One such example is when a condition for re-encryption requires proof of ownership of a wallet address; the *context* is used to provide the data and signature required for proof. diff --git a/nucypher/control/specifications/fields/base.py b/nucypher/control/specifications/fields/base.py index e17b10f10..c26f4badf 100644 --- a/nucypher/control/specifications/fields/base.py +++ b/nucypher/control/specifications/fields/base.py @@ -87,25 +87,20 @@ class Base64BytesRepresentation(BaseField, fields.Field): raise InvalidInputData(f"Could not parse {self.name}: {e}") -class Base64JSON(Base64BytesRepresentation): - """Serializes/Deserializes JSON objects as base64 byte representation.""" - +class JSON(BaseField, fields.Field): + """Serializes/Deserializes objects to/from JSON strings.""" def _serialize(self, value, attr, obj, **kwargs): try: value_json = json.dumps(value) + return value_json except Exception as e: raise InvalidInputData( f"Provided object type, {type(value)}, is not JSON serializable: {e}" ) - else: - json_base64_bytes = super()._serialize( - value_json.encode(), attr, obj, **kwargs - ) - return json_base64_bytes def _deserialize(self, value, attr, data, **kwargs): - json_bytes = super()._deserialize(value, attr, data, **kwargs) try: - return json.loads(json_bytes) + result = json.loads(value) + return result except Exception as e: - raise InvalidInputData(f"Invalid JSON bytes: {e}") + raise InvalidInputData(f"Invalid JSON: {e}") diff --git a/nucypher/utilities/porter/control/specifications/porter_schema.py b/nucypher/utilities/porter/control/specifications/porter_schema.py index 6d28ebb6d..b9514d744 100644 --- a/nucypher/utilities/porter/control/specifications/porter_schema.py +++ b/nucypher/utilities/porter/control/specifications/porter_schema.py @@ -160,7 +160,7 @@ class BobRetrieveCFrags(BaseSchema): required=True)) # optional - context = base_fields.Base64JSON( + context = base_fields.JSON( required=False, load_only=True, click=click.option( diff --git a/tests/acceptance/porter/control/test_porter_web_control_blockchain.py b/tests/acceptance/porter/control/test_porter_web_control_blockchain.py index 1278575de..fec32b1eb 100644 --- a/tests/acceptance/porter/control/test_porter_web_control_blockchain.py +++ b/tests/acceptance/porter/control/test_porter_web_control_blockchain.py @@ -18,15 +18,17 @@ import json import os from base64 import b64encode -from urllib.parse import urlencode from nucypher_core import RetrievalKit from nucypher.characters.lawful import Enrico -from nucypher.control.specifications.fields import Base64JSON +from nucypher.control.specifications.fields import JSON from nucypher.crypto.powers import DecryptingPower from nucypher.policy.kits import PolicyMessageKit, RetrievalResult -from nucypher.utilities.porter.control.specifications.fields import RetrievalResultSchema, RetrievalKit as RetrievalKitField +from nucypher.utilities.porter.control.specifications.fields import ( + RetrievalResultSchema, + RetrievalKit as RetrievalKitField, +) from tests.utils.middleware import MockRestMiddleware from tests.utils.policy import retrieval_request_setup, retrieval_params_decode_from_rest @@ -173,7 +175,7 @@ def test_retrieve_cfrags(blockchain_porter, # # Use context # - context_field = Base64JSON() + context_field = JSON() multiple_retrieval_kits_params['context'] = context_field._serialize(random_context, attr=None, obj=None) response = blockchain_porter_web_controller.post('/retrieve_cfrags', data=json.dumps( @@ -185,20 +187,6 @@ def test_retrieve_cfrags(blockchain_porter, assert retrieval_results assert len(retrieval_results) == 2 - # - # Try same retrieval (with multiple retrieval kits) using query parameters - # - url_retrieve_params = dict(multiple_retrieval_kits_params) # use multiple kit params from above - # adjust parameter for url query parameter list format - url_retrieve_params['retrieval_kits'] = ",".join(url_retrieve_params['retrieval_kits']) # adjust for list - response = blockchain_porter_web_controller.post(f'/retrieve_cfrags' - f'?{urlencode(url_retrieve_params)}') - assert response.status_code == 200 - response_data = json.loads(response.data) - retrieval_results = response_data['result']['retrieval_results'] - assert retrieval_results - assert len(retrieval_results) == 2 - # # Failure # diff --git a/tests/integration/porter/control/test_porter_web_control_federated.py b/tests/integration/porter/control/test_porter_web_control_federated.py index 0d3422a08..92a1d89df 100644 --- a/tests/integration/porter/control/test_porter_web_control_federated.py +++ b/tests/integration/porter/control/test_porter_web_control_federated.py @@ -18,15 +18,17 @@ import json from base64 import b64encode -from urllib.parse import urlencode from nucypher_core import RetrievalKit from nucypher.characters.lawful import Enrico -from nucypher.control.specifications.fields import Base64JSON +from nucypher.control.specifications.fields import JSON from nucypher.crypto.powers import DecryptingPower from nucypher.policy.kits import PolicyMessageKit, RetrievalResult -from nucypher.utilities.porter.control.specifications.fields import RetrievalResultSchema, RetrievalKit as RetrievalKitField +from nucypher.utilities.porter.control.specifications.fields import ( + RetrievalResultSchema, + RetrievalKit as RetrievalKitField, +) from tests.utils.policy import retrieval_request_setup, retrieval_params_decode_from_rest @@ -174,7 +176,7 @@ def test_retrieve_cfrags(federated_porter, # # Use context # - context_field = Base64JSON() + context_field = JSON() multiple_retrieval_kits_params['context'] = context_field._serialize(random_context, attr=None, obj=None) response = federated_porter_web_controller.post('/retrieve_cfrags', data=json.dumps(multiple_retrieval_kits_params)) assert response.status_code == 200 @@ -184,20 +186,6 @@ def test_retrieve_cfrags(federated_porter, assert retrieval_results assert len(retrieval_results) == 4 - # - # Try same retrieval (with multiple retrieval kits) using query parameters - # - url_retrieve_params = dict(multiple_retrieval_kits_params) # use multiple kit params from above - # adjust parameter for url query parameter list format - url_retrieve_params['retrieval_kits'] = ",".join(url_retrieve_params['retrieval_kits']) - response = federated_porter_web_controller.post(f'/retrieve_cfrags' - f'?{urlencode(url_retrieve_params)}') - assert response.status_code == 200 - response_data = json.loads(response.data) - retrieval_results = response_data['result']['retrieval_results'] - assert retrieval_results - assert len(retrieval_results) == 4 - # # Failure # diff --git a/tests/unit/test_control.py b/tests/unit/test_control.py index e5e89f460..efec10a06 100644 --- a/tests/unit/test_control.py +++ b/tests/unit/test_control.py @@ -22,7 +22,7 @@ import pytest from nucypher.control.specifications.exceptions import InvalidInputData from nucypher.control.specifications.fields import ( Base64BytesRepresentation, - Base64JSON, + JSON, PositiveInteger, String, StringList, @@ -74,7 +74,7 @@ def test_base64_representation_field(): field._deserialize(value=b"raw bytes with non base64 chars ?&^%", attr=None, data=None) -def test_base64_json_field(): +def test_json_field(): # test data dict_data = { "domain": {"name": "tdec", "version": 1, "chainId": 1, "salt": "blahblahblah"}, @@ -94,10 +94,10 @@ def test_base64_json_field(): # test serialization/deserialization of data test_data = [dict_data, list_data, str_data, num_data, bool_data] - field = Base64JSON() + field = JSON() for d in test_data: serialized = field._serialize(value=d, attr=None, obj=None) - assert serialized == b64encode(json.dumps(d).encode()).decode() + assert serialized == json.dumps(d) deserialized = field._deserialize(value=serialized, attr=None, data=None) assert deserialized == d @@ -109,5 +109,5 @@ def test_base64_json_field(): with pytest.raises(InvalidInputData): # attempt to deserialize invalid data field._deserialize( - value=b"raw bytes with non base64 chars ?&^%", attr=None, data=None + value=b"raw bytes", attr=None, data=None ) diff --git a/tests/utils/policy.py b/tests/utils/policy.py index f51d974e6..bc0dff696 100644 --- a/tests/utils/policy.py +++ b/tests/utils/policy.py @@ -24,7 +24,7 @@ from nucypher_core import MessageKit, RetrievalKit from nucypher.characters.control.specifications.fields import Key, TreasureMap from nucypher.characters.lawful import Enrico -from nucypher.control.specifications.fields import Base64JSON +from nucypher.control.specifications.fields import JSON from nucypher.crypto.powers import DecryptingPower from nucypher.utilities.porter.control.specifications.fields import ( RetrievalKit as RetrievalKitField, @@ -76,7 +76,7 @@ def retrieval_request_setup(enacted_policy, ) # context is optional if context: - retrieval_params["context"] = encode_bytes(Base64JSON, context) + retrieval_params["context"] = encode_bytes(JSON, context) return retrieval_params, message_kit @@ -98,7 +98,7 @@ def retrieval_params_decode_from_rest(retrieval_params: Dict) -> Dict: # context is optional if "context" in retrieval_params: decoded_params["context"] = decode_bytes( - Base64JSON, retrieval_params["context"] + JSON, retrieval_params["context"] ) return decoded_params