Marco Ricci commited on 2024-12-20 11:39:45
Zeige 3 geänderte Dateien mit 73 Einfügungen und 8 Löschungen.
Correctly handle empty `key` settings in the `vault` configuration: if at the global level, it is equivalent to eliding the setting, but at the service level, it explicitly disables key usage (even if a global key is set). This implies that both cases need different cleanup steps. Harmonize the warning message for ineffective passphrases for services: always include the affected service name. If during configuration import we find a key shadowing a passphrase, issue the same ineffective passphrase warning as would happen during interactive configuration or interactive use. (The tests need minor modifications and reparametrization to continue to cover all warning cases.)
... | ... |
@@ -517,7 +517,7 @@ def clean_up_falsy_vault_config_values( # noqa: C901,PLR0912 |
517 | 517 |
) |
518 | 518 |
) |
519 | 519 |
service_obj[key] = '' |
520 |
- elif key in {'notes', 'key'}: |
|
520 |
+ elif key == 'notes': |
|
521 | 521 |
if not js_truthiness(value): |
522 | 522 |
cleanup_completed.append( |
523 | 523 |
CleanupStep( |
... | ... |
@@ -525,6 +525,22 @@ def clean_up_falsy_vault_config_values( # noqa: C901,PLR0912 |
525 | 525 |
) |
526 | 526 |
) |
527 | 527 |
service_obj.pop(key) |
528 |
+ elif key == 'key': |
|
529 |
+ if not js_truthiness(value): |
|
530 |
+ if path == ['global']: |
|
531 |
+ cleanup_completed.append( |
|
532 |
+ CleanupStep( |
|
533 |
+ (*path, key), service_obj[key], 'remove', None |
|
534 |
+ ) |
|
535 |
+ ) |
|
536 |
+ service_obj.pop(key) |
|
537 |
+ else: |
|
538 |
+ cleanup_completed.append( |
|
539 |
+ CleanupStep( |
|
540 |
+ (*path, key), service_obj[key], 'replace', '' |
|
541 |
+ ) |
|
542 |
+ ) |
|
543 |
+ service_obj[key] = '' |
|
528 | 544 |
elif key == 'length': |
529 | 545 |
if not js_truthiness(value): |
530 | 546 |
cleanup_completed.append( |
... | ... |
@@ -2025,6 +2025,29 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915 |
2025 | 2025 |
) |
2026 | 2026 |
except AssertionError as e: |
2027 | 2027 |
err('The configuration file is invalid. ' + str(e)) |
2028 |
+ global_obj = maybe_config.get('global', {}) |
|
2029 |
+ has_key = _types.js_truthiness(global_obj.get('key')) |
|
2030 |
+ has_phrase = _types.js_truthiness(global_obj.get('phrase')) |
|
2031 |
+ if has_key and has_phrase: |
|
2032 |
+ logger.warning( |
|
2033 |
+ 'Setting a global passphrase is ineffective ' |
|
2034 |
+ 'because a key is also set.' |
|
2035 |
+ ) |
|
2036 |
+ for service_name, service_obj in maybe_config['services'].items(): |
|
2037 |
+ has_key = _types.js_truthiness( |
|
2038 |
+ service_obj.get('key') |
|
2039 |
+ ) or _types.js_truthiness(global_obj.get('key')) |
|
2040 |
+ has_phrase = _types.js_truthiness( |
|
2041 |
+ service_obj.get('phrase') |
|
2042 |
+ ) or _types.js_truthiness(global_obj.get('phrase')) |
|
2043 |
+ if has_key and has_phrase: |
|
2044 |
+ logger.warning( |
|
2045 |
+ ( |
|
2046 |
+ 'Setting a service passphrase is ineffective ' |
|
2047 |
+ 'because a key is also set: %s' |
|
2048 |
+ ), |
|
2049 |
+ json.dumps(service_name), |
|
2050 |
+ ) |
|
2028 | 2051 |
if overwrite_config: |
2029 | 2052 |
put_config(maybe_config) |
2030 | 2053 |
else: |
... | ... |
@@ -2153,12 +2176,18 @@ def derivepassphrase_vault( # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915 |
2153 | 2176 |
except AssertionError as e: |
2154 | 2177 |
err('The configuration file is invalid. ' + str(e)) |
2155 | 2178 |
if 'key' in settings: |
2179 |
+ if service: |
|
2156 | 2180 |
logger.warning( |
2157 | 2181 |
( |
2158 |
- 'Setting a %s passphrase is ineffective ' |
|
2159 |
- 'because a key is also set.' |
|
2182 |
+ 'Setting a service passphrase is ineffective ' |
|
2183 |
+ 'because a key is also set: %s' |
|
2160 | 2184 |
), |
2161 |
- settings_type, |
|
2185 |
+ json.dumps(service), |
|
2186 |
+ ) |
|
2187 |
+ else: |
|
2188 |
+ logger.warning( |
|
2189 |
+ 'Setting a global passphrase is ineffective ' |
|
2190 |
+ 'because a key is also set.' |
|
2162 | 2191 |
) |
2163 | 2192 |
if not view.maps[0]: |
2164 | 2193 |
settings_type = 'service' if service else 'global' |
... | ... |
@@ -221,7 +221,7 @@ def is_harmless_config_import_warning(record: tuple[str, int, str]) -> bool: |
221 | 221 |
), |
222 | 222 |
( |
223 | 223 |
'Setting a service passphrase is ineffective ' |
224 |
- 'because a key is also set.' |
|
224 |
+ 'because a key is also set:' |
|
225 | 225 |
), |
226 | 226 |
] |
227 | 227 |
return any(tests.warning_emitted(w, [record]) for w in possible_warnings) |
... | ... |
@@ -479,8 +479,17 @@ class TestCLI: |
479 | 479 |
), 'expected known output' |
480 | 480 |
|
481 | 481 |
@pytest.mark.parametrize( |
482 |
- 'config', |
|
482 |
+ ['config', 'command_line'], |
|
483 | 483 |
[ |
484 |
+ pytest.param( |
|
485 |
+ { |
|
486 |
+ 'global': {'key': DUMMY_KEY1_B64}, |
|
487 |
+ 'services': {}, |
|
488 |
+ }, |
|
489 |
+ ['--config', '-p'], |
|
490 |
+ id='global', |
|
491 |
+ ), |
|
492 |
+ pytest.param( |
|
484 | 493 |
{ |
485 | 494 |
'services': { |
486 | 495 |
DUMMY_SERVICE: { |
... | ... |
@@ -489,18 +498,26 @@ class TestCLI: |
489 | 498 |
}, |
490 | 499 |
}, |
491 | 500 |
}, |
501 |
+ ['--config', '-p', '--', DUMMY_SERVICE], |
|
502 |
+ id='service', |
|
503 |
+ ), |
|
504 |
+ pytest.param( |
|
492 | 505 |
{ |
493 | 506 |
'global': {'key': DUMMY_KEY1_B64}, |
494 | 507 |
'services': {DUMMY_SERVICE: DUMMY_CONFIG_SETTINGS.copy()}, |
495 | 508 |
}, |
509 |
+ ['--config', '-p', '--', DUMMY_SERVICE], |
|
510 |
+ id='service-over-global', |
|
511 |
+ ), |
|
496 | 512 |
], |
497 | 513 |
) |
498 |
- def test_206_setting_service_phrase_thus_overriding_key_in_config( |
|
514 |
+ def test_206_setting_phrase_thus_overriding_key_in_config( |
|
499 | 515 |
self, |
500 | 516 |
monkeypatch: pytest.MonkeyPatch, |
501 | 517 |
running_ssh_agent: tests.RunningSSHAgentInfo, |
502 | 518 |
caplog: pytest.LogCaptureFixture, |
503 | 519 |
config: _types.VaultConfig, |
520 |
+ command_line: list[str], |
|
504 | 521 |
) -> None: |
505 | 522 |
with monkeypatch.context(): |
506 | 523 |
monkeypatch.setenv('SSH_AUTH_SOCK', running_ssh_agent.socket) |
... | ... |
@@ -516,7 +533,7 @@ class TestCLI: |
516 | 533 |
): |
517 | 534 |
_result = runner.invoke( |
518 | 535 |
cli.derivepassphrase_vault, |
519 |
- ['--config', '-p', '--', DUMMY_SERVICE], |
|
536 |
+ command_line, |
|
520 | 537 |
input=DUMMY_PASSPHRASE, |
521 | 538 |
catch_exceptions=False, |
522 | 539 |
) |
... | ... |
@@ -529,6 +546,9 @@ class TestCLI: |
529 | 546 |
assert tests.warning_emitted( |
530 | 547 |
'Setting a service passphrase is ineffective ', |
531 | 548 |
caplog.record_tuples, |
549 |
+ ) or tests.warning_emitted( |
|
550 |
+ 'Setting a global passphrase is ineffective ', |
|
551 |
+ caplog.record_tuples, |
|
532 | 552 |
), 'expected known warning message' |
533 | 553 |
assert all(map(is_warning_line, result.stderr.splitlines(True))) |
534 | 554 |
assert all( |
535 | 555 |