add password confirmation dialog

This commit is contained in:
Peter Schmidbauer 2019-10-30 21:27:23 +01:00
parent da084dad0b
commit b19f180639
10 changed files with 42 additions and 21 deletions

View file

@ -9,13 +9,18 @@
Given we use the config "encrypted.yaml" Given we use the config "encrypted.yaml"
When we run "jrnl --decrypt" and enter "bad doggie no biscuit" When we run "jrnl --decrypt" and enter "bad doggie no biscuit"
Then the config for journal "default" should have "encrypt" set to "bool:False" Then the config for journal "default" should have "encrypt" set to "bool:False"
Then the output should contain "Journal decrypted" Then we should see the message "Journal decrypted"
and the journal should have 2 entries and the journal should have 2 entries
Scenario: Encrypting a journal Scenario: Encrypting a journal
Given we use the config "basic.yaml" Given we use the config "basic.yaml"
When we run "jrnl --encrypt" and enter "swordfish" and "n" When we run "jrnl --encrypt" and enter
Then the output should contain "Journal encrypted" """
swordfish
swordfish
n
"""
Then we should see the message "Journal encrypted"
and the config for journal "default" should have "encrypt" set to "bool:True" and the config for journal "default" should have "encrypt" set to "bool:True"
When we run "jrnl -n 1" and enter "swordfish" When we run "jrnl -n 1" and enter "swordfish"
Then the output should contain "Password" Then the output should contain "Password"
@ -23,7 +28,12 @@
Scenario: Storing a password in Keychain Scenario: Storing a password in Keychain
Given we use the config "multiple.yaml" Given we use the config "multiple.yaml"
When we run "jrnl simple --encrypt" and enter "sabertooth" and "y" When we run "jrnl simple --encrypt" and enter
"""
sabertooth
sabertooth
n
"""
When we set the keychain password of "simple" to "sabertooth" When we set the keychain password of "simple" to "sabertooth"
Then the config for journal "simple" should have "encrypt" set to "bool:True" Then the config for journal "simple" should have "encrypt" set to "bool:True"
When we run "jrnl simple -n 1" When we run "jrnl simple -n 1"

View file

@ -42,5 +42,10 @@ Feature: Multiple journals
Scenario: Don't crash if no file exists for a configured encrypted journal Scenario: Don't crash if no file exists for a configured encrypted journal
Given we use the config "multiple.yaml" Given we use the config "multiple.yaml"
When we run "jrnl new_encrypted Adding first entry" and enter "these three eyes" When we run "jrnl new_encrypted Adding first entry" and enter
"""
these three eyes
these three eyes
n
"""
Then we should see the message "Journal 'new_encrypted' created" Then we should see the message "Journal 'new_encrypted' created"

View file

@ -28,7 +28,7 @@ class TestKeyring(keyring.backend.KeyringBackend):
def get_password(self, servicename, username): def get_password(self, servicename, username):
return self.keys[servicename].get(username) return self.keys[servicename].get(username)
def delete_password(self, servicename, username, password): def delete_password(self, servicename, username):
self.keys[servicename][username] = None self.keys[servicename][username] = None
@ -93,7 +93,7 @@ def run_with_input(context, command, inputs1="", inputs2=""):
text = iter((inputs1, inputs2)) if inputs1 else iter(context.text.split("\n")) text = iter((inputs1, inputs2)) if inputs1 else iter(context.text.split("\n"))
args = ushlex(command)[1:] args = ushlex(command)[1:]
with patch("builtins.input", side_effect=_mock_input(text)) as mock_input: with patch("builtins.input", side_effect=_mock_input(text)) as mock_input:
with patch("jrnl.util.getpass", side_effect=_mock_getpass(text)) as mock_getpass: with patch("jrnl.util.gp.getpass", side_effect=_mock_getpass(text)) as mock_getpass:
with patch("sys.stdin.read", side_effect=text) as mock_read: with patch("sys.stdin.read", side_effect=text) as mock_read:
try: try:
cli.run(args or []) cli.run(args or [])

View file

@ -38,7 +38,7 @@ class EncryptedJournal(Journal.Journal):
filename = filename or self.config['journal'] filename = filename or self.config['journal']
if not os.path.exists(filename): if not os.path.exists(filename):
password = util.getpass("Enter password for new journal: ") password = util.create_password()
if password: if password:
if util.yesno("Do you want to store the password in your keychain?", default=True): if util.yesno("Do you want to store the password in your keychain?", default=True):
util.set_keychain(self.name, password) util.set_keychain(self.name, password)

View file

@ -71,7 +71,7 @@ class Journal:
filename = filename or self.config['journal'] filename = filename or self.config['journal']
if not os.path.exists(filename): if not os.path.exists(filename):
print("[Journal '{0}' created at {1}]".format(self.name, filename)) print("[Journal '{0}' created at {1}]".format(self.name, filename), file=sys.stderr)
self._create(filename) self._create(filename)
text = self._load(filename) text = self._load(filename)

View file

