Merge pull request #474 from PR0M3TH3AN/codex/improve-wrong-password-error-message

Improve password retry experience
This commit is contained in:
thePR0M3TH3AN
2025-07-12 09:38:16 -04:00
committed by GitHub
2 changed files with 88 additions and 67 deletions

View File

@@ -377,6 +377,9 @@ class PasswordManager:
) -> bool:
"""Set up encryption for the current fingerprint and load the seed."""
attempts = 0
max_attempts = 5
while attempts < max_attempts:
try:
if password is None:
password = prompt_existing_password("Enter your master password: ")
@@ -393,11 +396,13 @@ class PasswordManager:
try:
self.parent_seed = seed_mgr.decrypt_parent_seed()
except Exception:
msg = "Invalid password for selected seed profile."
msg = (
"Invalid password for selected seed profile. Please try again."
)
print(colored(msg, "red"))
if exit_on_fail:
sys.exit(1)
return False
attempts += 1
password = None
continue
key = derive_index_key(self.parent_seed)
@@ -411,17 +416,22 @@ class PasswordManager:
self.fingerprint_dir = fingerprint_dir
if not self.verify_password(password):
print(colored("Invalid password.", "red"))
if exit_on_fail:
sys.exit(1)
return False
print(colored("Invalid password. Please try again.", "red"))
attempts += 1
password = None
continue
return True
except KeyboardInterrupt:
raise
except Exception as e:
logger.error(f"Failed to set up EncryptionManager: {e}", exc_info=True)
print(colored(f"Error: Failed to set up encryption: {e}", "red"))
if exit_on_fail:
sys.exit(1)
return False
if exit_on_fail:
sys.exit(1)
return False
def load_parent_seed(
self, fingerprint_dir: Path, password: Optional[str] = None

View File

@@ -106,30 +106,38 @@ def prompt_new_password() -> str:
raise PasswordPromptError("Maximum password attempts exceeded")
def prompt_existing_password(prompt_message: str = "Enter your password: ") -> str:
def prompt_existing_password(
prompt_message: str = "Enter your password: ", max_retries: int = 5
) -> str:
"""
Prompts the user to enter an existing password, typically used for decryption purposes.
Prompt the user for an existing password.
This function ensures that the password is entered securely without echoing it to the terminal.
The user will be reprompted on empty input up to ``max_retries`` times.
Parameters:
prompt_message (str): The message displayed to prompt the user. Defaults to "Enter your password: ".
prompt_message (str): Message displayed when prompting for the password.
max_retries (int): Number of attempts allowed before aborting.
Returns:
str: The password entered by the user.
str: The password provided by the user.
Raises:
PasswordPromptError: If the user interrupts the operation.
PasswordPromptError: If the user interrupts the operation or exceeds
``max_retries`` attempts.
"""
attempts = 0
while attempts < max_retries:
try:
password = getpass.getpass(prompt=prompt_message).strip()
if not password:
print(colored("Error: Password cannot be empty.", "red"))
print(
colored("Error: Password cannot be empty. Please try again.", "red")
)
logging.warning("User attempted to enter an empty password.")
raise PasswordPromptError("Password cannot be empty")
attempts += 1
continue
# Normalize the password to NFKD form
normalized_password = unicodedata.normalize("NFKD", password)
logging.debug("User entered an existing password for decryption.")
return normalized_password
@@ -140,10 +148,13 @@ def prompt_existing_password(prompt_message: str = "Enter your password: ") -> s
raise PasswordPromptError("Operation cancelled by user")
except Exception as e:
logging.error(
f"Unexpected error during existing password prompt: {e}", exc_info=True
f"Unexpected error during existing password prompt: {e}",
exc_info=True,
)
print(colored(f"Error: {e}", "red"))
raise PasswordPromptError(str(e))
attempts += 1
raise PasswordPromptError("Maximum password attempts exceeded")
def confirm_action(