Respond to RFCs in PR #861

pull/861/head
Kieran Prasch 2019-04-01 20:46:00 -07:00
parent 13024ed048
commit 9abd4e27fb
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
7 changed files with 76 additions and 58 deletions

View File

@ -11,15 +11,22 @@ from nucypher.config.constants import DEFAULT_CONFIG_ROOT, USER_LOG_DIR
from nucypher.network.middleware import RestMiddleware from nucypher.network.middleware import RestMiddleware
DESTRUCTION = ''' DESTRUCTION = '''
*Permanently and irreversibly delete all* nucypher files including *Permanently and irreversibly delete all* nucypher files including:
- Private and Public Keys - Private and Public Keys
- Known Nodes - Known Nodes
- TLS certificates - TLS certificates
- Node Configurations - Node Configurations
- Log Files
Delete {}?''' Delete {}?'''
CHARACTER_DESTRUCTION = '''
Delete all {name} character files including:
- Private and Public Keys
- Known Nodes
- Node Configuration File
Delete {root}?'''
LOG = Logger('cli.actions') LOG = Logger('cli.actions')
@ -46,13 +53,12 @@ def load_seednodes(min_stake: int,
def destroy_configuration_root(config_root=None, force=False, logs: bool = False) -> str: def destroy_configuration_root(config_root=None, force=False, logs: bool = False) -> str:
"""CAUTION: This will destroy *all* nucypher configuration files from the filesystem""" """CAUTION: This will destroy *all* nucypher configuration files from the configuration root"""
config_root = config_root or DEFAULT_CONFIG_ROOT config_root = config_root or DEFAULT_CONFIG_ROOT
if not force: if not force:
message = f"Destroy top-level configuration directory: {config_root}?" click.confirm(DESTRUCTION.format(config_root), abort=True) # ABORT
click.confirm(message, abort=True) # ABORT
if os.path.isdir(config_root): if os.path.isdir(config_root):
shutil.rmtree(config_root, ignore_errors=force) # config shutil.rmtree(config_root, ignore_errors=force) # config
@ -68,7 +74,8 @@ def destroy_configuration_root(config_root=None, force=False, logs: bool = False
def destroy_configuration(character_config, force: bool = False) -> None: def destroy_configuration(character_config, force: bool = False) -> None:
if not force: if not force:
click.confirm(DESTRUCTION.format(character_config.config_root), abort=True) click.confirm(CHARACTER_DESTRUCTION.format(name=character_config._NAME,
root=character_config.config_root), abort=True)
try: try:
character_config.destroy() character_config.destroy()
@ -117,3 +124,12 @@ performing accurate re-encryption work orders will result in rewards
paid out in ETH retro-actively, on-demand. paid out in ETH retro-actively, on-demand.
Accept node operator obligation?""", abort=True) Accept node operator obligation?""", abort=True)
def handle_missing_configuration_file(character_config_class, config_file: str = None):
config_file_location = config_file or character_config_class.DEFAULT_CONFIG_FILE_LOCATION
message = f'No {character_config_class._NAME.capitalize()} configuration file found.\n' \
f'To create a new persistent {character_config_class._NAME.capitalize()} run: ' \
f'\'nucypher {character_config_class._NAME} init\''
raise click.FileError(filename=config_file_location, hint=message)

View File

@ -105,13 +105,17 @@ def alice(click_config,
federated_only=True) federated_only=True)
else: else:
alice_config = AliceConfiguration.from_configuration_file( try:
filepath=config_file, alice_config = AliceConfiguration.from_configuration_file(
domains={network or GLOBAL_DOMAIN}, filepath=config_file,
network_middleware=click_config.middleware, domains={network or GLOBAL_DOMAIN},
rest_port=discovery_port, network_middleware=click_config.middleware,
checksum_public_address=checksum_address, rest_port=discovery_port,
provider_uri=provider_uri) checksum_public_address=checksum_address,
provider_uri=provider_uri)
except FileNotFoundError:
return actions.handle_missing_configuration_file(character_config_class=AliceConfiguration,
config_file=config_file)
if not dev: if not dev:
click_config.unlock_keyring(character_configuration=alice_config) click_config.unlock_keyring(character_configuration=alice_config)