@ -79,7 +79,7 @@ def encrypt(journal, filename=None):
""" Encrypt into new file. If filename is not set, we encrypt the journal file itself. """ """ Encrypt into new file. If filename is not set, we encrypt the journal file itself. """
from . import EncryptedJournal from . import EncryptedJournal
journal.config['password'] = util.getpass("Enter new password: ") journal.config['password'] = util.create_password()
journal.config['encrypt'] = True journal.config['encrypt'] = True
new_journal = EncryptedJournal.EncryptedJournal(None, **journal.config) new_journal = EncryptedJournal.EncryptedJournal(None, **journal.config)
@ -89,7 +89,7 @@ def encrypt(journal, filename=None):
if util.yesno("Do you want to store the password in your keychain?", default=True): if util.yesno("Do you want to store the password in your keychain?", default=True):
util.set_keychain(journal.name, journal.config['password']) util.set_keychain(journal.name, journal.config['password'])
print("Journal encrypted to {0}.".format(filename or new_journal.config['journal'])) print("Journal encrypted to {0}.".format(filename or new_journal.config['journal']), file=sys.stderr)
def decrypt(journal, filename=None): def decrypt(journal, filename=None):
@ -100,7 +100,7 @@ def decrypt(journal, filename=None):
new_journal = Journal.PlainJournal(filename, **journal.config) new_journal = Journal.PlainJournal(filename, **journal.config)
new_journal.entries = journal.entries new_journal.entries = journal.entries
new_journal.write(filename) new_journal.write(filename)
print("Journal decrypted to {0}.".format(filename or new_journal.config['journal'])) print("Journal decrypted to {0}.".format(filename or new_journal.config['journal']), file=sys.stderr)
def list_journals(config): def list_journals(config):
@ -199,7 +199,7 @@ def run(manual_args=None):
raw = util.get_text_from_editor(config, template) raw = util.get_text_from_editor(config, template)
else: else:
try: try:
print("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n") print("[Compose Entry; " + _exit_multiline_code + " to finish writing]\n", file=sys.stderr)
raw = sys.stdin.read() raw = sys.stdin.read()
except KeyboardInterrupt: except KeyboardInterrupt:
print("[Entry NOT saved to journal.]", file=sys.stderr) print("[Entry NOT saved to journal.]", file=sys.stderr)

View file

@ -69,7 +69,7 @@ def upgrade_config(config):
for key in missing_keys: for key in missing_keys:
config[key] = default_config[key] config[key] = default_config[key]
save_config(config) save_config(config)
print("[Configuration updated to newest version at {}]".format(CONFIG_FILE_PATH)) print("[Configuration updated to newest version at {}]".format(CONFIG_FILE_PATH), file=sys.stderr)
def save_config(config): def save_config(config):
@ -139,7 +139,7 @@ def install():
else: else:
util.set_keychain("default", None) util.set_keychain("default", None)
EncryptedJournal._create(default_config['journals']['default'], password) EncryptedJournal._create(default_config['journals']['default'], password)
print("Journal will be encrypted.") print("Journal will be encrypted.", file=sys.stderr)
else: else:
PlainJournal._create(default_config['journals']['default']) PlainJournal._create(default_config['journals']['default'])

View file

@ -26,5 +26,5 @@ class JRNLImporter:
sys.exit(0) sys.exit(0)
journal.import_(other_journal_txt) journal.import_(other_journal_txt)
new_cnt = len(journal.entries) new_cnt = len(journal.entries)
print("[{0} imported to {1} journal]".format(new_cnt - old_cnt, journal.name)) print("[{0} imported to {1} journal]".format(new_cnt - old_cnt, journal.name), file=sys.stderr)
journal.write() journal.write()

View file

@ -111,10 +111,10 @@ older versions of jrnl anymore.
for j in all_journals: for j in all_journals:
j.write() j.write()
print("\nUpgrading config...") print("\nUpgrading config...", file=sys.stderr)
backup(config_path) backup(config_path)
print("\nWe're all done here and you can start enjoying jrnl 2.".format(config_path)) print("\nWe're all done here and you can start enjoying jrnl 2.".format(config_path), file=sys.stderr)
class UpgradeValidationException(Exception): class UpgradeValidationException(Exception):
"""Raised when the contents of an upgraded journal do not match the old journal""" """Raised when the contents of an upgraded journal do not match the old journal"""

View file

@ -39,12 +39,18 @@ class UserAbort(Exception):
pass pass
getpass = gp.getpass def create_password():
while True:
pw = gp.getpass("Enter password for new journal: ")
if pw == gp.getpass("Enter password again: "):
return pw
gp.getpass("Passwords did not match, please try again")
def get_password(validator, keychain=None, max_attempts=3): def get_password(validator, keychain=None, max_attempts=3):
pwd_from_keychain = keychain and get_keychain(keychain) pwd_from_keychain = keychain and get_keychain(keychain)
password = pwd_from_keychain or getpass() password = pwd_from_keychain or gp.getpass()
result = validator(password) result = validator(password)
# Password is bad: # Password is bad:
if result is None and pwd_from_keychain: if result is None and pwd_from_keychain:
@ -52,7 +58,7 @@ def get_password(validator, keychain=None, max_attempts=3):
attempt = 1 attempt = 1
while result is None and attempt < max_attempts: while result is None and attempt < max_attempts:
print("Wrong password, try again.", file=sys.stderr) print("Wrong password, try again.", file=sys.stderr)
password = getpass() password = gp.getpass()
result = validator(password) result = validator(password)
attempt += 1 attempt += 1
if result is not None: if result is not None: