Improve keyring creation prompts and warnings

pull/2662/head
Kieran R. Prasch 2021-04-22 23:01:38 -07:00 committed by Kieran Prasch
parent e96b1d7748
commit 5575c2c135
9 changed files with 36 additions and 14 deletions

View File

@ -21,18 +21,19 @@ import os
from constant_sorrow.constants import NO_PASSWORD from constant_sorrow.constants import NO_PASSWORD
from nacl.exceptions import CryptoError from nacl.exceptions import CryptoError
from nucypher.blockchain.eth.signers.software import ClefSigner
from nucypher.blockchain.eth.decorators import validate_checksum_address from nucypher.blockchain.eth.decorators import validate_checksum_address
from nucypher.blockchain.eth.signers.software import ClefSigner
from nucypher.characters.control.emitters import StdoutEmitter from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.cli.literature import ( from nucypher.cli.literature import (
COLLECT_ETH_PASSWORD, COLLECT_ETH_PASSWORD,
COLLECT_NUCYPHER_PASSWORD, COLLECT_NUCYPHER_PASSWORD,
DECRYPTING_CHARACTER_KEYRING, DECRYPTING_CHARACTER_KEYRING,
GENERIC_PASSWORD_PROMPT GENERIC_PASSWORD_PROMPT,
PASSWORD_COLLECTION_NOTICE
) )
from nucypher.config.base import CharacterConfiguration
from nucypher.config.constants import NUCYPHER_ENVVAR_KEYRING_PASSWORD from nucypher.config.constants import NUCYPHER_ENVVAR_KEYRING_PASSWORD
from nucypher.config.keyring import NucypherKeyring from nucypher.config.keyring import NucypherKeyring
from nucypher.config.base import CharacterConfiguration
def get_password_from_prompt(prompt: str = GENERIC_PASSWORD_PROMPT, envvar: str = None, confirm: bool = False) -> str: def get_password_from_prompt(prompt: str = GENERIC_PASSWORD_PROMPT, envvar: str = None, confirm: bool = False) -> str:
@ -77,11 +78,12 @@ def unlock_signer_account(config: CharacterConfiguration, json_ipc: bool) -> Non
config.signer.unlock_account(account=config.checksum_address, password=__password) config.signer.unlock_account(account=config.checksum_address, password=__password)
def get_nucypher_password(confirm: bool = False, envvar=NUCYPHER_ENVVAR_KEYRING_PASSWORD) -> str: def get_nucypher_password(emitter, confirm: bool = False, envvar=NUCYPHER_ENVVAR_KEYRING_PASSWORD) -> str:
"""Interactively collect a nucypher password""" """Interactively collect a nucypher password"""
prompt = COLLECT_NUCYPHER_PASSWORD prompt = COLLECT_NUCYPHER_PASSWORD
if confirm: if confirm:
from nucypher.config.keyring import NucypherKeyring from nucypher.config.keyring import NucypherKeyring
emitter.message(PASSWORD_COLLECTION_NOTICE)
prompt += f" ({NucypherKeyring.MINIMUM_PASSWORD_LENGTH} character minimum)" prompt += f" ({NucypherKeyring.MINIMUM_PASSWORD_LENGTH} character minimum)"
keyring_password = get_password_from_prompt(prompt=prompt, confirm=confirm, envvar=envvar) keyring_password = get_password_from_prompt(prompt=prompt, confirm=confirm, envvar=envvar)
return keyring_password return keyring_password

View File

