mirror of
https://github.com/jrnl-org/jrnl.git
synced 2025-05-20 13:08:31 +02:00
clean up some code around keyrings, clean up more tests
This commit is contained in:
parent
764227d2f2
commit
6ada26d629
2 changed files with 106 additions and 38 deletions
|
@ -1,8 +1,9 @@
|
|||
Feature: Encrypted journals
|
||||
Feature: Encrypting and decrypting journals
|
||||
|
||||
Scenario: Loading an encrypted journal
|
||||
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"
|
||||
Then we should be prompted for a password
|
||||
And the output should contain "2013-06-10 15:40 Life is good"
|
||||
|
||||
Scenario: Decrypting a journal
|
||||
|
@ -12,6 +13,13 @@
|
|||
And we should see the message "Journal decrypted"
|
||||
And the journal should have 2 entries
|
||||
|
||||
Scenario: Trying to decrypt an unencrypted journal
|
||||
Given we use the config "basic.yaml"
|
||||
When we run "jrnl --decrypt"
|
||||
Then the config for journal "default" should have "encrypt" set to "bool:False"
|
||||
And we should get no error
|
||||
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
|
||||
|
@ -23,10 +31,22 @@
|
|||
Then 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"
|
||||
Then we should be prompted for a password
|
||||
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
|
||||
"""
|
||||
Then we should be prompted for a password
|
||||
And we should see the message "Passwords did not match"
|
||||
And the config for journal "default" should not have "encrypt" set
|
||||
And the journal should have 2 entries
|
||||
|
||||
Scenario: Mistyping your password, then getting it right
|
||||
Given we use the config "basic.yaml"
|
||||
When we run "jrnl --encrypt" and enter
|
||||
"""
|
||||
|
@ -36,22 +56,23 @@
|
|||
swordfish
|
||||
n
|
||||
"""
|
||||
Then we should see the message "Passwords did not match"
|
||||
Then we should be prompted for a password
|
||||
And 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"
|
||||
Then we should be prompted for a 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 keyring
|
||||
Given we use the config "multiple.yaml"
|
||||
And we have a keyring
|
||||
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 the output should contain "2013-06-10 15:40 Life is good"
|
||||
|
@ -59,8 +80,8 @@
|
|||
|
||||
Scenario: Encrypt journal with no keyring backend and do not store in keyring
|
||||
Given we use the config "basic.yaml"
|
||||
When we disable the keychain
|
||||
And we run "jrnl test entry"
|
||||
And we do not have a keyring
|
||||
When we run "jrnl test entry"
|
||||
And we run "jrnl --encrypt" and enter
|
||||
"""
|
||||
password
|
||||
|
@ -71,8 +92,8 @@
|
|||
|
||||
Scenario: Encrypt journal with no keyring backend and do store in keyring
|
||||
Given we use the config "basic.yaml"
|
||||
When we disable the keychain
|
||||
And we run "jrnl test entry"
|
||||
And we do not have a keyring
|
||||
When we run "jrnl test entry"
|
||||
And we run "jrnl --encrypt" and enter
|
||||
"""
|
||||
password
|
||||
|
|
|
@ -64,8 +64,24 @@ class NoKeyring(keyring.backend.KeyringBackend):
|
|||
raise keyring.errors.NoKeyringError
|
||||
|
||||
|
||||
# set the keyring for keyring lib
|
||||
keyring.set_keyring(TestKeyring())
|
||||
class FailedKeyring(keyring.backend.KeyringBackend):
|
||||
"""
|
||||
A keyring that simulates an environment with a keyring that has passwords, but fails
|
||||
to return them.
|
||||
"""
|
||||
|
||||
priority = 2
|
||||
keys = defaultdict(dict)
|
||||
|
||||
|
||||
def set_password(self, servicename, username, password):
|
||||
self.keys[servicename][username] = password
|
||||
|
||||
def get_password(self, servicename, username):
|
||||
raise keyring.errors.NoKeyringError
|
||||
|
||||
def delete_password(self, servicename, username):
|
||||
self.keys[servicename][username] = None
|
||||
|
||||
|
||||
def ushlex(command):
|
||||
|
@ -93,6 +109,18 @@ def open_journal(journal_name="default"):
|
|||
return Journal.open_journal(journal_name, config)
|
||||
|
||||
|
||||
def read_value_from_string(string):
|
||||
if string[0] == "{":
|
||||
# Handle value being a dictionary
|
||||
return ast.literal_eval(string)
|
||||
|
||||
# Takes strings like "bool:true" or "int:32" and coerces them into proper type
|
||||
t, value = string.split(":")
|
||||
value = {"bool": lambda v: v.lower() == "true", "int": int, "str": str}[t](
|
||||
value
|
||||
)
|
||||
return value
|
||||
|
||||
@given('we use the config "{config_file}"')
|
||||
def set_config(context, config_file):
|
||||
full_path = os.path.join("features/configs", config_file)
|
||||
|
@ -103,6 +131,16 @@ def set_config(context, config_file):
|
|||
cf.write("version: {}".format(__version__))
|
||||
|
||||
|
||||
@given('we have a keyring')
|
||||
def set_keyring(context):
|
||||
keyring.set_keyring(TestKeyring())
|
||||
|
||||
|
||||
@given('we do not have a keyring')
|
||||
def disable_keyring(context):
|
||||
keyring.core.set_keyring(NoKeyring())
|
||||
|
||||
|
||||
@when('we change directory to "{path}"')
|
||||
def move_up_dir(context, path):
|
||||
os.chdir(path)
|
||||
|
@ -175,16 +213,22 @@ def matches_editor_arg(context, regex):
|
|||
def _mock_getpass(inputs):
|
||||
def prompt_return(prompt="Password: "):
|
||||
print(prompt)
|
||||
try:
|
||||
return next(inputs)
|
||||
except StopIteration:
|
||||
raise KeyboardInterrupt
|
||||
|
||||
return prompt_return
|
||||
|
||||
|
||||
def _mock_input(inputs):
|
||||
def prompt_return(prompt=""):
|
||||
try:
|
||||
val = next(inputs)
|
||||
print(prompt, val)
|
||||
return val
|
||||
except StopIteration:
|
||||
raise KeyboardInterrupt
|
||||
|
||||
return prompt_return
|
||||
|
||||
|
@ -247,11 +291,16 @@ def inputs_were_called(context):
|
|||
)
|
||||
|
||||
|
||||
@then("we were prompted for a password")
|
||||
def password_was_called(context, method):
|
||||
@then("we should be prompted for a password")
|
||||
def password_was_called(context):
|
||||
assert context.getpass.called
|
||||
|
||||
|
||||
@then("we should not be prompted for a password")
|
||||
def password_was_not_called(context):
|
||||
assert not context.getpass.called
|
||||
|
||||
|
||||
@then("all input was used")
|
||||
def all_input_was_used(context):
|
||||
# all inputs were used (ignore if empty string)
|
||||
|
@ -292,16 +341,11 @@ def load_template(context, filename):
|
|||
plugins.__exporter_types[exporter.names[0]] = exporter
|
||||
|
||||
|
||||
@when('we set the keychain password of "{journal}" to "{password}"')
|
||||
def set_keychain(context, journal, password):
|
||||
@when('we set the keyring password of "{journal}" to "{password}"')
|
||||
def set_keyring(context, journal, password):
|
||||
keyring.set_password("jrnl", journal, password)
|
||||
|
||||
|
||||
@when("we disable the keychain")
|
||||
def disable_keychain(context):
|
||||
keyring.core.set_keyring(NoKeyring())
|
||||
|
||||
|
||||
@then("we should get an error")
|
||||
def has_error(context):
|
||||
assert context.exit_status != 0, context.exit_status
|
||||
|
@ -397,8 +441,8 @@ def check_not_journal_content(context, text, journal_name="default"):
|
|||
|
||||
@then('journal "{journal_name}" should not exist')
|
||||
def journal_doesnt_exist(context, journal_name="default"):
|
||||
with open(install.CONFIG_FILE_PATH) as config_file:
|
||||
config = yaml.load(config_file, Loader=yaml.FullLoader)
|
||||
config = load_config(install.CONFIG_FILE_PATH)
|
||||
|
||||
journal_path = config["journals"][journal_name]
|
||||
assert not os.path.exists(journal_path)
|
||||
|
||||
|
@ -407,23 +451,26 @@ def journal_doesnt_exist(context, journal_name="default"):
|
|||
@then('the config should have "{key}" set to "{value}"')
|
||||
@then('the config for journal "{journal}" should have "{key}" set to "{value}"')
|
||||
def config_var(context, key, value="", journal=None):
|
||||
value = value or context.text or ""
|
||||
if not value[0] == "{":
|
||||
t, value = value.split(":")
|
||||
value = {"bool": lambda v: v.lower() == "true", "int": int, "str": str}[t](
|
||||
value
|
||||
)
|
||||
else:
|
||||
# Handle value being a dictionary
|
||||
value = ast.literal_eval(value)
|
||||
|
||||
value = read_value_from_string(value or context.text or "")
|
||||
config = load_config(install.CONFIG_FILE_PATH)
|
||||
|
||||
if journal:
|
||||
config = config["journals"][journal]
|
||||
|
||||
assert key in config
|
||||
assert config[key] == value
|
||||
|
||||
|
||||
@then('the config for journal "{journal}" should not have "{key}" set')
|
||||
def config_var(context, key, value="", journal=None):
|
||||
config = load_config(install.CONFIG_FILE_PATH)
|
||||
|
||||
if journal:
|
||||
config = config["journals"][journal]
|
||||
|
||||
assert key not in config
|
||||
|
||||
|
||||
@then("the journal should have {number:d} entries")
|
||||
@then("the journal should have {number:d} entry")
|
||||
@then('journal "{journal_name}" should have {number:d} entries')
|
||||
|
|
Loading…
Add table
Reference in a new issue