diff --git a/nucypher/control/controllers.py b/nucypher/control/controllers.py index f50c06751..e6b80e2f0 100644 --- a/nucypher/control/controllers.py +++ b/nucypher/control/controllers.py @@ -27,16 +27,15 @@ import maya from flask import Flask, Response from hendrix.deploy.base import HendrixDeploy from hendrix.deploy.tls import HendrixDeployTLS -from twisted.internet import reactor, stdio +from twisted.internet import reactor -from nucypher.cli.processes import JSONRPCLineReceiver from nucypher.config.constants import MAX_UPLOAD_CONTENT_LENGTH from nucypher.control.emitters import StdoutEmitter, JSONRPCStdoutEmitter, WebEmitter from nucypher.control.interfaces import ControlInterface from nucypher.control.specifications.exceptions import SpecificationError from nucypher.exceptions import DevelopmentInstallationRequired from nucypher.network.resources import get_static_resources -from nucypher.utilities.concurrency import WorkerPool, WorkerPoolException +from nucypher.utilities.concurrency import WorkerPoolException from nucypher.utilities.logging import Logger, GlobalLoggerSettings @@ -157,10 +156,6 @@ class JSONRPCController(InterfaceControlServer): test_client = JSONRPCTestClient(rpc_controller=self) return test_client - def make_control_transport(self): - transport = stdio.StandardIO(JSONRPCLineReceiver(rpc_controller=self)) - return transport - def handle_procedure_call(self, control_request) -> int: # Validate request and read request metadata diff --git a/nucypher/utilities/porter/porter.py b/nucypher/utilities/porter/porter.py index 9087271b5..06f0322dc 100644 --- a/nucypher/utilities/porter/porter.py +++ b/nucypher/utilities/porter/porter.py @@ -33,7 +33,7 @@ from nucypher.blockchain.eth.registry import ( InMemoryContractRegistry, ) from nucypher.characters.lawful import Ursula -from nucypher.control.controllers import JSONRPCController, WebController +from nucypher.control.controllers import WebController from nucypher.crypto.powers import DecryptingPower from nucypher.network.nodes import Learner from nucypher.network.retrieval import RetrievalClient @@ -219,14 +219,6 @@ the Pipe for PRE Application network operations self.controller = controller return controller - def make_rpc_controller(self, crash_on_error: bool = False): - controller = JSONRPCController(app_name=self.APP_NAME, - crash_on_error=crash_on_error, - interface=self.interface) - - self.controller = controller - return controller - def make_web_controller(self, crash_on_error: bool = False, htpasswd_filepath: Path = None, diff --git a/tests/acceptance/characters/control/test_rpc_control_blockchain.py b/tests/acceptance/characters/control/test_rpc_control_blockchain.py index 7f430c0d2..9f39c8472 100644 --- a/tests/acceptance/characters/control/test_rpc_control_blockchain.py +++ b/tests/acceptance/characters/control/test_rpc_control_blockchain.py @@ -14,13 +14,10 @@ You should have received a copy of the GNU Affero General Public License along with nucypher. If not, see . """ -from base64 import b64encode -import pytest -from nucypher.characters.control.interfaces import AliceInterface from nucypher.characters.control.interfaces import EnricoInterface -from tests.utils.controllers import get_fields, validate_json_rpc_response_data +from tests.utils.controllers import validate_json_rpc_response_data def test_enrico_rpc_character_control_encrypt_message(enrico_rpc_controller_test_client, encrypt_control_request): @@ -30,94 +27,3 @@ def test_enrico_rpc_character_control_encrypt_message(enrico_rpc_controller_test assert validate_json_rpc_response_data(response=response, method_name=method_name, interface=EnricoInterface) - - -def test_bob_rpc_character_control_retrieve_with_tmap(enacted_blockchain_policy, - blockchain_bob, - bob_rpc_controller, - retrieve_control_request): - # So that this test can run even independently. - if not blockchain_bob.done_seeding: - blockchain_bob.learn_from_teacher_node() - - tmap_64 = b64encode(bytes(enacted_blockchain_policy.treasure_map)).decode() - method_name, params = retrieve_control_request - params['encrypted_treasure_map'] = tmap_64 - request_data = {'method': method_name, 'params': params} - response = bob_rpc_controller.send(request_data) - assert response.data['result']['cleartexts'][0] == 'Welcome to flippering number 1.' - - # Make a wrong treasure map - enc_wrong_tmap = bytes(enacted_blockchain_policy.treasure_map)[1:-1] - - tmap_bytes = bytes(enc_wrong_tmap) - tmap_64 = b64encode(tmap_bytes).decode() - request_data['params']['encrypted_treasure_map'] = tmap_64 - with pytest.raises(ValueError): - bob_rpc_controller.send(request_data) - - -def test_alice_rpc_character_control_create_policy(alice_rpc_test_client, create_policy_control_request): - alice_rpc_test_client.__class__.MESSAGE_ID = 0 - method_name, params = create_policy_control_request - request_data = {'method': method_name, 'params': params} - rpc_response = alice_rpc_test_client.send(request=request_data) - assert rpc_response.success is True - assert rpc_response.id == 1 - - _input_fields, _optional_fields, required_output_fileds = get_fields(AliceInterface, method_name) - - assert 'jsonrpc' in rpc_response.data - for output_field in required_output_fileds: - assert output_field in rpc_response.content - - try: - bytes.fromhex(rpc_response.content['policy_encrypting_key']) - except (KeyError, ValueError): - pytest.fail("Invalid Policy Encrypting Key") - - # Confirm the same message send works again, with a unique ID - request_data = {'method': method_name, 'params': params} - rpc_response = alice_rpc_test_client.send(request=request_data) - assert rpc_response.success is True - assert rpc_response.id == 2 - - # Send a bulk create policy request - bulk_request = list() - for i in range(50): - request_data = {'method': method_name, 'params': params} - bulk_request.append(request_data) - - rpc_responses = alice_rpc_test_client.send(request=bulk_request) - for response_id, rpc_response in enumerate(rpc_responses, start=3): - assert rpc_response.success is True - assert rpc_response.id == response_id - - -def test_alice_rpc_character_control_bad_input(alice_rpc_test_client, create_policy_control_request): - alice_rpc_test_client.__class__.MESSAGE_ID = 0 - - # Send bad data to assert error returns (Request #3) - alice_rpc_test_client.crash_on_error = False - - response = alice_rpc_test_client.send(request={'bogus': 'input'}, malformed=True) - assert response.error_code == -32600 - - -def test_alice_rpc_character_control_derive_policy_encrypting_key(alice_rpc_test_client): - method_name = 'derive_policy_encrypting_key' - request_data = {'method': method_name, 'params': {'label': 'test'}} - response = alice_rpc_test_client.send(request_data) - assert response.success is True - assert validate_json_rpc_response_data(response=response, - method_name=method_name, - interface=AliceInterface) - - -def test_alice_rpc_character_control_grant(alice_rpc_test_client, grant_control_request): - method_name, params = grant_control_request - request_data = {'method': method_name, 'params': params} - response = alice_rpc_test_client.send(request_data) - assert validate_json_rpc_response_data(response=response, - method_name=method_name, - interface=AliceInterface) diff --git a/tests/acceptance/cli/ursula/test_federated_ursula.py b/tests/acceptance/cli/ursula/test_federated_ursula.py index 455c32924..f4f60f634 100644 --- a/tests/acceptance/cli/ursula/test_federated_ursula.py +++ b/tests/acceptance/cli/ursula/test_federated_ursula.py @@ -152,7 +152,6 @@ def test_run_federated_ursula_from_config_file(custom_filepath: Path, click_runn # Run Ursula run_args = ('ursula', 'run', '--dry-run', - '--interactive', '--lonely', '--config-file', str(custom_config_filepath.absolute())) @@ -164,7 +163,6 @@ def test_run_federated_ursula_from_config_file(custom_filepath: Path, click_runn assert result.exit_code == 0, result.output assert 'Federated' in result.output, 'WARNING: Federated ursula is not running in federated mode' assert 'Running' in result.output - assert "'help' or '?'" in result.output def test_ursula_save_metadata(click_runner, custom_filepath): diff --git a/tests/acceptance/cli/ursula/test_run_ursula.py b/tests/acceptance/cli/ursula/test_run_ursula.py index b9fc7174b..9fa556db2 100644 --- a/tests/acceptance/cli/ursula/test_run_ursula.py +++ b/tests/acceptance/cli/ursula/test_run_ursula.py @@ -30,7 +30,6 @@ from nucypher.config.constants import ( NUCYPHER_ENVVAR_KEYSTORE_PASSWORD, TEMPORARY_DOMAIN, ) -from nucypher.network.nodes import Teacher from nucypher.utilities.networking import LOOPBACK_ADDRESS, UnknownIPAddress from tests.constants import ( FAKE_PASSWORD_CONFIRMED, @@ -204,7 +203,6 @@ def test_persistent_node_storage_integration(click_runner, run_args = ('ursula', 'run', '--dry-run', '--debug', - '--interactive', '--config-file', str(another_ursula_configuration_file_location.absolute()), '--teacher', teacher_uri) @@ -222,7 +220,6 @@ def test_persistent_node_storage_integration(click_runner, run_args = ('ursula', 'run', '--dry-run', '--debug', - '--interactive', '--config-file', str(another_ursula_configuration_file_location.absolute())) with pytest.raises(Operator.ActorError): diff --git a/tests/acceptance/porter/control/conftest.py b/tests/acceptance/porter/control/conftest.py index 4df5ebbfb..2fb18e9d8 100644 --- a/tests/acceptance/porter/control/conftest.py +++ b/tests/acceptance/porter/control/conftest.py @@ -18,9 +18,6 @@ import pytest -# -# Web -# @pytest.fixture(scope='module') def blockchain_porter_web_controller(blockchain_porter): web_controller = blockchain_porter.make_web_controller(crash_on_error=False) @@ -31,12 +28,3 @@ def blockchain_porter_web_controller(blockchain_porter): def blockchain_porter_basic_auth_web_controller(blockchain_porter, basic_auth_file): web_controller = blockchain_porter.make_web_controller(crash_on_error=False, htpasswd_filepath=basic_auth_file) yield web_controller.test_client() - - -# -# RPC -# -@pytest.fixture(scope='module') -def blockchain_porter_rpc_controller(blockchain_porter): - rpc_controller = blockchain_porter.make_rpc_controller(crash_on_error=True) - yield rpc_controller.test_client() diff --git a/tests/integration/characters/control/test_web_control_federated.py b/tests/integration/characters/control/test_web_control_federated.py deleted file mode 100644 index d494e25a9..000000000 --- a/tests/integration/characters/control/test_web_control_federated.py +++ /dev/null @@ -1,346 +0,0 @@ -""" - This file is part of nucypher. - - nucypher is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - nucypher is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with nucypher. If not, see . -""" - - -import datetime -import json -from base64 import b64decode, b64encode - -import maya -import pytest -from click.testing import CliRunner - -from nucypher_core import MessageKit, EncryptedTreasureMap - -import nucypher -from nucypher.crypto.powers import DecryptingPower - -click_runner = CliRunner() - - -def test_label_whose_b64_representation_is_invalid_utf8(alice_web_controller_test_client, create_policy_control_request): - # In our Discord, user robin#2324 (github username @robin-thomas) reported certain labels - # break Bob's retrieve endpoint. - # convo starts here: https://ptb.discordapp.com/channels/411401661714792449/411401661714792451/564353305887637517 - - bad_label = '516d593559505355376d454b61374751577146467a47754658396d516a685674716b7663744b376b4b666a35336d' - - method_name, params = create_policy_control_request - params['label'] = bad_label - - # This previously caused an unhandled UnicodeDecodeError. #920 - response = alice_web_controller_test_client.put(f'/{method_name}', data=json.dumps(params)) - assert response.status_code == 200 - - -def test_alice_web_character_control_create_policy(alice_web_controller_test_client, create_policy_control_request): - method_name, params = create_policy_control_request - - response = alice_web_controller_test_client.put(f'/{method_name}', data=json.dumps(params)) - assert response.status_code == 200 - - create_policy_response = json.loads(response.data) - assert 'version' in create_policy_response - assert 'label' in create_policy_response['result'] - - try: - bytes.fromhex(create_policy_response['result']['policy_encrypting_key']) - except (KeyError, ValueError): - pytest.fail("Invalid Policy Encrypting Key") - - # Send bad data to assert error returns - response = alice_web_controller_test_client.put('/create_policy', data=json.dumps({'bad': 'input'})) - assert response.status_code == 400 - - -def test_alice_web_character_control_derive_policy_encrypting_key(alice_web_controller_test_client): - label = 'test' - response = alice_web_controller_test_client.post(f'/derive_policy_encrypting_key/{label}') - assert response.status_code == 200 - - response_data = json.loads(response.data) - assert 'policy_encrypting_key' in response_data['result'] - - -def test_alice_web_character_control_grant(alice_web_controller_test_client, grant_control_request): - method_name, params = grant_control_request - endpoint = f'/{method_name}' - - response = alice_web_controller_test_client.put(endpoint, data=json.dumps(params)) - assert response.status_code == 200 - - response_data = json.loads(response.data) - assert 'treasure_map' in response_data['result'] - assert 'policy_encrypting_key' in response_data['result'] - assert 'alice_verifying_key' in response_data['result'] - - map_bytes = b64decode(response_data['result']['treasure_map']) - encrypted_map = EncryptedTreasureMap.from_bytes(map_bytes) - - # Send bad data to assert error returns - response = alice_web_controller_test_client.put(endpoint, data=json.dumps({'bad': 'input'})) - assert response.status_code == 400 - - # Malform the request - bad_params = dict(params) - del(bad_params['bob_encrypting_key']) - response = alice_web_controller_test_client.put(endpoint, data=json.dumps(bad_params)) - assert response.status_code == 400 - - # test key validation with a bad key - bad_params = dict(params) - bad_params['bob_encrypting_key'] = '12345' - response = alice_web_controller_test_client.put(endpoint, data=json.dumps(bad_params)) - assert response.status_code == 400 - assert b'non-hexadecimal number found in fromhex' in response.data - - -def test_alice_character_control_revoke(alice_web_controller_test_client, federated_bob): - bob_pubkey_enc = federated_bob.public_keys(DecryptingPower) - - grant_request_data = { - 'bob_encrypting_key': bytes(bob_pubkey_enc).hex(), - 'bob_verifying_key': bytes(federated_bob.stamp).hex(), - 'label': 'test-revoke', - 'threshold': 2, - 'shares': 3, - 'expiration': (maya.now() + datetime.timedelta(days=3)).iso8601(), - } - response = alice_web_controller_test_client.put('/grant', data=json.dumps(grant_request_data)) - assert response.status_code == 200 - - revoke_request_data = { - 'label': 'test', - 'bob_verifying_key': bytes(federated_bob.stamp).hex() - } - - response = alice_web_controller_test_client.delete(f'/revoke', data=json.dumps(revoke_request_data)) - assert response.status_code == 200 - - response_data = json.loads(response.data) - assert 'result' in response_data - assert 'failed_revocations' in response_data['result'] - assert response_data['result']['failed_revocations'] == 0 - - -def test_alice_character_control_decrypt(alice_web_controller_test_client, - enacted_federated_policy, - capsule_side_channel): - - message_kit = capsule_side_channel() - - label = enacted_federated_policy.label.decode() - policy_encrypting_key = bytes(enacted_federated_policy.public_key).hex() - message_kit = b64encode(bytes(message_kit)).decode() - - request_data = { - 'label': label, - 'message_kit': message_kit, - } - - response = alice_web_controller_test_client.post('/decrypt', data=json.dumps(request_data)) - assert response.status_code == 200 - - response_data = json.loads(response.data) - assert 'cleartexts' in response_data['result'] - - response_message = response_data['result']['cleartexts'][0] - assert response_message == 'Welcome to flippering number 1.' # This is the first message - in a test below, we'll show retrieving a second one. - - # Send bad data to assert error returns - response = alice_web_controller_test_client.post('/decrypt', data=json.dumps({'bad': 'input'})) - assert response.status_code == 400 - - del(request_data['message_kit']) - response = alice_web_controller_test_client.put('/decrypt', data=json.dumps(request_data)) - assert response.status_code == 405 - - -def test_bob_web_character_control_retrieve(bob_web_controller_test_client, retrieve_control_request): - method_name, params = retrieve_control_request - endpoint = f'/{method_name}' - - response = bob_web_controller_test_client.post(endpoint, data=json.dumps(params)) - assert response.status_code == 200 - - response_data = json.loads(response.data) - assert 'cleartexts' in response_data['result'] - - response_message = response_data['result']['cleartexts'][0] - assert response_message == 'Welcome to flippering number 2.' # This is the second message - the first is in the test above. - - # Send bad data to assert error returns - response = bob_web_controller_test_client.post(endpoint, data=json.dumps({'bad': 'input'})) - assert response.status_code == 400 - - -def test_bob_web_character_control_retrieve_again(bob_web_controller_test_client, retrieve_control_request): - method_name, params = retrieve_control_request - endpoint = f'/{method_name}' - - response = bob_web_controller_test_client.post(endpoint, data=json.dumps(params)) - assert response.status_code == 200 - - response_data = json.loads(response.data) - assert 'cleartexts' in response_data['result'] - - response_message = response_data['result']['cleartexts'][0] - assert response_message == 'Welcome to flippering number 2.' # We have received exactly the same message again. - - bad_params = dict(params) - del(bad_params['alice_verifying_key']) - response = bob_web_controller_test_client.post(endpoint, data=json.dumps(bad_params)) - assert response.status_code == 400 - - -def test_bob_web_character_control_retrieve_multiple_kits(bob_web_controller_test_client, - retrieve_control_request, - capsule_side_channel): - method_name, params = retrieve_control_request - multiple_kits_params = dict(params) - - message_kits = [] - # capsule_side_channel has module scope so resetting - weird: resetting produces a message kit...ok(?) - reset_message_kit, _ = capsule_side_channel.reset(plaintext_passthrough=True) - message_kits.append(b64encode(bytes(reset_message_kit)).decode()) # add initial message kit - # add some more - for index in range(1, 5): - message_kit = capsule_side_channel() - message_kits.append(b64encode(bytes(message_kit)).decode()) - - endpoint = f'/{method_name}' - multiple_kits_params['message_kits'] = message_kits # replace message kits entry - response = bob_web_controller_test_client.post(endpoint, data=json.dumps(multiple_kits_params)) - assert response.status_code == 200 - - response_data = json.loads(response.data) - assert 'cleartexts' in response_data['result'] - - cleartexts = response_data['result']['cleartexts'] - assert len(cleartexts) == len(message_kits) - for index, cleartext in enumerate(cleartexts): - assert cleartext.encode() == capsule_side_channel.plaintexts[index] - - -def test_enrico_web_character_control_encrypt_message(enrico_web_controller_test_client, encrypt_control_request): - method_name, params = encrypt_control_request - endpoint = f'/{method_name}' - response = enrico_web_controller_test_client.post(endpoint, data=json.dumps(params)) - assert response.status_code == 200 - - response_data = json.loads(response.data) - assert 'message_kit' in response_data['result'] - - # Check that it serializes correctly. - MessageKit.from_bytes(b64decode(response_data['result']['message_kit'])) - - # Send bad data to assert error return - response = enrico_web_controller_test_client.post('/encrypt_message', data=json.dumps({'bad': 'input'})) - assert response.status_code == 400 - - bad_params = dict(params) - del(bad_params['message']) - response = enrico_web_controller_test_client.post('/encrypt_message', data=bad_params) - assert response.status_code == 400 - - -def test_web_character_control_lifecycle(alice_web_controller_test_client, - bob_web_controller_test_client, - enrico_web_controller_from_alice, - federated_alice, - federated_bob, - federated_ursulas, - random_policy_label): - - random_label = random_policy_label.decode() # Unicode string - - bob_keys_response = bob_web_controller_test_client.get('/public_keys') - assert bob_keys_response.status_code == 200 - - response_data = json.loads(bob_keys_response.data) - assert str(nucypher.__version__) == response_data['version'] - bob_keys = response_data['result'] - assert 'bob_encrypting_key' in bob_keys - assert 'bob_verifying_key' in bob_keys - - bob_encrypting_key_hex = bob_keys['bob_encrypting_key'] - bob_verifying_key_hex = bob_keys['bob_verifying_key'] - - # Create a policy via Alice control - alice_request_data = { - 'bob_encrypting_key': bob_encrypting_key_hex, - 'bob_verifying_key': bob_verifying_key_hex, - 'threshold': 1, - 'shares': 1, - 'label': random_label, - 'expiration': (maya.now() + datetime.timedelta(days=3)).iso8601(), # TODO - } - - response = alice_web_controller_test_client.put('/grant', data=json.dumps(alice_request_data)) - assert response.status_code == 200 - - # Check Response Keys - alice_response_data = json.loads(response.data) - assert 'treasure_map' in alice_response_data['result'] - assert 'policy_encrypting_key' in alice_response_data['result'] - assert 'alice_verifying_key' in alice_response_data['result'] - assert 'version' in alice_response_data - assert str(nucypher.__version__) == alice_response_data['version'] - - # This is sidechannel policy metadata. It should be given to Bob by the - # application developer at some point. - alice_verifying_key_hex = alice_response_data['result']['alice_verifying_key'] - - # Encrypt some data via Enrico control - # Alice will also be Enrico via Enrico.from_alice - # (see enrico_control_from_alice fixture) - - plaintext = "I'm bereaved, not a sap!" # type: str - enrico_request_data = { - 'message': b64encode(bytes(plaintext, encoding='utf-8')).decode(), - } - - response = enrico_web_controller_from_alice.post('/encrypt_message', data=json.dumps(enrico_request_data)) - assert response.status_code == 200 - - enrico_response_data = json.loads(response.data) - assert 'message_kit' in enrico_response_data['result'] - - kit_bytes = b64decode(enrico_response_data['result']['message_kit'].encode()) - bob_message_kit = MessageKit.from_bytes(kit_bytes) - - # Retrieve data via Bob control - encoded_message_kit = b64encode(bytes(bob_message_kit)).decode() - - bob_request_data = { - 'alice_verifying_key': alice_verifying_key_hex, - 'message_kits': [encoded_message_kit], - 'encrypted_treasure_map': alice_response_data['result']['treasure_map'] - } - - # Give bob a node to remember - teacher = list(federated_ursulas)[1] - federated_bob.remember_node(teacher) - - response = bob_web_controller_test_client.post('/retrieve_and_decrypt', data=json.dumps(bob_request_data)) - assert response.status_code == 200 - - bob_response_data = json.loads(response.data) - assert 'cleartexts' in bob_response_data['result'] - - for cleartext in bob_response_data['result']['cleartexts']: - assert b64decode(cleartext.encode()).decode() == plaintext diff --git a/tests/integration/porter/control/conftest.py b/tests/integration/porter/control/conftest.py index b7fef04b7..ff530e5ba 100644 --- a/tests/integration/porter/control/conftest.py +++ b/tests/integration/porter/control/conftest.py @@ -17,9 +17,6 @@ import pytest -# -# Web -# @pytest.fixture(scope='module') def federated_porter_web_controller(federated_porter): web_controller = federated_porter.make_web_controller(crash_on_error=False) @@ -30,12 +27,3 @@ def federated_porter_web_controller(federated_porter): def federated_porter_basic_auth_web_controller(federated_porter, basic_auth_file): web_controller = federated_porter.make_web_controller(crash_on_error=False, htpasswd_filepath=basic_auth_file) yield web_controller.test_client() - - -# -# RPC -# -@pytest.fixture(scope='module') -def federated_porter_rpc_controller(federated_porter): - rpc_controller = federated_porter.make_rpc_controller(crash_on_error=True) - yield rpc_controller.test_client()