2018-11-20 04:27:31 +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-20 04:27:31 +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-20 04:27:31 +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-20 04:27:31 +00:00
|
|
|
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
|
|
|
2018-11-22 18:29:57 +00:00
|
|
|
import json
|
2018-11-20 04:27:31 +00:00
|
|
|
import os
|
2018-11-22 18:29:57 +00:00
|
|
|
from json import JSONDecodeError
|
2018-11-20 04:27:31 +00:00
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
from nucypher.cli.main import nucypher_cli
|
2018-11-22 18:29:57 +00:00
|
|
|
from nucypher.config.characters import UrsulaConfiguration
|
2018-11-25 18:08:12 +00:00
|
|
|
from nucypher.config.constants import APP_DIR, DEFAULT_CONFIG_ROOT
|
2018-11-25 20:45:59 +00:00
|
|
|
from nucypher.utilities.sandbox.constants import (
|
|
|
|
INSECURE_DEVELOPMENT_PASSWORD,
|
|
|
|
MOCK_CUSTOM_INSTALLATION_PATH,
|
|
|
|
MOCK_IP_ADDRESS,
|
2019-02-20 02:05:09 +00:00
|
|
|
MOCK_URSULA_STARTING_PORT,
|
|
|
|
TEMPORARY_DOMAIN)
|
2018-11-20 04:27:31 +00:00
|
|
|
|
|
|
|
|
2018-11-25 21:51:20 +00:00
|
|
|
def test_initialize_ursula_defaults(click_runner, mocker):
|
|
|
|
|
|
|
|
# Mock out filesystem writes
|
|
|
|
mocker.patch.object(UrsulaConfiguration, 'initialize', autospec=True)
|
|
|
|
mocker.patch.object(UrsulaConfiguration, 'to_configuration_file', autospec=True)
|
|
|
|
|
|
|
|
# Use default ursula init args
|
2019-02-20 02:05:09 +00:00
|
|
|
init_args = ('ursula', 'init',
|
|
|
|
'--network', TEMPORARY_DOMAIN,
|
|
|
|
'--federated-only')
|
|
|
|
|
2018-11-25 21:51:20 +00:00
|
|
|
user_input = '{ip}\n{password}\n{password}\n'.format(password=INSECURE_DEVELOPMENT_PASSWORD, ip=MOCK_IP_ADDRESS)
|
|
|
|
result = click_runner.invoke(nucypher_cli, init_args, input=user_input, catch_exceptions=False)
|
|
|
|
assert result.exit_code == 0
|
|
|
|
|
|
|
|
# REST Host
|
|
|
|
assert 'Enter Ursula\'s public-facing IPv4 address' in result.output
|
|
|
|
|
|
|
|
# Auth
|
|
|
|
assert 'Enter keyring password:' in result.output, 'WARNING: User was not prompted for password'
|
|
|
|
assert 'Repeat for confirmation:' in result.output, 'User was not prompted to confirm password'
|
|
|
|
|
|
|
|
|
|
|
|
def test_initialize_custom_configuration_root(custom_filepath, click_runner):
|
2018-11-20 04:27:31 +00:00
|
|
|
|
2018-11-22 18:29:57 +00:00
|
|
|
# Use a custom local filepath for configuration
|
2018-11-25 21:51:20 +00:00
|
|
|
init_args = ('ursula', 'init',
|
2019-02-20 02:05:09 +00:00
|
|
|
'--network', TEMPORARY_DOMAIN,
|
2018-12-03 02:16:41 +00:00
|
|
|
'--federated-only',
|
2018-11-25 21:51:20 +00:00
|
|
|
'--config-root', custom_filepath,
|
|
|
|
'--rest-host', MOCK_IP_ADDRESS,
|
|
|
|
'--rest-port', MOCK_URSULA_STARTING_PORT)
|
|
|
|
|
2019-02-15 03:48:15 +00:00
|
|
|
user_input = '{password}\n{password}'.format(password=INSECURE_DEVELOPMENT_PASSWORD)
|
2018-11-22 18:29:57 +00:00
|
|
|
result = click_runner.invoke(nucypher_cli, init_args, input=user_input, catch_exceptions=False)
|
2018-11-20 04:27:31 +00:00
|
|
|
assert result.exit_code == 0
|
2018-11-22 18:29:57 +00:00
|
|
|
|
2018-11-25 18:08:12 +00:00
|
|
|
# CLI Output
|
2018-11-22 18:29:57 +00:00
|
|
|
assert MOCK_CUSTOM_INSTALLATION_PATH in result.output, "Configuration not in system temporary directory"
|
|
|
|
assert "nucypher ursula run" in result.output, 'Help message is missing suggested command'
|
2018-11-25 21:51:20 +00:00
|
|
|
assert 'IPv4' not in result.output
|
2018-11-25 18:08:12 +00:00
|
|
|
|
|
|
|
# Files and Directories
|
2018-11-22 18:29:57 +00:00
|
|
|
assert os.path.isdir(custom_filepath), 'Configuration file does not exist'
|
|
|
|
assert os.path.isdir(os.path.join(custom_filepath, 'keyring')), 'Keyring does not exist'
|
|
|
|
assert os.path.isdir(os.path.join(custom_filepath, 'known_nodes')), 'known_nodes directory does not exist'
|
|
|
|
|
2018-11-24 05:11:14 +00:00
|
|
|
custom_config_filepath = os.path.join(custom_filepath, UrsulaConfiguration.CONFIG_FILENAME)
|
|
|
|
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
|
|
|
|
|
2018-11-25 18:08:12 +00:00
|
|
|
# Auth
|
|
|
|
assert 'Enter keyring password:' in result.output, 'WARNING: User was not prompted for password'
|
|
|
|
assert 'Repeat for confirmation:' in result.output, 'User was not prompted to confirm password'
|
|
|
|
|
2018-11-22 18:29:57 +00:00
|
|
|
|
2018-12-08 21:06:40 +00:00
|
|
|
def test_configuration_file_contents(custom_filepath, nominal_federated_configuration_fields):
|
2018-11-22 18:29:57 +00:00
|
|
|
custom_config_filepath = os.path.join(custom_filepath, UrsulaConfiguration.CONFIG_FILENAME)
|
|
|
|
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
|
|
|
|
|
|
|
|
# Check the contents of the configuration file
|
|
|
|
with open(custom_config_filepath, 'r') as config_file:
|
|
|
|
raw_contents = config_file.read()
|
|
|
|
|
|
|
|
try:
|
|
|
|
data = json.loads(raw_contents)
|
|
|
|
except JSONDecodeError:
|
|
|
|
raise pytest.fail(msg="Invalid JSON configuration file {}".format(custom_config_filepath))
|
|
|
|
|
2018-12-08 21:06:40 +00:00
|
|
|
for field in nominal_federated_configuration_fields:
|
2018-11-22 18:29:57 +00:00
|
|
|
assert field in data, "Missing field '{}' from configuration file."
|
|
|
|
if any(keyword in field for keyword in ('path', 'dir')):
|
|
|
|
path = data[field]
|
|
|
|
user_data_dir = APP_DIR.user_data_dir
|
|
|
|
# assert os.path.exists(path), '{} does not exist'.format(path)
|
|
|
|
assert user_data_dir not in path, '{} includes default appdir path {}'.format(field, user_data_dir)
|
|
|
|
|
2018-11-24 05:11:14 +00:00
|
|
|
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
|
|
|
|
|
2018-11-22 18:29:57 +00:00
|
|
|
|
2018-11-25 18:08:12 +00:00
|
|
|
def test_password_prompt(click_runner, custom_filepath):
|
|
|
|
|
|
|
|
# Ensure the configuration file still exists
|
|
|
|
custom_config_filepath = os.path.join(custom_filepath, UrsulaConfiguration.CONFIG_FILENAME)
|
|
|
|
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
|
|
|
|
|
|
|
|
view_args = ('ursula', 'view', '--config-file', custom_config_filepath)
|
|
|
|
|
|
|
|
user_input = '{}\n'.format(INSECURE_DEVELOPMENT_PASSWORD)
|
|
|
|
result = click_runner.invoke(nucypher_cli, view_args, input=user_input, catch_exceptions=False, env=dict())
|
|
|
|
assert 'password' in result.output, 'WARNING: User was not prompted for password'
|
|
|
|
assert result.exit_code == 0
|
|
|
|
|
|
|
|
envvars = {'NUCYPHER_KEYRING_PASSWORD': INSECURE_DEVELOPMENT_PASSWORD}
|
|
|
|
result = click_runner.invoke(nucypher_cli, view_args, input=user_input, catch_exceptions=False, env=envvars)
|
|
|
|
assert not 'password' in result.output, 'User was prompted for password'
|
|
|
|
assert result.exit_code == 0
|
|
|
|
|
|
|
|
|
2018-12-08 21:06:40 +00:00
|
|
|
def test_ursula_view_configuration(custom_filepath, click_runner, nominal_federated_configuration_fields):
|
2018-11-25 18:08:12 +00:00
|
|
|
|
|
|
|
# Ensure the configuration file still exists
|
2018-11-22 18:29:57 +00:00
|
|
|
custom_config_filepath = os.path.join(custom_filepath, UrsulaConfiguration.CONFIG_FILENAME)
|
2018-11-24 05:11:14 +00:00
|
|
|
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
|
|
|
|
|
2018-11-25 18:08:12 +00:00
|
|
|
view_args = ('ursula', 'view', '--config-file', os.path.join(custom_filepath, UrsulaConfiguration.CONFIG_FILENAME))
|
2018-11-22 18:29:57 +00:00
|
|
|
|
|
|
|
# View the configuration
|
|
|
|
result = click_runner.invoke(nucypher_cli, view_args,
|
|
|
|
input='{}\n'.format(INSECURE_DEVELOPMENT_PASSWORD),
|
|
|
|
catch_exceptions=False)
|
|
|
|
|
2018-11-25 18:08:12 +00:00
|
|
|
# CLI Output
|
2018-11-22 18:29:57 +00:00
|
|
|
assert 'password' in result.output, 'WARNING: User was not prompted for password'
|
|
|
|
assert MOCK_CUSTOM_INSTALLATION_PATH in result.output
|
2018-12-08 21:06:40 +00:00
|
|
|
for field in nominal_federated_configuration_fields:
|
2018-11-22 18:29:57 +00:00
|
|
|
assert field in result.output, "Missing field '{}' from configuration file."
|
|
|
|
|
|
|
|
# Make sure nothing crazy is happening...
|
|
|
|
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
|
|
|
|
|
|
|
|
|
2019-01-30 19:37:45 +00:00
|
|
|
def test_run_federated_ursula_from_config_file(custom_filepath, click_runner):
|
2018-11-25 18:08:12 +00:00
|
|
|
|
|
|
|
# Ensure the configuration file still exists
|
2018-11-22 18:29:57 +00:00
|
|
|
custom_config_filepath = os.path.join(custom_filepath, UrsulaConfiguration.CONFIG_FILENAME)
|
2018-11-24 05:11:14 +00:00
|
|
|
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
|
|
|
|
|
2018-11-25 18:08:12 +00:00
|
|
|
# Run Ursula
|
2019-01-30 19:37:45 +00:00
|
|
|
run_args = ('ursula', 'run',
|
|
|
|
'--dry-run',
|
|
|
|
'--config-file', custom_config_filepath)
|
2019-02-22 05:38:54 +00:00
|
|
|
|
2018-11-22 18:29:57 +00:00
|
|
|
result = click_runner.invoke(nucypher_cli, run_args,
|
|
|
|
input='{}\nY\n'.format(INSECURE_DEVELOPMENT_PASSWORD),
|
|
|
|
catch_exceptions=False)
|
|
|
|
|
2018-11-25 18:08:12 +00:00
|
|
|
# CLI Output
|
2018-11-20 04:27:31 +00:00
|
|
|
assert result.exit_code == 0
|
2019-01-30 19:37:45 +00:00
|
|
|
assert 'Federated' in result.output, 'WARNING: Federated ursula is not running in federated mode'
|
|
|
|
assert 'Connecting' in result.output
|
|
|
|
assert 'Running' in result.output
|
|
|
|
assert 'Attached' in result.output
|
|
|
|
assert "'help' or '?'" in result.output
|
2018-11-22 18:29:57 +00:00
|
|
|
|
|
|
|
|
2019-02-15 03:48:15 +00:00
|
|
|
def test_empty_federated_status(click_runner, custom_filepath):
|
|
|
|
|
|
|
|
custom_config_filepath = os.path.join(custom_filepath, UrsulaConfiguration.CONFIG_FILENAME)
|
|
|
|
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
|
|
|
|
|
|
|
|
status_args = ('status', '--config-file', custom_config_filepath)
|
|
|
|
result = click_runner.invoke(nucypher_cli, status_args, catch_exceptions=True)
|
|
|
|
|
|
|
|
assert result.exit_code == 0
|
|
|
|
|
|
|
|
assert 'Federated Only' in result.output
|
|
|
|
heading = 'Known Nodes (connected 0 / seen 0)'
|
|
|
|
assert heading in result.output
|
|
|
|
assert 'password' not in result.output
|
|
|
|
|
|
|
|
|
2018-11-22 18:29:57 +00:00
|
|
|
def test_ursula_destroy_configuration(custom_filepath, click_runner):
|
2018-11-25 18:08:12 +00:00
|
|
|
|
|
|
|
preexisting_live_configuration = os.path.isdir(DEFAULT_CONFIG_ROOT)
|
2019-02-14 08:23:48 +00:00
|
|
|
preexisting_live_configuration_file = os.path.isfile(os.path.join(DEFAULT_CONFIG_ROOT,
|
|
|
|
UrsulaConfiguration.CONFIG_FILENAME))
|
2018-11-25 18:08:12 +00:00
|
|
|
|
|
|
|
# Ensure the configuration file still exists
|
2018-11-22 18:29:57 +00:00
|
|
|
custom_config_filepath = os.path.join(custom_filepath, UrsulaConfiguration.CONFIG_FILENAME)
|
2018-11-24 05:11:14 +00:00
|
|
|
assert os.path.isfile(custom_config_filepath), 'Configuration file does not exist'
|
|
|
|
|
2018-11-25 18:08:12 +00:00
|
|
|
# Run the destroy command
|
2018-11-22 18:29:57 +00:00
|
|
|
destruction_args = ('ursula', 'destroy', '--config-file', custom_config_filepath)
|
|
|
|
result = click_runner.invoke(nucypher_cli, destruction_args,
|
|
|
|
input='{}\nY\n'.format(INSECURE_DEVELOPMENT_PASSWORD),
|
|
|
|
catch_exceptions=False)
|
|
|
|
|
2018-11-25 18:08:12 +00:00
|
|
|
# CLI Output
|
2018-11-24 05:11:14 +00:00
|
|
|
assert not os.path.isfile(custom_config_filepath), 'Configuration file still exists'
|
2018-11-22 18:29:57 +00:00
|
|
|
assert 'password' in result.output, 'WARNING: User was not prompted for password'
|
|
|
|
assert '? [y/N]:' in result.output, 'WARNING: User was not asked to destroy files'
|
|
|
|
assert custom_filepath in result.output, 'WARNING: Configuration path not in output. Deleting the wrong path?'
|
2019-03-18 20:47:50 +00:00
|
|
|
assert f'Deleted' in result.output, '"Destroyed" not in output'
|
|
|
|
assert custom_filepath in result.output
|
2018-11-22 18:29:57 +00:00
|
|
|
assert result.exit_code == 0, 'Destruction did not succeed'
|
|
|
|
|
|
|
|
# Ensure the files are deleted from the filesystem
|
2019-03-18 17:44:27 +00:00
|
|
|
assert not os.path.isfile(custom_config_filepath), 'Files still exist' # ... shes's gone...
|
2019-04-02 03:46:00 +00:00
|
|
|
assert os.path.isdir(custom_filepath), 'Nucypher files no longer exist' # ... but not NuCypher ...
|
2018-11-25 18:08:12 +00:00
|
|
|
|
|
|
|
# If this test started off with a live configuration, ensure it still exists
|
|
|
|
if preexisting_live_configuration:
|
|
|
|
configuration_still_exists = os.path.isdir(DEFAULT_CONFIG_ROOT)
|
|
|
|
assert configuration_still_exists
|
|
|
|
|
|
|
|
if preexisting_live_configuration_file:
|
|
|
|
file_still_exists = os.path.isfile(os.path.join(DEFAULT_CONFIG_ROOT, UrsulaConfiguration.CONFIG_FILENAME))
|
|
|
|
assert file_still_exists, 'WARNING: Test command deleted live non-test files'
|