Compare commits

...

7 Commits

Author SHA1 Message Date
248999c23e Final 2025-04-03 17:10:52 +00:00
4e55547235 Command Restructuring 2025-04-02 15:35:12 +00:00
96ec66de79 Applied changes 2025-04-02 10:50:25 +00:00
7a0c56878b Applied changes 2025-04-02 10:47:35 +00:00
92ebc77597 2nd Draft 2025-03-30 16:19:56 +00:00
863c954144 added schema.yaml 2025-03-30 15:57:42 +00:00
f7e4d38762 First Draft 2025-03-30 14:02:56 +00:00
3 changed files with 132 additions and 20 deletions

View File

@@ -2683,6 +2683,20 @@ properties:
description: |
Command to use instead of "keepassxc-cli".
example: /usr/local/bin/keepassxc-cli
key_file:
type: string
description: |
Path to a key file for unlocking the KeePassXC database.
example: /path/to/keyfile
yubikey:
type: string
description: |
YubiKey slot and optional serial number used to access the KeePassXC database.
Format: "<slot[:serial]>", where:
- <slot> is the YubiKey slot number (e.g., `1` or `2`).
- <serial> (optional) is the YubiKey's serial number (e.g., `1:7370001`).
example: "1:7370001"
description: |
Configuration for integration with the KeePassXC password manager.
default_actions:

View File

@@ -11,34 +11,31 @@ def load_credential(hook_config, config, credential_parameters):
'''
Given the hook configuration dict, the configuration dict, and a credential parameters tuple
containing a KeePassXC database path and an attribute name to load, run keepassxc-cli to fetch
the corresponidng KeePassXC credential and return it.
the corresponding KeePassXC credential and return it.
Raise ValueError if keepassxc-cli can't retrieve the credential.
'''
try:
(database_path, attribute_name) = credential_parameters
except ValueError:
path_and_name = ' '.join(credential_parameters)
raise ValueError(
f'Cannot load credential with invalid KeePassXC database path and attribute name: "{path_and_name}"'
)
raise ValueError( f'Invalid KeePassXC credential parameters: {credential_parameters}')
expanded_database_path = os.path.expanduser(database_path)
if not os.path.exists(expanded_database_path):
raise ValueError(
f'Cannot load credential because KeePassXC database path does not exist: {database_path}'
)
return borgmatic.execute.execute_command_and_capture_output(
raise ValueError( f'KeePassXC database path does not exist: {database_path}')
# Build the keepassxc-cli command
command = (
tuple(shlex.split((hook_config or {}).get('keepassxc_cli_command', 'keepassxc-cli')))
+ (
'show',
'--show-protected',
'--attributes',
'Password',
expanded_database_path,
attribute_name,
)
).rstrip(os.linesep)
+ ('show', '--show-protected', '--attributes', 'Password')
+ (('--key-file', hook_config['key_file']) if 'key_file' in hook_config else ())
+ (('--yubikey', hook_config['yubikey']) if 'yubikey' in hook_config else ())
+ (expanded_database_path, attribute_name) # Ensure database & entry are last
)
try:
return borgmatic.execute.execute_command_and_capture_output(command).rstrip(os.linesep)
except Exception as e:
raise ValueError(f'Failed to retrieve credential: {e}')

View File

@@ -116,3 +116,104 @@ def test_load_credential_with_expanded_directory_with_present_database_fetches_p
)
== 'password'
)
def test_load_credential_with_key_file():
flexmock(module.os.path).should_receive('expanduser').with_args('database.kdbx').and_return(
'database.kdbx'
)
flexmock(module.os.path).should_receive('exists').and_return(True)
flexmock(module.borgmatic.execute).should_receive(
'execute_command_and_capture_output'
).with_args(
(
'keepassxc-cli',
'show',
'--show-protected',
'--attributes',
'Password',
'--key-file',
'/path/to/keyfile',
'database.kdbx',
'mypassword',
)
).and_return(
'password'
).once()
assert (
module.load_credential(
hook_config={'key_file': '/path/to/keyfile'},
config={},
credential_parameters=('database.kdbx', 'mypassword'),
)
== 'password'
)
def test_load_credential_with_yubikey():
flexmock(module.os.path).should_receive('expanduser').with_args('database.kdbx').and_return(
'database.kdbx'
)
flexmock(module.os.path).should_receive('exists').and_return(True)
flexmock(module.borgmatic.execute).should_receive(
'execute_command_and_capture_output'
).with_args(
(
'keepassxc-cli',
'show',
'--show-protected',
'--attributes',
'Password',
'--yubikey',
'1:7370001',
'database.kdbx',
'mypassword',
)
).and_return(
'password'
).once()
assert (
module.load_credential(
hook_config={'yubikey': '1:7370001'},
config={},
credential_parameters=('database.kdbx', 'mypassword'),
)
== 'password'
)
def test_load_credential_with_key_file_and_yubikey():
flexmock(module.os.path).should_receive('expanduser').with_args('database.kdbx').and_return(
'database.kdbx'
)
flexmock(module.os.path).should_receive('exists').and_return(True)
flexmock(module.borgmatic.execute).should_receive(
'execute_command_and_capture_output'
).with_args(
(
'keepassxc-cli',
'show',
'--show-protected',
'--attributes',
'Password',
'--key-file',
'/path/to/keyfile',
'--yubikey',
'2',
'database.kdbx',
'mypassword',
)
).and_return(
'password'
).once()
assert (
module.load_credential(
hook_config={'key_file': '/path/to/keyfile', 'yubikey': '2'},
config={},
credential_parameters=('database.kdbx', 'mypassword'),
)
== 'password'
)