mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-10 16:48:31 +02:00
Fix keyring error handling (#1138)
* Capture KeyringLocked exception and allow manual password entry * Create LockedKeyring for steps * Support types in set_keyring step * Clarify LockedKeyring docs * Deal with locked exceptions elsewhere too * Create behave tests for locked keyring * Fix linting * Fix keyring step to allow no type * Handle all keyring retrieval errors * Better keyring error handling * Remove locked keyring for steps; generalize failed keyring * Finalize tests for keyring handling * Update set_keyring step * Make password of failed keyring encryption test more semantic
This commit is contained in:
parent
9f1bef7fde
commit
57de4f9ba4
3 changed files with 56 additions and 20 deletions
|
@ -24,6 +24,7 @@ Feature: Using the installed keyring
|
||||||
n
|
n
|
||||||
"""
|
"""
|
||||||
Then we should get no error
|
Then we should get no error
|
||||||
|
And we should not see the message "Failed to retrieve keyring"
|
||||||
|
|
||||||
Scenario: Encrypt journal with no keyring backend and do store in keyring
|
Scenario: Encrypt journal with no keyring backend and do store in keyring
|
||||||
Given we use the config "simple.yaml"
|
Given we use the config "simple.yaml"
|
||||||
|
@ -36,25 +37,53 @@ Feature: Using the installed keyring
|
||||||
y
|
y
|
||||||
"""
|
"""
|
||||||
Then we should get no error
|
Then we should get no error
|
||||||
|
And we should not see the message "Failed to retrieve keyring"
|
||||||
# @todo add step to check contents of keyring
|
# @todo add step to check contents of keyring
|
||||||
|
|
||||||
@todo
|
@todo
|
||||||
Scenario: Open an encrypted journal with wrong password in keyring
|
Scenario: Open an encrypted journal with wrong password in keyring
|
||||||
# This should ask the user for the password after the keyring fails
|
# This should ask the user for the password after the keyring fails
|
||||||
|
|
||||||
@todo
|
|
||||||
Scenario: Open encrypted journal when keyring exists but fails
|
|
||||||
# This should ask the user for the password after the keyring fails
|
|
||||||
|
|
||||||
@todo
|
@todo
|
||||||
Scenario: Decrypt journal with password in keyring
|
Scenario: Decrypt journal with password in keyring
|
||||||
|
|
||||||
@todo
|
@todo
|
||||||
Scenario: Decrypt journal without a keyring
|
Scenario: Decrypt journal without a keyring
|
||||||
|
|
||||||
@todo
|
Scenario: Encrypt journal when keyring exists but fails
|
||||||
|
Given we use the config "simple.yaml"
|
||||||
|
And we have a failed keyring
|
||||||
|
When we run "jrnl --encrypt" and enter
|
||||||
|
"""
|
||||||
|
this password will not be saved in keyring
|
||||||
|
this password will not be saved in keyring
|
||||||
|
y
|
||||||
|
"""
|
||||||
|
Then we should see the message "Failed to retrieve keyring"
|
||||||
|
And we should get no error
|
||||||
|
And we should be prompted for a password
|
||||||
|
And the config for journal "default" should have "encrypt" set to "bool:True"
|
||||||
|
|
||||||
Scenario: Decrypt journal when keyring exists but fails
|
Scenario: Decrypt journal when keyring exists but fails
|
||||||
|
Given we use the config "encrypted.yaml"
|
||||||
|
And we have a failed keyring
|
||||||
|
When we run "jrnl --decrypt" and enter "bad doggie no biscuit"
|
||||||
|
Then we should see the message "Failed to retrieve keyring"
|
||||||
|
And we should get no error
|
||||||
|
And we should be prompted for a password
|
||||||
|
And we should see the message "Journal decrypted"
|
||||||
|
And the config for journal "default" should have "encrypt" set to "bool:False"
|
||||||
|
And the journal should have 2 entries
|
||||||
|
|
||||||
|
Scenario: Open encrypted journal when keyring exists but fails
|
||||||
# This should ask the user for the password after the keyring fails
|
# This should ask the user for the password after the keyring fails
|
||||||
|
Given we use the config "encrypted.yaml"
|
||||||
|
And we have a failed keyring
|
||||||
|
When we run "jrnl -n 1" and enter "bad doggie no biscuit"
|
||||||
|
Then we should see the message "Failed to retrieve keyring"
|
||||||
|
And we should get no error
|
||||||
|
And we should be prompted for a password
|
||||||
|
And the output should contain "2013-06-10 15:40 Life is good"
|
||||||
|
|
||||||
Scenario: Mistyping your password
|
Scenario: Mistyping your password
|
||||||
Given we use the config "simple.yaml"
|
Given we use the config "simple.yaml"
|
||||||
|
|
|
@ -69,21 +69,19 @@ class NoKeyring(keyring.backend.KeyringBackend):
|
||||||
|
|
||||||
class FailedKeyring(keyring.backend.KeyringBackend):
|
class FailedKeyring(keyring.backend.KeyringBackend):
|
||||||
"""
|
"""
|
||||||
A keyring that simulates an environment with a keyring that has passwords, but fails
|
A keyring that cannot be retrieved.
|
||||||
to return them.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
priority = 2
|
priority = 2
|
||||||
keys = defaultdict(dict)
|
|
||||||
|
|
||||||
def set_password(self, servicename, username, password):
|
def set_password(self, servicename, username, password):
|
||||||
self.keys[servicename][username] = password
|
raise keyring.errors.KeyringError
|
||||||
|
|
||||||
def get_password(self, servicename, username):
|
def get_password(self, servicename, username):
|
||||||
raise keyring.errors.NoKeyringError
|
raise keyring.errors.KeyringError
|
||||||
|
|
||||||
def delete_password(self, servicename, username):
|
def delete_password(self, servicename, username):
|
||||||
self.keys[servicename][username] = None
|
raise keyring.errors.KeyringError
|
||||||
|
|
||||||
|
|
||||||
# set a default keyring
|
# set a default keyring
|
||||||
|
@ -148,8 +146,12 @@ def use_password(context, password, num=1):
|
||||||
|
|
||||||
|
|
||||||
@given("we have a keyring")
|
@given("we have a keyring")
|
||||||
def set_keyring(context):
|
@given("we have a {type} keyring")
|
||||||
keyring.set_keyring(TestKeyring())
|
def set_keyring(context, type=""):
|
||||||
|
if type == "failed":
|
||||||
|
keyring.set_keyring(FailedKeyring())
|
||||||
|
else:
|
||||||
|
keyring.set_keyring(TestKeyring())
|
||||||
|
|
||||||
|
|
||||||
@given("we do not have a keyring")
|
@given("we do not have a keyring")
|
||||||
|
|
|
@ -176,7 +176,9 @@ def get_keychain(journal_name):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return keyring.get_password("jrnl", journal_name)
|
return keyring.get_password("jrnl", journal_name)
|
||||||
except RuntimeError:
|
except keyring.errors.KeyringError as e:
|
||||||
|
if not isinstance(e, keyring.errors.NoKeyringError):
|
||||||
|
print("Failed to retrieve keyring", file=sys.stderr)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
@ -186,13 +188,16 @@ def set_keychain(journal_name, password):
|
||||||
if password is None:
|
if password is None:
|
||||||
try:
|
try:
|
||||||
keyring.delete_password("jrnl", journal_name)
|
keyring.delete_password("jrnl", journal_name)
|
||||||
except keyring.errors.PasswordDeleteError:
|
except keyring.errors.KeyringError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
keyring.set_password("jrnl", journal_name, password)
|
keyring.set_password("jrnl", journal_name, password)
|
||||||
except keyring.errors.NoKeyringError:
|
except keyring.errors.KeyringError as e:
|
||||||
print(
|
if isinstance(e, keyring.errors.NoKeyringError):
|
||||||
"Keyring backend not found. Please install one of the supported backends by visiting: https://pypi.org/project/keyring/",
|
print(
|
||||||
file=sys.stderr,
|
"Keyring backend not found. Please install one of the supported backends by visiting: https://pypi.org/project/keyring/",
|
||||||
)
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("Failed to retrieve keyring", file=sys.stderr)
|
||||||
|
|
Loading…
Add table
Reference in a new issue