summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Schmidbauer <peter.schmidb@gmail.com>2019-10-31 21:22:50 +0100
committerPeter Schmidbauer <peter.schmidb@gmail.com>2020-04-10 11:51:55 -0700
commit8eb185f8a2aa69d4d8b58ae4828a82717c5083b6 (patch)
tree2bb90c33c543a44ada2f21321f0872ff7271cbbb
parent70c946bc13f7d6b1b1e78cf1f656c94708488651 (diff)
Add password confirmation dialog
-rw-r--r--features/encryption.feature42
-rw-r--r--features/multiple_journals.feature7
-rw-r--r--features/steps/core.py28
-rw-r--r--jrnl/EncryptedJournal.py2
-rw-r--r--jrnl/cli.py2
-rw-r--r--jrnl/util.py10
6 files changed, 66 insertions, 25 deletions
diff --git a/features/encryption.feature b/features/encryption.feature
index d30c48b3..787fa850 100644
--- a/features/encryption.feature
+++ b/features/encryption.feature
@@ -3,29 +3,55 @@
Given we use the config "encrypted.yaml"
When we run "jrnl -n 1" and enter "bad doggie no biscuit"
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
Given we use the config "encrypted.yaml"
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 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
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"
- 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"
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
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"
Then the config for journal "simple" should have "encrypt" set to "bool:True"
When we run "jrnl simple -n 1"
- Then we should not see the message "Password"
- and the output should contain "2013-06-10 15:40 Life is good"
+ Then the output should contain "2013-06-10 15:40 Life is good"
diff --git a/features/multiple_journals.feature b/features/multiple_journals.feature
index 28587f96..56c4b3a7 100644
--- a/features/multiple_journals.feature
+++ b/features/multiple_journals.feature
@@ -42,5 +42,10 @@ Feature: Multiple journals
Scenario: Don't crash if no file exists for a configured encrypted journal
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"
diff --git a/features/steps/core.py b/features/steps/core.py
index 5e8e3ea3..57b958f8 100644
--- a/features/steps/core.py
+++ b/features/steps/core.py
@@ -33,7 +33,7 @@ class TestKeyring(keyring.backend.KeyringBackend):
def get_password(self, servicename, 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
@@ -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 "{inputs1}"')
-@when('we run "{command}" and enter "{inputs1}" and "{inputs2}"')
-def run_with_input(context, command, inputs1="", inputs2=""):
+@when('we run "{command}" and enter "{inputs}"')
+def run_with_input(context, command, inputs=""):
# 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()'
- if inputs1:
- text = iter((inputs1, inputs2))
- elif context.text:
+ if context.text:
text = iter(context.text.split("\n"))
else:
- text = iter(("", ""))
+ text = iter([inputs])
+
args = ushlex(command)[1:]
- 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("sys.stdin.read", side_effect=text) as mock_read:
+ with patch("builtins.input", side_effect=_mock_input(text)) as mock_input,\
+ patch("getpass.getpass", side_effect=_mock_getpass(text)) as mock_getpass,\
+ patch("sys.stdin.read", side_effect=text) as mock_read:
try:
cli.run(args or [])
context.exit_status = 0
except SystemExit as e:
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
+ # all inputs were used
+ try:
+ next(text)
+ assert False, "Not all inputs were consumed"
+ except StopIteration:
+ pass
diff --git a/jrnl/EncryptedJournal.py b/jrnl/EncryptedJournal.py
index 1cce66b8..f3ff63c6 100644
--- a/jrnl/EncryptedJournal.py
+++ b/jrnl/EncryptedJournal.py
@@ -38,7 +38,7 @@ class EncryptedJournal(Journal.Journal):
filename = filename or self.config['journal']
if not os.path.exists(filename):
- password = util.getpass("Enter password for new journal: ")
+ password = util.create_password()
if password:
if util.yesno("Do you want to store the password in your keychain?", default=True):
util.set_keychain(self.name, password)
diff --git a/jrnl/cli.py b/jrnl/cli.py
index 738087e4..945a728f 100644
--- a/jrnl/cli.py
+++ b/jrnl/cli.py
@@ -78,7 +78,7 @@ def encrypt(journal, filename=None):
""" Encrypt into new file. If filename is not set, we encrypt the journal file itself. """
from . import EncryptedJournal
- journal.config['password'] = util.getpass("Enter new password: ")
+ journal.config['password'] = util.create_password()
journal.config['encrypt'] = True
new_journal = EncryptedJournal.EncryptedJournal(None, **journal.config)
diff --git a/jrnl/util.py b/jrnl/util.py
index fc917ae9..f70b2df9 100644
--- a/jrnl/util.py
+++ b/jrnl/util.py
@@ -37,12 +37,18 @@ class UserAbort(Exception):
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):
pwd_from_keychain = keychain and get_keychain(keychain)
- password = pwd_from_keychain or getpass()
+ password = pwd_from_keychain or gp.getpass()
result = validator(password)
# Password is bad:
if result is None and pwd_from_keychain: