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 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.signers.software import ClefSigner
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.cli.literature import (
COLLECT_ETH_PASSWORD,
COLLECT_NUCYPHER_PASSWORD,
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.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:
@ -77,11 +78,12 @@ def unlock_signer_account(config: CharacterConfiguration, json_ipc: bool) -> Non
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"""
prompt = COLLECT_NUCYPHER_PASSWORD
if confirm:
from nucypher.config.keyring import NucypherKeyring
emitter.message(PASSWORD_COLLECTION_NOTICE)
prompt += f" ({NucypherKeyring.MINIMUM_PASSWORD_LENGTH} character minimum)"
keyring_password = get_password_from_prompt(prompt=prompt, confirm=confirm, envvar=envvar)
return keyring_password

View File

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

View File

@ -141,7 +141,7 @@ class BobConfigOptions:
provider_uri=self.provider_uri) # TODO: See #1888
return BobConfiguration.generate(
password=get_nucypher_password(confirm=True),
password=get_nucypher_password(emitter=emitter, confirm=True),
config_root=config_root,
checksum_address=checksum_address,
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 click
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.cli.actions.auth import (
get_client_password,
get_nucypher_password,
@ -116,7 +116,7 @@ class FelixConfigOptions:
def generate_config(self, config_root, discovery_port):
return FelixConfiguration.generate(
password=get_nucypher_password(confirm=True),
password=get_nucypher_password(emitter=StdoutEmitter(), confirm=True),
config_root=config_root,
rest_host=self.host,
rest_port=discovery_port,
@ -163,7 +163,7 @@ class FelixCharacterOptions:
# Authenticate
unlock_nucypher_keyring(emitter,
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,
envvar=NUCYPHER_ENVVAR_WORKER_ETH_PASSWORD)

View File

@ -179,7 +179,7 @@ class UrsulaConfigOptions:
if not self.rest_host:
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,
rest_host=self.rest_host,
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)
if not 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:
config_options.domain = select_network(emitter)
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_DISABLING_AGREEMENT = """
By disabling snapshots, staker {staking_address} will be excluded from all future DAO validations
until snapshots are enabled.
@ -380,6 +381,11 @@ DEFAULT_TO_LONE_CONFIG_FILE = "Defaulting to {config_class} configuration file:
# 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_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_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
if new_configuration.default_filepath() != filepath:

View File

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

View File

@ -21,6 +21,7 @@ from constant_sorrow.constants import NO_PASSWORD
from nacl.exceptions import CryptoError
from nucypher.blockchain.eth.decorators import InvalidChecksumAddress
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.cli.actions.auth import (
get_client_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))
def test_get_nucypher_password(mock_stdin, mock_account, confirm, capsys):
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 mock_stdin.empty()
captured = capsys.readouterr()