mirror of https://github.com/nucypher/nucypher.git
Improve keyring creation prompts and warnings
parent
e96b1d7748
commit
5575c2c135
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue