mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Add password confirmation dialog
This commit is contained in:
parent
70c946bc13
commit
8eb185f8a2
6 changed files with 66 additions and 25 deletions
|
@ -3,29 +3,55 @@
|
||||||
Given we use the config "encrypted.yaml"
|
Given we use the config "encrypted.yaml"
|
||||||
When we run "jrnl -n 1" and enter "bad doggie no biscuit"
|
When we run "jrnl -n 1" and enter "bad doggie no biscuit"
|
||||||
Then the output should contain "Password"
|
Then the output should contain "Password"
|
||||||
and the output should contain "2013-06-10 15:40 Life is good"
|
And the output should contain "2013-06-10 15:40 Life is good"
|
||||||
|
|
||||||
Scenario: Decrypting a journal
|
Scenario: Decrypting a journal
|
||||||
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 we should see the message "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
|
||||||
|
"""
|
||||||
|
swordfish
|
||||||
|
swordfish
|
||||||
|
n
|
||||||
|
"""
|
||||||
Then we should see the message "Journal encrypted"
|
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"
|
||||||
and the output should contain "2013-06-10 15:40 Life is good"
|
And the output should contain "2013-06-10 15:40 Life is good"
|
||||||
|
|
||||||
|
Scenario: Mistyping your password
|
||||||
|
Given we use the config "basic.yaml"
|
||||||
|
When we run "jrnl --encrypt" and enter
|
||||||
|
"""
|
||||||
|
swordfish
|
||||||
|
sordfish
|
||||||
|
swordfish
|
||||||
|
swordfish
|
||||||
|
n
|
||||||
|
"""
|
||||||
|
Then we should see the message "Passwords did not match"
|
||||||
|
And we should see the message "Journal encrypted"
|
||||||
|
And the config for journal "default" should have "encrypt" set to "bool:True"
|
||||||
|
When we run "jrnl -n 1" and enter "swordfish"
|
||||||
|
Then the output should contain "Password"
|
||||||
|
And the output should contain "2013-06-10 15:40 Life is good"
|
||||||
|
|
||||||
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
|
||||||
|
y
|
||||||
|
"""
|
||||||
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"
|
||||||
Then we should not see the message "Password"
|
Then the output should contain "2013-06-10 15:40 Life is good"
|
||||||
and the output should contain "2013-06-10 15:40 Life is good"
|
|
||||||
|
|
|
@ -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" and "y"
|
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"
|
||||||
|
|
|
@ -33,7 +33,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
|
||||||
|
|
||||||
|
|
||||||
|
@ -109,29 +109,33 @@ def _mock_input(inputs):
|
||||||
|
|
||||||
@when('we run "{command}" and enter')
|
@when('we run "{command}" and enter')
|
||||||
@when('we run "{command}" and enter ""')
|
@when('we run "{command}" and enter ""')
|
||||||
@when('we run "{command}" and enter "{inputs1}"')
|
@when('we run "{command}" and enter "{inputs}"')
|
||||||
@when('we run "{command}" and enter "{inputs1}" and "{inputs2}"')
|
def run_with_input(context, command, inputs=""):
|
||||||
def run_with_input(context, command, inputs1="", inputs2=""):
|
|
||||||
# create an iterator through all inputs. These inputs will be fed one by one
|
# create an iterator through all inputs. These inputs will be fed one by one
|
||||||
# to the mocked calls for 'input()', 'util.getpass()' and 'sys.stdin.read()'
|
# to the mocked calls for 'input()', 'util.getpass()' and 'sys.stdin.read()'
|
||||||
if inputs1:
|
if context.text:
|
||||||
text = iter((inputs1, inputs2))
|
|
||||||
elif context.text:
|
|
||||||
text = iter(context.text.split("\n"))
|
text = iter(context.text.split("\n"))
|
||||||
else:
|
else:
|
||||||
text = iter(("", ""))
|
text = iter([inputs])
|
||||||
|
|
||||||
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:
|
patch("getpass.getpass", side_effect=_mock_getpass(text)) as mock_getpass,\
|
||||||
with patch("sys.stdin.read", side_effect=text) as mock_read:
|
patch("sys.stdin.read", side_effect=text) as mock_read:
|
||||||
try:
|
try:
|
||||||
cli.run(args or [])
|
cli.run(args or [])
|
||||||
context.exit_status = 0
|
context.exit_status = 0
|
||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
context.exit_status = e.code
|
context.exit_status = e.code
|
||||||
|
|
||||||
# assert at least one of the mocked input methods got called
|
# at least one of the mocked input methods got called
|
||||||
assert mock_input.called or mock_getpass.called or mock_read.called
|
assert mock_input.called or mock_getpass.called or mock_read.called
|
||||||
|
# all inputs were used
|
||||||
|
try:
|
||||||
|
next(text)
|
||||||
|
assert False, "Not all inputs were consumed"
|
||||||
|
except StopIteration:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -78,7 +78,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)
|
||||||
|
|
10
jrnl/util.py
10
jrnl/util.py
|
@ -37,12 +37,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
|
||||||
|
|
||||||
|
print("Passwords did not match, please try again", file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
|
|
Loading…
Add table
Reference in a new issue