2018-11-04 19:23:11 +00:00
|
|
|
"""
|
|
|
|
This file is part of nucypher.
|
|
|
|
|
|
|
|
nucypher is free software: you can redistribute it and/or modify
|
2019-03-05 02:50:11 +00:00
|
|
|
it under the terms of the GNU Affero General Public License as published by
|
2018-11-04 19:23:11 +00:00
|
|
|
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
|
2019-03-05 02:50:11 +00:00
|
|
|
GNU Affero General Public License for more details.
|
2018-11-04 19:23:11 +00:00
|
|
|
|
2019-03-05 02:50:11 +00:00
|
|
|
You should have received a copy of the GNU Affero General Public License
|
2018-11-04 19:23:11 +00:00
|
|
|
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
"""
|
2020-05-13 02:29:29 +00:00
|
|
|
import time
|
2020-01-12 21:57:09 +00:00
|
|
|
|
2019-02-15 01:32:11 +00:00
|
|
|
import requests
|
2019-02-14 19:15:04 +00:00
|
|
|
import socket
|
2018-11-29 22:26:54 +00:00
|
|
|
from bytestring_splitter import VariableLengthBytestring
|
2019-02-06 23:33:38 +00:00
|
|
|
from constant_sorrow.constants import CERTIFICATE_NOT_SAVED
|
2019-11-10 10:57:40 +00:00
|
|
|
from flask import Response
|
2018-09-13 19:26:25 +00:00
|
|
|
|
2020-05-13 03:23:33 +00:00
|
|
|
from nucypher.characters.lawful import Ursula
|
|
|
|
from nucypher.network.middleware import NucypherMiddlewareClient, RestMiddleware
|
2020-05-13 18:24:36 +00:00
|
|
|
from tests.utils.ursula import MOCK_KNOWN_URSULAS_CACHE
|
2020-05-13 03:23:33 +00:00
|
|
|
|
2019-02-05 04:21:50 +00:00
|
|
|
|
2019-11-10 10:57:40 +00:00
|
|
|
class _TestMiddlewareClient(NucypherMiddlewareClient):
|
2019-02-15 15:48:27 +00:00
|
|
|
timeout = None
|
|
|
|
|
2019-02-05 04:21:50 +00:00
|
|
|
@staticmethod
|
|
|
|
def response_cleaner(response):
|
|
|
|
response.content = response.data
|
|
|
|
return response
|
2019-02-05 03:32:18 +00:00
|
|
|
|
2018-09-13 19:26:25 +00:00
|
|
|
def _get_mock_client_by_ursula(self, ursula):
|
2019-06-17 08:41:10 +00:00
|
|
|
port = ursula.rest_interface.port
|
2018-09-13 19:26:25 +00:00
|
|
|
return self._get_mock_client_by_port(port)
|
|
|
|
|
|
|
|
def _get_mock_client_by_url(self, url):
|
|
|
|
port = int(url.split(":")[1])
|
|
|
|
return self._get_mock_client_by_port(port)
|
|
|
|
|
2019-02-05 23:53:48 +00:00
|
|
|
def _get_mock_client_by_port(self, port):
|
|
|
|
ursula = self._get_ursula_by_port(port)
|
|
|
|
rest_app = ursula.rest_app
|
|
|
|
rest_app.testing = True
|
|
|
|
mock_client = rest_app.test_client()
|
|
|
|
return mock_client
|
|
|
|
|
2018-10-23 16:49:43 +00:00
|
|
|
def _get_ursula_by_port(self, port):
|
2018-09-13 19:26:25 +00:00
|
|
|
try:
|
2018-11-22 18:21:28 +00:00
|
|
|
return MOCK_KNOWN_URSULAS_CACHE[port]
|
2018-09-13 19:26:25 +00:00
|
|
|
except KeyError:
|
|
|
|
raise RuntimeError(
|
|
|
|
"Can't find an Ursula with port {} - did you spin up the right test ursulas?".format(port))
|
2018-10-23 16:49:43 +00:00
|
|
|
|
2019-02-05 04:38:58 +00:00
|
|
|
def parse_node_or_host_and_port(self, node, host, port):
|
2019-02-05 04:21:17 +00:00
|
|
|
if node:
|
|
|
|
if any((host, port)):
|
|
|
|
raise ValueError("Don't pass host and port if you are passing the node.")
|
2019-02-05 23:54:13 +00:00
|
|
|
mock_client = self._get_mock_client_by_ursula(node)
|
2019-02-05 04:21:17 +00:00
|
|
|
elif all((host, port)):
|
|
|
|
node = self._get_ursula_by_port(port)
|
2019-02-05 23:54:13 +00:00
|
|
|
mock_client = self._get_mock_client_by_port(port)
|
2019-02-05 04:21:17 +00:00
|
|
|
else:
|
|
|
|
raise ValueError("You need to pass either the node or a host and port.")
|
2019-02-05 23:54:13 +00:00
|
|
|
|
2019-02-06 23:33:38 +00:00
|
|
|
# We don't use certs in mock-style tests anyway.
|
|
|
|
return node.rest_url(), CERTIFICATE_NOT_SAVED, mock_client
|
2019-02-05 04:21:17 +00:00
|
|
|
|
|
|
|
def invoke_method(self, method, url, *args, **kwargs):
|
2019-02-06 05:28:37 +00:00
|
|
|
_cert_location = kwargs.pop("verify") # TODO: Is this something that can be meaningfully tested?
|
2019-02-05 04:21:17 +00:00
|
|
|
kwargs.pop("timeout", None) # Just get rid of timeout; not needed for the test client.
|
2019-02-06 05:28:37 +00:00
|
|
|
response = super().invoke_method(method, url, *args, **kwargs)
|
2019-02-05 04:21:17 +00:00
|
|
|
return response
|
|
|
|
|
2019-02-05 23:54:26 +00:00
|
|
|
def clean_params(self, request_kwargs):
|
|
|
|
request_kwargs["query_string"] = request_kwargs.pop("params", {})
|
|
|
|
|
2019-02-05 03:46:06 +00:00
|
|
|
|
|
|
|
class MockRestMiddleware(RestMiddleware):
|
|
|
|
_ursulas = None
|
|
|
|
|
2019-12-13 02:23:50 +00:00
|
|
|
_client_class = _TestMiddlewareClient
|
2019-02-05 03:46:06 +00:00
|
|
|
|
|
|
|
class NotEnoughMockUrsulas(Ursula.NotEnoughUrsulas):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def get_certificate(self, host, port, timeout=3, retry_attempts: int = 3, retry_rate: int = 2,
|
|
|
|
current_attempt: int = 0):
|
2019-02-06 04:25:29 +00:00
|
|
|
ursula = self.client._get_ursula_by_port(port)
|
2018-11-03 21:54:48 +00:00
|
|
|
return ursula.certificate
|
2018-09-13 19:26:25 +00:00
|
|
|
|
|
|
|
|
2019-11-10 10:57:40 +00:00
|
|
|
class MockRestMiddlewareForLargeFleetTests(MockRestMiddleware):
|
|
|
|
"""
|
|
|
|
A MockRestMiddleware with workaround necessary to test the conditions that arise with thousands of nodes.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def get_nodes_via_rest(self,
|
|
|
|
node,
|
|
|
|
announce_nodes=None,
|
|
|
|
nodes_i_need=None,
|
|
|
|
fleet_checksum=None):
|
|
|
|
known_nodes_bytestring = node.bytestring_of_known_nodes()
|
|
|
|
signature = node.stamp(known_nodes_bytestring)
|
|
|
|
r = Response(bytes(signature) + known_nodes_bytestring)
|
|
|
|
r.content = r.data
|
|
|
|
return r
|
|
|
|
|
|
|
|
|
2020-05-13 02:29:29 +00:00
|
|
|
class SluggishLargeFleetMiddleware(MockRestMiddlewareForLargeFleetTests):
|
|
|
|
"""
|
|
|
|
Similar to above, but with added delay to simulate network latency.
|
|
|
|
"""
|
|
|
|
def put_treasure_map_on_node(self, *args, **kwargs):
|
|
|
|
time.sleep(.1)
|
|
|
|
return super().put_treasure_map_on_node(*args, **kwargs)
|
|
|
|
|
|
|
|
|
2019-02-15 01:32:11 +00:00
|
|
|
class _MiddlewareClientWithConnectionProblems(_TestMiddlewareClient):
|
|
|
|
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.ports_that_are_down = set()
|
|
|
|
self.certs_are_broken = False
|
|
|
|
|
|
|
|
def _get_ursula_by_port(self, port):
|
|
|
|
if port in self.ports_that_are_down:
|
|
|
|
raise ConnectionRefusedError
|
|
|
|
else:
|
|
|
|
return super()._get_ursula_by_port(port)
|
|
|
|
|
|
|
|
def get(self, *args, **kwargs):
|
|
|
|
if kwargs.get("path") == "public_information":
|
|
|
|
if self.certs_are_broken:
|
|
|
|
raise requests.exceptions.SSLError
|
|
|
|
port = kwargs.get("port")
|
|
|
|
if port in self.ports_that_are_down:
|
|
|
|
raise socket.gaierror
|
|
|
|
|
|
|
|
real_get = super(_TestMiddlewareClient, self).__getattr__("get")
|
|
|
|
return real_get(*args, **kwargs)
|
|
|
|
|
|
|
|
|
2019-02-14 19:44:38 +00:00
|
|
|
class NodeIsDownMiddleware(MockRestMiddleware):
|
|
|
|
"""
|
|
|
|
Modified middleware to emulate one node being down amongst many.
|
|
|
|
"""
|
2019-11-10 10:57:40 +00:00
|
|
|
|
2019-02-15 01:32:11 +00:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.client = _MiddlewareClientWithConnectionProblems()
|
2019-02-14 19:44:38 +00:00
|
|
|
|
2019-02-15 01:32:11 +00:00
|
|
|
def node_is_down(self, node):
|
2019-06-17 08:41:10 +00:00
|
|
|
self.client.ports_that_are_down.add(node.rest_interface.port)
|
2019-02-15 01:32:11 +00:00
|
|
|
|
|
|
|
def node_is_up(self, node):
|
2019-06-17 08:41:10 +00:00
|
|
|
self.client.ports_that_are_down.remove(node.rest_interface.port)
|
2019-02-14 19:44:38 +00:00
|
|
|
|
2019-09-26 22:55:16 +00:00
|
|
|
def all_nodes_up(self):
|
|
|
|
self.client.ports_that_are_down = set()
|
|
|
|
|
|
|
|
def all_nodes_down(self):
|
|
|
|
self.client.ports_that_are_down = set(MOCK_KNOWN_URSULAS_CACHE)
|
|
|
|
|
|
|
|
|
2018-09-13 19:26:25 +00:00
|
|
|
class EvilMiddleWare(MockRestMiddleware):
|
|
|
|
"""
|
|
|
|
Middleware for assholes.
|
|
|
|
"""
|
2018-10-23 16:45:43 +00:00
|
|
|
|
2018-09-13 19:26:25 +00:00
|
|
|
def propagate_shitty_interface_id(self, ursula, shitty_interface_id):
|
|
|
|
"""
|
|
|
|
Try to get Ursula to propagate a malicious (or otherwise shitty) interface ID.
|
|
|
|
"""
|
2019-02-06 04:25:47 +00:00
|
|
|
response = self.client.post(node=ursula,
|
|
|
|
path="node_metadata",
|
2018-11-29 22:26:54 +00:00
|
|
|
data=bytes(VariableLengthBytestring(shitty_interface_id))
|
|
|
|
)
|
2018-09-23 04:04:40 +00:00
|
|
|
return response
|
2020-03-24 22:14:24 +00:00
|
|
|
|
|
|
|
def upload_arbitrary_data(self, node, path, data):
|
|
|
|
response = self.client.post(node_or_sprout=node,
|
|
|
|
path=path,
|
|
|
|
data=data)
|
2020-05-13 03:21:15 +00:00
|
|
|
return response
|