Removes RPCController and Web controller functionality

pull/2987/head
Kieran Prasch 2022-10-25 17:33:01 +01:00
parent 98434daf17
commit 2c4634acea
8 changed files with 4 additions and 486 deletions

View File

@ -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

View File

@ -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,

View File

@ -14,13 +14,10 @@
You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
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)

View File

@ -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):

View File

@ -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):

View File

@ -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()

View File

@ -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 <https://www.gnu.org/licenses/>.
"""
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

View File

@ -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()