@ -206,7 +206,7 @@ class AliceFullConfigOptions:
network=opts.domain) network=opts.domain)
return AliceConfiguration.generate( return AliceConfiguration.generate(
password=get_nucypher_password(confirm=True), password=get_nucypher_password(emitter=emitter, confirm=True),
config_root=config_root, config_root=config_root,
checksum_address=pay_with, checksum_address=pay_with,
domain=opts.domain, domain=opts.domain,

View File

@ -141,7 +141,7 @@ class BobConfigOptions:
provider_uri=self.provider_uri) # TODO: See #1888 provider_uri=self.provider_uri) # TODO: See #1888
return BobConfiguration.generate( return BobConfiguration.generate(
password=get_nucypher_password(confirm=True), password=get_nucypher_password(emitter=emitter, confirm=True),
config_root=config_root, config_root=config_root,
checksum_address=checksum_address, checksum_address=checksum_address,
domain=self.domain, domain=self.domain,

View File

@ -16,10 +16,10 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
""" """
import click
import os import os
import click from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.cli.actions.auth import ( from nucypher.cli.actions.auth import (
get_client_password, get_client_password,
get_nucypher_password, get_nucypher_password,
@ -116,7 +116,7 @@ class FelixConfigOptions:
def generate_config(self, config_root, discovery_port): def generate_config(self, config_root, discovery_port):
return FelixConfiguration.generate( return FelixConfiguration.generate(
password=get_nucypher_password(confirm=True), password=get_nucypher_password(emitter=StdoutEmitter(), confirm=True),
config_root=config_root, config_root=config_root,
rest_host=self.host, rest_host=self.host,
rest_port=discovery_port, rest_port=discovery_port,
@ -163,7 +163,7 @@ class FelixCharacterOptions:
# Authenticate # Authenticate
unlock_nucypher_keyring(emitter, unlock_nucypher_keyring(emitter,
character_configuration=felix_config, character_configuration=felix_config,
password=get_nucypher_password(confirm=False)) password=get_nucypher_password(emitter=emitter, confirm=False))
client_password = get_client_password(checksum_address=felix_config.checksum_address, client_password = get_client_password(checksum_address=felix_config.checksum_address,
envvar=NUCYPHER_ENVVAR_WORKER_ETH_PASSWORD) envvar=NUCYPHER_ENVVAR_WORKER_ETH_PASSWORD)

View File

@ -179,7 +179,7 @@ class UrsulaConfigOptions:
if not self.rest_host: if not self.rest_host:
self.rest_host = collect_worker_ip_address(emitter, network=self.domain, force=force) self.rest_host = collect_worker_ip_address(emitter, network=self.domain, force=force)
return UrsulaConfiguration.generate(password=get_nucypher_password(confirm=True), return UrsulaConfiguration.generate(password=get_nucypher_password(emitter=emitter, confirm=True),
config_root=config_root, config_root=config_root,
rest_host=self.rest_host, rest_host=self.rest_host,
rest_port=self.rest_port, rest_port=self.rest_port,
@ -301,6 +301,8 @@ def init(general_config, config_options, force, config_root):
_pre_launch_warnings(emitter, dev=None, force=force) _pre_launch_warnings(emitter, dev=None, force=force)
if not config_root: if not config_root:
config_root = general_config.config_root config_root = general_config.config_root
if not config_options.federated_only and not config_options.provider_uri:
raise click.BadOptionUsage('--provider', message="--provider is required to initialize a new ursula.")
if not config_options.federated_only and not config_options.domain: if not config_options.federated_only and not config_options.domain:
config_options.domain = select_network(emitter) config_options.domain = select_network(emitter)
ursula_config = config_options.generate_config(emitter, config_root, force) ursula_config = config_options.generate_config(emitter, config_root, force)

View File

@ -210,6 +210,7 @@ SUCCESSFUL_DISABLE_RESTAKING = 'Successfully disabled re-staking for {staking_ad
# #
# Snapshots # Snapshots
# #
SNAPSHOTS_DISABLING_AGREEMENT = """ SNAPSHOTS_DISABLING_AGREEMENT = """
By disabling snapshots, staker {staking_address} will be excluded from all future DAO validations By disabling snapshots, staker {staking_address} will be excluded from all future DAO validations
until snapshots are enabled. until snapshots are enabled.
@ -380,6 +381,11 @@ DEFAULT_TO_LONE_CONFIG_FILE = "Defaulting to {config_class} configuration file:
# Authentication # Authentication
# #
PASSWORD_COLLECTION_NOTICE = f"""
Please provide a password to lock Worker keys.
Do not forget this password, and ideally store it using a password manager.
"""
COLLECT_ETH_PASSWORD = "Enter ethereum account password ({checksum_address})" COLLECT_ETH_PASSWORD = "Enter ethereum account password ({checksum_address})"
COLLECT_NUCYPHER_PASSWORD = 'Enter nucypher keyring password' COLLECT_NUCYPHER_PASSWORD = 'Enter nucypher keyring password'

View File

@ -55,7 +55,18 @@ def paint_new_installation_help(emitter, new_configuration, filepath):
character_config_class = new_configuration.__class__ character_config_class = new_configuration.__class__
character_name = character_config_class.NAME.lower() character_name = character_config_class.NAME.lower()
emitter.message(f"Generated keyring {new_configuration.keyring_root}", color='green') emitter.message(f"Generated keyring", color='green')
emitter.message(f"""
Public key (stamp): {bytes(new_configuration.keyring.signing_public_key).hex()}
Path to keyring: {new_configuration.keyring_root}
- You can share your public key with anyone. Others need it to interact with you.
- Never share secret keys with anyone! Character keys are required to interact with the network!
- Backup your keyring! Without the keyring you wil not be able to use existing network policies.
- Remember your password! Without the password, it's impossible to decrypt the key!
""")
default_config_filepath = True default_config_filepath = True
if new_configuration.default_filepath() != filepath: if new_configuration.default_filepath() != filepath:

View File

@ -84,7 +84,7 @@ def make_cli_character(character_config,
if unlock_keyring: if unlock_keyring:
unlock_nucypher_keyring(emitter, unlock_nucypher_keyring(emitter,
character_configuration=character_config, character_configuration=character_config,
password=get_nucypher_password(confirm=False)) password=get_nucypher_password(emitter=emitter, confirm=False))
# Handle Signer/Wallet # Handle Signer/Wallet
if unlock_signer: if unlock_signer:

View File

@ -21,6 +21,7 @@ from constant_sorrow.constants import NO_PASSWORD
from nacl.exceptions import CryptoError from nacl.exceptions import CryptoError
from nucypher.blockchain.eth.decorators import InvalidChecksumAddress from nucypher.blockchain.eth.decorators import InvalidChecksumAddress
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.cli.actions.auth import ( from nucypher.cli.actions.auth import (
get_client_password, get_client_password,
get_nucypher_password, get_nucypher_password,
@ -90,7 +91,7 @@ def test_get_client_password(mock_stdin, mock_account, confirm, capsys):
@pytest.mark.parametrize('confirm', (True, False)) @pytest.mark.parametrize('confirm', (True, False))
def test_get_nucypher_password(mock_stdin, mock_account, confirm, capsys): def test_get_nucypher_password(mock_stdin, mock_account, confirm, capsys):
mock_stdin.password(INSECURE_DEVELOPMENT_PASSWORD, confirm=confirm) mock_stdin.password(INSECURE_DEVELOPMENT_PASSWORD, confirm=confirm)
result = get_nucypher_password(confirm=confirm) result = get_nucypher_password(emitter=StdoutEmitter(), confirm=confirm)
assert result == INSECURE_DEVELOPMENT_PASSWORD assert result == INSECURE_DEVELOPMENT_PASSWORD
assert mock_stdin.empty() assert mock_stdin.empty()
captured = capsys.readouterr() captured = capsys.readouterr()