View File

@ -62,10 +62,7 @@ def bob(click_config,
"""Create a brand-new persistent Bob""" """Create a brand-new persistent Bob"""
if dev: if dev:
actions.handle_control_output(message="WARNING: Using temporary storage area", click_config.emit(message="WARNING: Using temporary storage area", color='yellow')
quiet=quiet,
color='yellow',
json=click_config.json)
if not config_root: # Flag if not config_root: # Flag
config_root = click_config.config_file # Envvar config_root = click_config.config_file # Envvar
@ -82,22 +79,6 @@ def bob(click_config,
return painting.paint_new_installation_help(new_configuration=new_bob_config, return painting.paint_new_installation_help(new_configuration=new_bob_config,
config_file=config_file) config_file=config_file)
elif action == "destroy":
"""Delete all configuration files from the disk"""
if dev:
message = "'nucypher ursula destroy' cannot be used in --dev mode"
raise click.BadOptionUsage(option_name='--dev', message=message)
destroyed_path = actions.destroy_configuration_root(config_class=BobConfiguration,
config_file=config_file,
network=network,
config_root=config_root,
force=force)
return click_config.emitter(message=f"Destroyed {destroyed_path}")
# #
# Get Bob Configuration # Get Bob Configuration
# #
@ -109,12 +90,17 @@ def bob(click_config,
federated_only=True, federated_only=True,
network_middleware=click_config.middleware) network_middleware=click_config.middleware)
else: else:
bob_config = BobConfiguration.from_configuration_file(
filepath=config_file, try:
domains={network or GLOBAL_DOMAIN}, bob_config = BobConfiguration.from_configuration_file(
rest_port=discovery_port, filepath=config_file,
provider_uri=provider_uri, domains={network or GLOBAL_DOMAIN},
network_middleware=click_config.middleware) rest_port=discovery_port,
provider_uri=provider_uri,
network_middleware=click_config.middleware)
except FileNotFoundError:
return actions.handle_missing_configuration_file(character_config_class=BobConfiguration,
config_file=config_file)
# Teacher Ursula # Teacher Ursula
teacher_uris = [teacher_uri] if teacher_uri else list() teacher_uris = [teacher_uri] if teacher_uri else list()
@ -167,5 +153,12 @@ def bob(click_config,
response = BOB.controller.retrieve(request=bob_request_data) response = BOB.controller.retrieve(request=bob_request_data)
return response return response
elif action == "destroy":
"""Delete Bob's character configuration files from the disk"""
if dev:
message = "'nucypher ursula destroy' cannot be used in --dev mode"
raise click.BadOptionUsage(option_name='--dev', message=message)
return actions.destroy_configuration(character_config=bob_config)
else: else:
raise click.BadArgumentUsage(f"No such argument {action}") raise click.BadArgumentUsage(f"No such argument {action}")

View File

@ -36,7 +36,6 @@ from nucypher.cli.types import (
STAKE_VALUE STAKE_VALUE
) )
from nucypher.config.characters import UrsulaConfiguration from nucypher.config.characters import UrsulaConfiguration
from nucypher.config.keyring import NucypherKeyring
from nucypher.utilities.sandbox.constants import ( from nucypher.utilities.sandbox.constants import (
TEMPORARY_DOMAIN, TEMPORARY_DOMAIN,
) )
@ -197,18 +196,22 @@ def ursula(click_config,
else: else:
# Domains -> bytes | or default # Domains -> bytes | or default
domains = [bytes(network, encoding='utf-8')] if network else None domains = set(bytes(network, encoding='utf-8')) if network else None
# Load Ursula from Configuration File # Load Ursula from Configuration File
ursula_config = UrsulaConfiguration.from_configuration_file(filepath=config_file, try:
domains=domains, ursula_config = UrsulaConfiguration.from_configuration_file(filepath=config_file,
registry_filepath=registry_filepath, domains=domains,
provider_uri=provider_uri, registry_filepath=registry_filepath,
rest_host=rest_host, provider_uri=provider_uri,
rest_port=rest_port, rest_host=rest_host,
db_filepath=db_filepath, rest_port=rest_port,
poa=poa, db_filepath=db_filepath,
federated_only=federated_only) poa=poa,
federated_only=federated_only)
except FileNotFoundError:
return actions.handle_missing_configuration_file(character_config_class=UrsulaConfiguration,
config_file=config_file)
click_config.unlock_keyring(character_configuration=ursula_config) click_config.unlock_keyring(character_configuration=ursula_config)

View File

@ -15,6 +15,7 @@ You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>. along with nucypher. If not, see <https://www.gnu.org/licenses/>.
""" """
import base64 import base64
import contextlib
import json import json
import os import os
import shutil import shutil
@ -666,8 +667,8 @@ class NucypherKeyring:
keypaths = self._generate_key_filepaths(account=self.checksum_address, keypaths = self._generate_key_filepaths(account=self.checksum_address,
public_key_dir=public_key_dir, public_key_dir=public_key_dir,
private_key_dir=private_key_dir) private_key_dir=private_key_dir)
# Remove the parsed paths from the disk, weather they exist or not.
for filepath in keypaths.values(): for filepath in keypaths.values():
try: with contextlib.suppress(FileNotFoundError):
os.remove(filepath) os.remove(filepath)
except FileNotFoundError:
pass

View File

@ -81,7 +81,7 @@ def test_coexisting_configurations(click_runner,
assert os.path.isfile(felix_file_location) assert os.path.isfile(felix_file_location)
assert len(os.listdir(public_keys_dir)) == 3 assert len(os.listdir(public_keys_dir)) == 3
# Use a custom local filepath to init an persistent Alice # Use a custom local filepath to init a persistent Alice
alice_init_args = ('alice', 'init', alice_init_args = ('alice', 'init',
'--network', TEMPORARY_DOMAIN, '--network', TEMPORARY_DOMAIN,
'--provider-uri', TEST_PROVIDER_URI, '--provider-uri', TEST_PROVIDER_URI,
@ -94,7 +94,7 @@ def test_coexisting_configurations(click_runner,
assert os.path.isfile(alice_file_location) assert os.path.isfile(alice_file_location)
assert len(os.listdir(public_keys_dir)) == 5 assert len(os.listdir(public_keys_dir)) == 5
# Use the same local filepath to init an persistent Ursula # Use the same local filepath to init a persistent Ursula
init_args = ('ursula', 'init', init_args = ('ursula', 'init',
'--network', TEMPORARY_DOMAIN, '--network', TEMPORARY_DOMAIN,
'--provider-uri', TEST_PROVIDER_URI, '--provider-uri', TEST_PROVIDER_URI,
@ -128,15 +128,16 @@ def test_coexisting_configurations(click_runner,
# Run # Run
# #
# Now start running your Ursula! # Run an Ursula amidst the other configuration files
run_args = ('ursula', 'run', '--dry-run', run_args = ('ursula', 'run', '--dry-run',
'--config-file', another_ursula_configuration_file_location) '--config-file', another_ursula_configuration_file_location)
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}' user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}'
result = click_runner.invoke(nucypher_cli, run_args, input=user_input, catch_exceptions=False) result = click_runner.invoke(nucypher_cli, run_args, input=user_input, catch_exceptions=False)
assert result.exit_code == 0 assert result.exit_code == 0
assert another_ursula in result.output
# Check that the proper Ursula console is attached
assert another_ursula in result.output
# #
# Destroy # Destroy

View File

@ -220,7 +220,7 @@ def test_ursula_destroy_configuration(custom_filepath, click_runner):
# Ensure the files are deleted from the filesystem # Ensure the files are deleted from the filesystem
assert not os.path.isfile(custom_config_filepath), 'Files still exist' # ... shes's gone... assert not os.path.isfile(custom_config_filepath), 'Files still exist' # ... shes's gone...
assert os.path.isdir(custom_filepath), 'Nucypher files no longer still exist' # ... but not NuCypher ... assert os.path.isdir(custom_filepath), 'Nucypher files no longer exist' # ... but not NuCypher ...
# If this test started off with a live configuration, ensure it still exists # If this test started off with a live configuration, ensure it still exists
if preexisting_live_configuration: if preexisting_live_configuration: