Marco Ricci commited on 2024-08-31 13:32:33
Zeige 7 geänderte Dateien mit 415 Einfügungen und 410 Löschungen.
Also replicate this module structure in the tests, which allows us to use `pytest.importorskip` to deal with missing `cryptography` support.
... | ... |
@@ -2,33 +2,18 @@ |
2 | 2 |
# |
3 | 3 |
# SPDX-License-Identifier: MIT |
4 | 4 |
|
5 |
-"""Command-line interface for derivepassphrase_export.""" |
|
5 |
+"""Foreign configuration exporter for derivepassphrase.""" |
|
6 | 6 |
|
7 | 7 |
from __future__ import annotations |
8 | 8 |
|
9 |
-import base64 |
|
10 |
-import importlib |
|
11 |
-import json |
|
12 |
-import logging |
|
13 | 9 |
import os |
14 |
-from typing import TYPE_CHECKING, Any, Literal |
|
15 |
- |
|
16 |
-import click |
|
17 |
-from typing_extensions import assert_never |
|
18 | 10 |
|
19 | 11 |
import derivepassphrase as dpp |
20 |
-from derivepassphrase import _types |
|
21 |
- |
|
22 |
-if TYPE_CHECKING: |
|
23 |
- import types |
|
24 |
- from collections.abc import Sequence |
|
25 | 12 |
|
26 | 13 |
__author__ = dpp.__author__ |
27 | 14 |
__version__ = dpp.__version__ |
28 | 15 |
|
29 |
-__all__ = ('derivepassphrase_export',) |
|
30 |
- |
|
31 |
-PROG_NAME = 'derivepassphrase_export' |
|
16 |
+__all__ = () |
|
32 | 17 |
|
33 | 18 |
|
34 | 19 |
def get_vault_key() -> bytes: |
... | ... |
@@ -86,139 +71,3 @@ def get_vault_path() -> str | bytes | os.PathLike: |
86 | 71 |
msg = 'Cannot determine home directory' |
87 | 72 |
raise RuntimeError(msg) |
88 | 73 |
return result |
89 |
- |
|
90 |
- |
|
91 |
-@click.command( |
|
92 |
- context_settings={'help_option_names': ['-h', '--help']}, |
|
93 |
-) |
|
94 |
-@click.option( |
|
95 |
- '-f', |
|
96 |
- '--format', |
|
97 |
- 'formats', |
|
98 |
- metavar='FMT', |
|
99 |
- multiple=True, |
|
100 |
- default=('v0.3', 'v0.2', 'storeroom'), |
|
101 |
- type=click.Choice(['v0.2', 'v0.3', 'storeroom']), |
|
102 |
- help='try the following storage formats, in order (default: v0.3, v0.2)', |
|
103 |
-) |
|
104 |
-@click.option( |
|
105 |
- '-k', |
|
106 |
- '--key', |
|
107 |
- metavar='K', |
|
108 |
- help=( |
|
109 |
- 'use K as the storage master key ' |
|
110 |
- '(default: check the `VAULT_KEY`, `LOGNAME`, `USER` or ' |
|
111 |
- '`USERNAME` environment variables)' |
|
112 |
- ), |
|
113 |
-) |
|
114 |
-@click.argument('path', metavar='PATH', required=True) |
|
115 |
-@click.pass_context |
|
116 |
-def derivepassphrase_export( |
|
117 |
- ctx: click.Context, |
|
118 |
- /, |
|
119 |
- *, |
|
120 |
- path: str | bytes | os.PathLike[str], |
|
121 |
- formats: Sequence[Literal['v0.2', 'v0.3', 'storeroom']] = (), |
|
122 |
- key: str | bytes | None = None, |
|
123 |
-) -> None: |
|
124 |
- """Export a vault-native configuration to standard output. |
|
125 |
- |
|
126 |
- Read the vault-native configuration at PATH, extract all information |
|
127 |
- from it, and export the resulting configuration to standard output. |
|
128 |
- Depending on the configuration format, this may either be a file or |
|
129 |
- a directory. |
|
130 |
- |
|
131 |
- If PATH is explicitly given as `VAULT_PATH`, then use the |
|
132 |
- `VAULT_PATH` environment variable to determine the correct path. |
|
133 |
- (Use `./VAULT_PATH` or similar to indicate a file/directory actually |
|
134 |
- named `VAULT_PATH`.) |
|
135 |
- |
|
136 |
- """ |
|
137 |
- |
|
138 |
- def load_data( |
|
139 |
- fmt: Literal['v0.2', 'v0.3', 'storeroom'], |
|
140 |
- path: str | bytes | os.PathLike[str], |
|
141 |
- key: bytes, |
|
142 |
- ) -> Any: |
|
143 |
- contents: bytes |
|
144 |
- module: types.ModuleType |
|
145 |
- match fmt: |
|
146 |
- case 'v0.2': |
|
147 |
- module = importlib.import_module( |
|
148 |
- 'derivepassphrase.exporter.vault_v03_and_below' |
|
149 |
- ) |
|
150 |
- if module.STUBBED: |
|
151 |
- raise ModuleNotFoundError |
|
152 |
- with open(path, 'rb') as infile: |
|
153 |
- contents = base64.standard_b64decode(infile.read()) |
|
154 |
- return module.V02Reader(contents, key).run() |
|
155 |
- case 'v0.3': |
|
156 |
- module = importlib.import_module( |
|
157 |
- 'derivepassphrase.exporter.vault_v03_and_below' |
|
158 |
- ) |
|
159 |
- if module.STUBBED: |
|
160 |
- raise ModuleNotFoundError |
|
161 |
- with open(path, 'rb') as infile: |
|
162 |
- contents = base64.standard_b64decode(infile.read()) |
|
163 |
- return module.V03Reader(contents, key).run() |
|
164 |
- case 'storeroom': |
|
165 |
- module = importlib.import_module( |
|
166 |
- 'derivepassphrase.exporter.storeroom' |
|
167 |
- ) |
|
168 |
- if module.STUBBED: |
|
169 |
- raise ModuleNotFoundError |
|
170 |
- return module.export_storeroom_data(path, key) |
|
171 |
- case _: # pragma: no cover |
|
172 |
- assert_never(fmt) |
|
173 |
- |
|
174 |
- logging.basicConfig() |
|
175 |
- if path in {'VAULT_PATH', b'VAULT_PATH'}: |
|
176 |
- path = get_vault_path() |
|
177 |
- if key is None: |
|
178 |
- key = get_vault_key() |
|
179 |
- elif isinstance(key, str): # pragma: no branch |
|
180 |
- key = key.encode('utf-8') |
|
181 |
- for fmt in formats: |
|
182 |
- try: |
|
183 |
- config = load_data(fmt, path, key) |
|
184 |
- except ( |
|
185 |
- IsADirectoryError, |
|
186 |
- NotADirectoryError, |
|
187 |
- ValueError, |
|
188 |
- RuntimeError, |
|
189 |
- ): |
|
190 |
- logging.info('Cannot load as %s: %s', fmt, path) |
|
191 |
- continue |
|
192 |
- except OSError as exc: |
|
193 |
- click.echo( |
|
194 |
- ( |
|
195 |
- f'{PROG_NAME}: ERROR: Cannot parse {path!r} as ' |
|
196 |
- f'a valid config: {exc.strerror}: {exc.filename!r}' |
|
197 |
- ), |
|
198 |
- err=True, |
|
199 |
- ) |
|
200 |
- ctx.exit(1) |
|
201 |
- except ModuleNotFoundError: |
|
202 |
- # TODO(the-13th-letter): Use backslash continuation. |
|
203 |
- # https://github.com/nedbat/coveragepy/issues/1836 |
|
204 |
- msg = f""" |
|
205 |
-{PROG_NAME}: ERROR: Cannot load the required Python module "cryptography". |
|
206 |
-{PROG_NAME}: INFO: pip users: see the "export" extra. |
|
207 |
-""".lstrip('\n') |
|
208 |
- click.echo(msg, nl=False, err=True) |
|
209 |
- ctx.exit(1) |
|
210 |
- else: |
|
211 |
- if not _types.is_vault_config(config): |
|
212 |
- click.echo( |
|
213 |
- f'{PROG_NAME}: ERROR: Invalid vault config: {config!r}', |
|
214 |
- err=True, |
|
215 |
- ) |
|
216 |
- ctx.exit(1) |
|
217 |
- click.echo(json.dumps(config, indent=2, sort_keys=True)) |
|
218 |
- break |
|
219 |
- else: |
|
220 |
- click.echo( |
|
221 |
- f'{PROG_NAME}: ERROR: Cannot parse {path!r} as a valid config.', |
|
222 |
- err=True, |
|
223 |
- ) |
|
224 |
- ctx.exit(1) |
... | ... |
@@ -0,0 +1,167 @@ |
1 |
+# SPDX-FileCopyrightText: 2024 Marco Ricci <m@the13thletter.info> |
|
2 |
+# |
|
3 |
+# SPDX-License-Identifier: MIT |
|
4 |
+ |
|
5 |
+"""Command-line interface for derivepassphrase_export.""" |
|
6 |
+ |
|
7 |
+from __future__ import annotations |
|
8 |
+ |
|
9 |
+import base64 |
|
10 |
+import importlib |
|
11 |
+import json |
|
12 |
+import logging |
|
13 |
+from typing import TYPE_CHECKING, Any, Literal |
|
14 |
+ |
|
15 |
+import click |
|
16 |
+from typing_extensions import assert_never |
|
17 |
+ |
|
18 |
+import derivepassphrase as dpp |
|
19 |
+from derivepassphrase import _types, exporter |
|
20 |
+ |
|
21 |
+if TYPE_CHECKING: |
|
22 |
+ import os |
|
23 |
+ import types |
|
24 |
+ from collections.abc import Sequence |
|
25 |
+ |
|
26 |
+__author__ = dpp.__author__ |
|
27 |
+__version__ = dpp.__version__ |
|
28 |
+ |
|
29 |
+__all__ = ('derivepassphrase_export',) |
|
30 |
+ |
|
31 |
+PROG_NAME = 'derivepassphrase_export' |
|
32 |
+ |
|
33 |
+ |
|
34 |
+@click.command( |
|
35 |
+ context_settings={'help_option_names': ['-h', '--help']}, |
|
36 |
+) |
|
37 |
+@click.option( |
|
38 |
+ '-f', |
|
39 |
+ '--format', |
|
40 |
+ 'formats', |
|
41 |
+ metavar='FMT', |
|
42 |
+ multiple=True, |
|
43 |
+ default=('v0.3', 'v0.2', 'storeroom'), |
|
44 |
+ type=click.Choice(['v0.2', 'v0.3', 'storeroom']), |
|
45 |
+ help='try the following storage formats, in order (default: v0.3, v0.2)', |
|
46 |
+) |
|
47 |
+@click.option( |
|
48 |
+ '-k', |
|
49 |
+ '--key', |
|
50 |
+ metavar='K', |
|
51 |
+ help=( |
|
52 |
+ 'use K as the storage master key ' |
|
53 |
+ '(default: check the `VAULT_KEY`, `LOGNAME`, `USER` or ' |
|
54 |
+ '`USERNAME` environment variables)' |
|
55 |
+ ), |
|
56 |
+) |
|
57 |
+@click.argument('path', metavar='PATH', required=True) |
|
58 |
+@click.pass_context |
|
59 |
+def derivepassphrase_export( |
|
60 |
+ ctx: click.Context, |
|
61 |
+ /, |
|
62 |
+ *, |
|
63 |
+ path: str | bytes | os.PathLike[str], |
|
64 |
+ formats: Sequence[Literal['v0.2', 'v0.3', 'storeroom']] = (), |
|
65 |
+ key: str | bytes | None = None, |
|
66 |
+) -> None: |
|
67 |
+ """Export a vault-native configuration to standard output. |
|
68 |
+ |
|
69 |
+ Read the vault-native configuration at PATH, extract all information |
|
70 |
+ from it, and export the resulting configuration to standard output. |
|
71 |
+ Depending on the configuration format, this may either be a file or |
|
72 |
+ a directory. |
|
73 |
+ |
|
74 |
+ If PATH is explicitly given as `VAULT_PATH`, then use the |
|
75 |
+ `VAULT_PATH` environment variable to determine the correct path. |
|
76 |
+ (Use `./VAULT_PATH` or similar to indicate a file/directory actually |
|
77 |
+ named `VAULT_PATH`.) |
|
78 |
+ |
|
79 |
+ """ |
|
80 |
+ |
|
81 |
+ def load_data( |
|
82 |
+ fmt: Literal['v0.2', 'v0.3', 'storeroom'], |
|
83 |
+ path: str | bytes | os.PathLike[str], |
|
84 |
+ key: bytes, |
|
85 |
+ ) -> Any: |
|
86 |
+ contents: bytes |
|
87 |
+ module: types.ModuleType |
|
88 |
+ match fmt: |
|
89 |
+ case 'v0.2': |
|
90 |
+ module = importlib.import_module( |
|
91 |
+ 'derivepassphrase.exporter.vault_v03_and_below' |
|
92 |
+ ) |
|
93 |
+ if module.STUBBED: |
|
94 |
+ raise ModuleNotFoundError |
|
95 |
+ with open(path, 'rb') as infile: |
|
96 |
+ contents = base64.standard_b64decode(infile.read()) |
|
97 |
+ return module.V02Reader(contents, key).run() |
|
98 |
+ case 'v0.3': |
|
99 |
+ module = importlib.import_module( |
|
100 |
+ 'derivepassphrase.exporter.vault_v03_and_below' |
|
101 |
+ ) |
|
102 |
+ if module.STUBBED: |
|
103 |
+ raise ModuleNotFoundError |
|
104 |
+ with open(path, 'rb') as infile: |
|
105 |
+ contents = base64.standard_b64decode(infile.read()) |
|
106 |
+ return module.V03Reader(contents, key).run() |
|
107 |
+ case 'storeroom': |
|
108 |
+ module = importlib.import_module( |
|
109 |
+ 'derivepassphrase.exporter.storeroom' |
|
110 |
+ ) |
|
111 |
+ if module.STUBBED: |
|
112 |
+ raise ModuleNotFoundError |
|
113 |
+ return module.export_storeroom_data(path, key) |
|
114 |
+ case _: # pragma: no cover |
|
115 |
+ assert_never(fmt) |
|
116 |
+ |
|
117 |
+ logging.basicConfig() |
|
118 |
+ if path in {'VAULT_PATH', b'VAULT_PATH'}: |
|
119 |
+ path = exporter.get_vault_path() |
|
120 |
+ if key is None: |
|
121 |
+ key = exporter.get_vault_key() |
|
122 |
+ elif isinstance(key, str): # pragma: no branch |
|
123 |
+ key = key.encode('utf-8') |
|
124 |
+ for fmt in formats: |
|
125 |
+ try: |
|
126 |
+ config = load_data(fmt, path, key) |
|
127 |
+ except ( |
|
128 |
+ IsADirectoryError, |
|
129 |
+ NotADirectoryError, |
|
130 |
+ ValueError, |
|
131 |
+ RuntimeError, |
|
132 |
+ ): |
|
133 |
+ logging.info('Cannot load as %s: %s', fmt, path) |
|
134 |
+ continue |
|
135 |
+ except OSError as exc: |
|
136 |
+ click.echo( |
|
137 |
+ ( |
|
138 |
+ f'{PROG_NAME}: ERROR: Cannot parse {path!r} as ' |
|
139 |
+ f'a valid config: {exc.strerror}: {exc.filename!r}' |
|
140 |
+ ), |
|
141 |
+ err=True, |
|
142 |
+ ) |
|
143 |
+ ctx.exit(1) |
|
144 |
+ except ModuleNotFoundError: |
|
145 |
+ # TODO(the-13th-letter): Use backslash continuation. |
|
146 |
+ # https://github.com/nedbat/coveragepy/issues/1836 |
|
147 |
+ msg = f""" |
|
148 |
+{PROG_NAME}: ERROR: Cannot load the required Python module "cryptography". |
|
149 |
+{PROG_NAME}: INFO: pip users: see the "export" extra. |
|
150 |
+""".lstrip('\n') |
|
151 |
+ click.echo(msg, nl=False, err=True) |
|
152 |
+ ctx.exit(1) |
|
153 |
+ else: |
|
154 |
+ if not _types.is_vault_config(config): |
|
155 |
+ click.echo( |
|
156 |
+ f'{PROG_NAME}: ERROR: Invalid vault config: {config!r}', |
|
157 |
+ err=True, |
|
158 |
+ ) |
|
159 |
+ ctx.exit(1) |
|
160 |
+ click.echo(json.dumps(config, indent=2, sort_keys=True)) |
|
161 |
+ break |
|
162 |
+ else: |
|
163 |
+ click.echo( |
|
164 |
+ f'{PROG_NAME}: ERROR: Cannot parse {path!r} as a valid config.', |
|
165 |
+ err=True, |
|
166 |
+ ) |
|
167 |
+ ctx.exit(1) |
... | ... |
@@ -350,6 +350,99 @@ DUMMY_PHRASE_FROM_KEY1_RAW = ( |
350 | 350 |
) |
351 | 351 |
DUMMY_PHRASE_FROM_KEY1 = b'8JgZgGwal9UmA27M42WPhmYHExkTCSEzM/nkNlMdr/0NCB/s+HObjF9VORZ8U1QsHlK7MO1/ieIvaVFV2J6mAg==' # noqa: E501 |
352 | 352 |
|
353 |
+VAULT_MASTER_KEY = 'vault key' |
|
354 |
+VAULT_V02_CONFIG = 'P7xeh5y4jmjpJ2pFq4KUcTVoaE9ZOEkwWmpVTURSSWQxbGt6emN4aFE4eFM3anVPbDRNTGpOLzY3eDF5aE1YTm5LNWh5Q1BwWTMwM3M5S083MWRWRFlmOXNqSFJNcStGMWFOS3c2emhiOUNNenZYTmNNMnZxaUErdlRoOGF2ZHdGT1ZLNTNLOVJQcU9jWmJrR3g5N09VcVBRZ0ZnSFNUQy9HdFVWWnFteVhRVkY3MHNBdnF2ZWFEbFBseWRGelE1c3BFTnVUckRQdWJSL29wNjFxd2Y2ZVpob3VyVzRod3FKTElTenJ1WTZacTJFOFBtK3BnVzh0QWVxcWtyWFdXOXYyenNQeFNZbWt1MDU2Vm1kVGtISWIxWTBpcWRFbyswUVJudVVhZkVlNVpGWDA4WUQ2Q2JTWW81SnlhQ2Zxa3cxNmZoQjJES0Uyd29rNXpSck5iWVBrVmEwOXFya1NpMi9saU5LL3F0M3N3MjZKekNCem9ER2svWkZ0SUJLdmlHRno0VlQzQ3pqZTBWcTM3YmRiNmJjTkhqUHZoQ0NxMW1ldW1XOFVVK3pQMEtUMkRMVGNvNHFlOG40ck5KcGhsYXg1b1VzZ1NYU1B2T3RXdEkwYzg4NWE3YWUzOWI1MDI0MThhMWZjODQ3MDA2OTJmNDQ0MDkxNGFiNmRlMGQ2YjZiNjI5NGMwN2IwMmI4MGZi' # noqa: E501 |
|
355 |
+VAULT_V02_CONFIG_DATA = { |
|
356 |
+ 'global': { |
|
357 |
+ 'phrase': DUMMY_PASSPHRASE.decode('utf-8').rstrip('\n'), |
|
358 |
+ }, |
|
359 |
+ 'services': { |
|
360 |
+ '(meta)': { |
|
361 |
+ 'notes': 'This config was originally in v0.2 format.', |
|
362 |
+ }, |
|
363 |
+ DUMMY_SERVICE: DUMMY_CONFIG_SETTINGS.copy(), |
|
364 |
+ }, |
|
365 |
+} |
|
366 |
+VAULT_V03_CONFIG = 'sBPBrr8BFHPxSJkV/A53zk9zwDQHFxLe6UIusCVvzFQre103pcj5xxmE11lMTA0U2QTYjkhRXKkH5WegSmYpAnzReuRsYZlWWp6N4kkubf+twZ9C3EeggPm7as2Af4TICHVbX4uXpIHeQJf9y1OtqrO+SRBrgPBzgItoxsIxebxVKgyvh1CZQOSkn7BIzt9xKhDng3ubS4hQ91fB0QCumlldTbUl8tj4Xs5JbvsSlUMxRlVzZ0OgAOrSsoWELXmsp6zXFa9K6wIuZa4wQuMLQFHiA64JO1CR3I+rviWCeMlbTOuJNx6vMB5zotKJqA2hIUpN467TQ9vI4g/QTo40m5LT2EQKbIdTvBQAzcV4lOcpr5Lqt4LHED5mKvm/4YfpuuT3I3XCdWfdG5SB7ciiB4Go+xQdddy3zZMiwm1fEwIB8XjFf2cxoJdccLQ2yxf+9diedBP04EsMHrvxKDhQ7/vHl7xF2MMFTDKl3WFd23vvcjpR1JgNAKYprG/e1p/7' # noqa: E501 |
|
367 |
+VAULT_V03_CONFIG_DATA = { |
|
368 |
+ 'global': { |
|
369 |
+ 'phrase': DUMMY_PASSPHRASE.decode('utf-8').rstrip('\n'), |
|
370 |
+ }, |
|
371 |
+ 'services': { |
|
372 |
+ '(meta)': { |
|
373 |
+ 'notes': 'This config was originally in v0.3 format.', |
|
374 |
+ }, |
|
375 |
+ DUMMY_SERVICE: DUMMY_CONFIG_SETTINGS.copy(), |
|
376 |
+ }, |
|
377 |
+} |
|
378 |
+VAULT_STOREROOM_CONFIG_ZIPPED = b""" |
|
379 |
+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 |
|
429 |
+""" |
|
430 |
+VAULT_STOREROOM_CONFIG_DATA = { |
|
431 |
+ 'global': { |
|
432 |
+ 'phrase': DUMMY_PASSPHRASE.decode('utf-8').rstrip('\n'), |
|
433 |
+ }, |
|
434 |
+ 'services': { |
|
435 |
+ '(meta)': { |
|
436 |
+ 'notes': 'This config was originally in storeroom format.', |
|
437 |
+ }, |
|
438 |
+ DUMMY_SERVICE: DUMMY_CONFIG_SETTINGS.copy(), |
|
439 |
+ }, |
|
440 |
+} |
|
441 |
+ |
|
442 |
+CANNOT_LOAD_CRYPTOGRAPHY = ( |
|
443 |
+ b'Cannot load the required Python module "cryptography".' |
|
444 |
+) |
|
445 |
+ |
|
353 | 446 |
skip_if_no_agent = pytest.mark.skipif( |
354 | 447 |
not os.environ.get('SSH_AUTH_SOCK'), reason='running SSH agent required' |
355 | 448 |
) |
... | ... |
@@ -427,7 +520,7 @@ def isolated_vault_exporter_config( |
427 | 520 |
except AttributeError: |
428 | 521 |
|
429 | 522 |
@contextlib.contextmanager |
430 |
- def chdir(newpath: str) -> Iterator[None]: |
|
523 |
+ def chdir(newpath: str) -> Iterator[None]: # pragma: no branch |
|
431 | 524 |
oldpath = os.getcwd() |
432 | 525 |
os.chdir(newpath) |
433 | 526 |
yield |
... | ... |
@@ -13,107 +13,7 @@ import pytest |
13 | 13 |
|
14 | 14 |
import tests |
15 | 15 |
from derivepassphrase import exporter |
16 |
- |
|
17 |
-VAULT_MASTER_KEY = 'vault key' |
|
18 |
-VAULT_V02_CONFIG = 'P7xeh5y4jmjpJ2pFq4KUcTVoaE9ZOEkwWmpVTURSSWQxbGt6emN4aFE4eFM3anVPbDRNTGpOLzY3eDF5aE1YTm5LNWh5Q1BwWTMwM3M5S083MWRWRFlmOXNqSFJNcStGMWFOS3c2emhiOUNNenZYTmNNMnZxaUErdlRoOGF2ZHdGT1ZLNTNLOVJQcU9jWmJrR3g5N09VcVBRZ0ZnSFNUQy9HdFVWWnFteVhRVkY3MHNBdnF2ZWFEbFBseWRGelE1c3BFTnVUckRQdWJSL29wNjFxd2Y2ZVpob3VyVzRod3FKTElTenJ1WTZacTJFOFBtK3BnVzh0QWVxcWtyWFdXOXYyenNQeFNZbWt1MDU2Vm1kVGtISWIxWTBpcWRFbyswUVJudVVhZkVlNVpGWDA4WUQ2Q2JTWW81SnlhQ2Zxa3cxNmZoQjJES0Uyd29rNXpSck5iWVBrVmEwOXFya1NpMi9saU5LL3F0M3N3MjZKekNCem9ER2svWkZ0SUJLdmlHRno0VlQzQ3pqZTBWcTM3YmRiNmJjTkhqUHZoQ0NxMW1ldW1XOFVVK3pQMEtUMkRMVGNvNHFlOG40ck5KcGhsYXg1b1VzZ1NYU1B2T3RXdEkwYzg4NWE3YWUzOWI1MDI0MThhMWZjODQ3MDA2OTJmNDQ0MDkxNGFiNmRlMGQ2YjZiNjI5NGMwN2IwMmI4MGZi' # noqa: E501 |
|
19 |
-VAULT_V02_CONFIG_DATA = { |
|
20 |
- 'global': { |
|
21 |
- 'phrase': tests.DUMMY_PASSPHRASE.decode('utf-8').rstrip('\n'), |
|
22 |
- }, |
|
23 |
- 'services': { |
|
24 |
- '(meta)': { |
|
25 |
- 'notes': 'This config was originally in v0.2 format.', |
|
26 |
- }, |
|
27 |
- tests.DUMMY_SERVICE: tests.DUMMY_CONFIG_SETTINGS.copy(), |
|
28 |
- }, |
|
29 |
-} |
|
30 |
-VAULT_V03_CONFIG = 'sBPBrr8BFHPxSJkV/A53zk9zwDQHFxLe6UIusCVvzFQre103pcj5xxmE11lMTA0U2QTYjkhRXKkH5WegSmYpAnzReuRsYZlWWp6N4kkubf+twZ9C3EeggPm7as2Af4TICHVbX4uXpIHeQJf9y1OtqrO+SRBrgPBzgItoxsIxebxVKgyvh1CZQOSkn7BIzt9xKhDng3ubS4hQ91fB0QCumlldTbUl8tj4Xs5JbvsSlUMxRlVzZ0OgAOrSsoWELXmsp6zXFa9K6wIuZa4wQuMLQFHiA64JO1CR3I+rviWCeMlbTOuJNx6vMB5zotKJqA2hIUpN467TQ9vI4g/QTo40m5LT2EQKbIdTvBQAzcV4lOcpr5Lqt4LHED5mKvm/4YfpuuT3I3XCdWfdG5SB7ciiB4Go+xQdddy3zZMiwm1fEwIB8XjFf2cxoJdccLQ2yxf+9diedBP04EsMHrvxKDhQ7/vHl7xF2MMFTDKl3WFd23vvcjpR1JgNAKYprG/e1p/7' # noqa: E501 |
|
31 |
-VAULT_V03_CONFIG_DATA = { |
|
32 |
- 'global': { |
|
33 |
- 'phrase': tests.DUMMY_PASSPHRASE.decode('utf-8').rstrip('\n'), |
|
34 |
- }, |
|
35 |
- 'services': { |
|
36 |
- '(meta)': { |
|
37 |
- 'notes': 'This config was originally in v0.3 format.', |
|
38 |
- }, |
|
39 |
- tests.DUMMY_SERVICE: tests.DUMMY_CONFIG_SETTINGS.copy(), |
|
40 |
- }, |
|
41 |
-} |
|
42 |
-VAULT_STOREROOM_CONFIG_ZIPPED = b""" |
|
43 |
-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
-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
-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
-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
-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
-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
-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
-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
-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
-7/JpSEhcmu7GDvQJ8TyLos6vPCSmxO6RRG3X4BLpqHkTgeqHz+YDZwTV+6y5dvSmTSsCP5uPCmi+ |
|
53 |
-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
-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
-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
-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
-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
-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
-EPrxoE08fu+v6DKnSopnG8GTkbscPZ+K5q2kC6m7pCizKO1sLKG7fMBRnJxnel/vmpY2lFCB4ADy |
|
60 |
-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
-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
-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
-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
-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
-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
-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
-OooHgzgTIa0y8fbpst30PEUwfUAS+lYzPXG3y+QUiy5nrJFPb0IwESd9gIIOVSfZK63wvD5ueoxj |
|
68 |
-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
-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
-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
-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
-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
-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
-a6oJzlJiP5JGxRxZPj50N6EMtzNB+tZoIGxgtOFVpiJ05yMQFztY6I6LKIgvXW/s919GIjGshqdM |
|
75 |
-XVPFxaKG4p9Iux/xazf48FY8O7SMmbQC1VsXIYo+7eSpIY67VzrCoh41wXPklOWS6CV8RR/JBSqq |
|
76 |
-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
-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
-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
-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
-P5QMwMFfAWipfXwvFPWU582cd3t7JVV5pBV0Y1clL4eKUd0w1m1M5JrkgW5PlfpOVedgABSe4zPY |
|
81 |
-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
-NRF83cWp+7rOYgNO9wWLs915oQmWAqAtqRYCiWlgAtxYFg0MnNS4/G80FvFmQTh0cjwcF1xEVPeW |
|
83 |
-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
-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
-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
-+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
-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
-SwECHgMUAAAACACdVhlZ01RRk9IAAADmAAAABQAAAAAAAAABAAAApIEAAAAALmtleXNQSwECHgMU |
|
89 |
-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
-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
-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
-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 |
|
93 |
-""" |
|
94 |
-VAULT_STOREROOM_CONFIG_DATA = { |
|
95 |
- 'global': { |
|
96 |
- 'phrase': tests.DUMMY_PASSPHRASE.decode('utf-8').rstrip('\n'), |
|
97 |
- }, |
|
98 |
- 'services': { |
|
99 |
- '(meta)': { |
|
100 |
- 'notes': 'This config was originally in storeroom format.', |
|
101 |
- }, |
|
102 |
- tests.DUMMY_SERVICE: tests.DUMMY_CONFIG_SETTINGS.copy(), |
|
103 |
- }, |
|
104 |
-} |
|
105 |
- |
|
106 |
-try: |
|
107 |
- from cryptography.hazmat.primitives import ciphers |
|
108 |
-except ModuleNotFoundError: |
|
109 |
- CRYPTOGRAPHY_SUPPORT = False |
|
110 |
-else: |
|
111 |
- CRYPTOGRAPHY_SUPPORT = True |
|
112 |
- del ciphers |
|
113 |
- |
|
114 |
-CANNOT_LOAD_CRYPTOGRAPHY = ( |
|
115 |
- b'Cannot load the required Python module "cryptography".' |
|
116 |
-) |
|
16 |
+from derivepassphrase.exporter import cli |
|
117 | 17 |
|
118 | 18 |
|
119 | 19 |
class Test001ExporterUtils: |
... | ... |
@@ -206,91 +106,6 @@ class Test001ExporterUtils: |
206 | 106 |
|
207 | 107 |
|
208 | 108 |
class Test002CLI: |
209 |
- @pytest.mark.xfail( |
|
210 |
- not CRYPTOGRAPHY_SUPPORT, reason='cryptography module not found' |
|
211 |
- ) |
|
212 |
- def test_200_path_parameter(self, monkeypatch: pytest.MonkeyPatch) -> None: |
|
213 |
- runner = click.testing.CliRunner(mix_stderr=False) |
|
214 |
- with tests.isolated_vault_exporter_config( |
|
215 |
- monkeypatch=monkeypatch, |
|
216 |
- runner=runner, |
|
217 |
- vault_config=VAULT_V03_CONFIG, |
|
218 |
- vault_key=VAULT_MASTER_KEY, |
|
219 |
- ): |
|
220 |
- monkeypatch.setenv('VAULT_KEY', VAULT_MASTER_KEY) |
|
221 |
- result = runner.invoke( |
|
222 |
- exporter.derivepassphrase_export, |
|
223 |
- ['VAULT_PATH'], |
|
224 |
- ) |
|
225 |
- assert not result.exception |
|
226 |
- assert (result.exit_code, result.stderr_bytes) == (0, b'') |
|
227 |
- assert json.loads(result.stdout) == VAULT_V03_CONFIG_DATA |
|
228 |
- |
|
229 |
- @pytest.mark.xfail( |
|
230 |
- not CRYPTOGRAPHY_SUPPORT, reason='cryptography module not found' |
|
231 |
- ) |
|
232 |
- def test_201_key_parameter(self, monkeypatch: pytest.MonkeyPatch) -> None: |
|
233 |
- runner = click.testing.CliRunner(mix_stderr=False) |
|
234 |
- with tests.isolated_vault_exporter_config( |
|
235 |
- monkeypatch=monkeypatch, |
|
236 |
- runner=runner, |
|
237 |
- vault_config=VAULT_V03_CONFIG, |
|
238 |
- ): |
|
239 |
- result = runner.invoke( |
|
240 |
- exporter.derivepassphrase_export, |
|
241 |
- ['-k', VAULT_MASTER_KEY, '.vault'], |
|
242 |
- ) |
|
243 |
- assert not result.exception |
|
244 |
- assert (result.exit_code, result.stderr_bytes) == (0, b'') |
|
245 |
- assert json.loads(result.stdout) == VAULT_V03_CONFIG_DATA |
|
246 |
- |
|
247 |
- @pytest.mark.xfail( |
|
248 |
- not CRYPTOGRAPHY_SUPPORT, reason='cryptography module not found' |
|
249 |
- ) |
|
250 |
- @pytest.mark.parametrize( |
|
251 |
- ['version', 'config', 'config_data'], |
|
252 |
- [ |
|
253 |
- pytest.param( |
|
254 |
- '0.2', VAULT_V02_CONFIG, VAULT_V02_CONFIG_DATA, id='0.2' |
|
255 |
- ), |
|
256 |
- pytest.param( |
|
257 |
- '0.3', VAULT_V03_CONFIG, VAULT_V03_CONFIG_DATA, id='0.3' |
|
258 |
- ), |
|
259 |
- pytest.param( |
|
260 |
- 'storeroom', |
|
261 |
- VAULT_STOREROOM_CONFIG_ZIPPED, |
|
262 |
- VAULT_STOREROOM_CONFIG_DATA, |
|
263 |
- id='storeroom', |
|
264 |
- ), |
|
265 |
- ], |
|
266 |
- ) |
|
267 |
- def test_210_load_vault_v02_v03_storeroom( |
|
268 |
- self, |
|
269 |
- monkeypatch: pytest.MonkeyPatch, |
|
270 |
- version: str, |
|
271 |
- config: str | bytes, |
|
272 |
- config_data: dict[str, Any], |
|
273 |
- ) -> None: |
|
274 |
- runner = click.testing.CliRunner(mix_stderr=False) |
|
275 |
- with tests.isolated_vault_exporter_config( |
|
276 |
- monkeypatch=monkeypatch, |
|
277 |
- runner=runner, |
|
278 |
- vault_config=config, |
|
279 |
- ): |
|
280 |
- result = runner.invoke( |
|
281 |
- exporter.derivepassphrase_export, |
|
282 |
- [ |
|
283 |
- '-f', |
|
284 |
- f'v{version}' if version.startswith('0') else version, |
|
285 |
- '-k', |
|
286 |
- VAULT_MASTER_KEY, |
|
287 |
- 'VAULT_PATH', |
|
288 |
- ], |
|
289 |
- ) |
|
290 |
- assert not result.exception |
|
291 |
- assert (result.exit_code, result.stderr_bytes) == (0, b'') |
|
292 |
- assert json.loads(result.stdout) == config_data |
|
293 |
- |
|
294 | 109 |
def test_300_invalid_format( |
295 | 110 |
self, |
296 | 111 |
monkeypatch: pytest.MonkeyPatch, |
... | ... |
@@ -299,11 +114,11 @@ class Test002CLI: |
299 | 114 |
with tests.isolated_vault_exporter_config( |
300 | 115 |
monkeypatch=monkeypatch, |
301 | 116 |
runner=runner, |
302 |
- vault_config=VAULT_V03_CONFIG, |
|
303 |
- vault_key=VAULT_MASTER_KEY, |
|
117 |
+ vault_config=tests.VAULT_V03_CONFIG, |
|
118 |
+ vault_key=tests.VAULT_MASTER_KEY, |
|
304 | 119 |
): |
305 | 120 |
result = runner.invoke( |
306 |
- exporter.derivepassphrase_export, |
|
121 |
+ cli.derivepassphrase_export, |
|
307 | 122 |
['-f', 'INVALID', 'VAULT_PATH'], |
308 | 123 |
catch_exceptions=False, |
309 | 124 |
) |
... | ... |
@@ -314,68 +129,3 @@ class Test002CLI: |
314 | 129 |
assert b'-f' in result.stderr_bytes |
315 | 130 |
assert b'--format' in result.stderr_bytes |
316 | 131 |
assert b'INVALID' in result.stderr_bytes |
317 |
- |
|
318 |
- @pytest.mark.xfail( |
|
319 |
- not CRYPTOGRAPHY_SUPPORT, reason='cryptography module not found' |
|
320 |
- ) |
|
321 |
- def test_301_vault_config_not_found( |
|
322 |
- self, |
|
323 |
- monkeypatch: pytest.MonkeyPatch, |
|
324 |
- ) -> None: |
|
325 |
- runner = click.testing.CliRunner(mix_stderr=False) |
|
326 |
- with tests.isolated_vault_exporter_config( |
|
327 |
- monkeypatch=monkeypatch, |
|
328 |
- runner=runner, |
|
329 |
- vault_config=VAULT_V03_CONFIG, |
|
330 |
- vault_key=VAULT_MASTER_KEY, |
|
331 |
- ): |
|
332 |
- result = runner.invoke( |
|
333 |
- exporter.derivepassphrase_export, |
|
334 |
- ['does-not-exist.txt'], |
|
335 |
- ) |
|
336 |
- assert isinstance(result.exception, SystemExit) |
|
337 |
- assert result.exit_code |
|
338 |
- assert result.stderr_bytes |
|
339 |
- assert ( |
|
340 |
- b"Cannot parse 'does-not-exist.txt' as a valid config" |
|
341 |
- in result.stderr_bytes |
|
342 |
- ) |
|
343 |
- assert CANNOT_LOAD_CRYPTOGRAPHY not in result.stderr_bytes |
|
344 |
- |
|
345 |
- @pytest.mark.xfail( |
|
346 |
- not CRYPTOGRAPHY_SUPPORT, reason='cryptography module not found' |
|
347 |
- ) |
|
348 |
- def test_302_vault_config_invalid( |
|
349 |
- self, |
|
350 |
- monkeypatch: pytest.MonkeyPatch, |
|
351 |
- ) -> None: |
|
352 |
- runner = click.testing.CliRunner(mix_stderr=False) |
|
353 |
- with tests.isolated_vault_exporter_config( |
|
354 |
- monkeypatch=monkeypatch, |
|
355 |
- runner=runner, |
|
356 |
- vault_config='', |
|
357 |
- vault_key=VAULT_MASTER_KEY, |
|
358 |
- ): |
|
359 |
- result = runner.invoke( |
|
360 |
- exporter.derivepassphrase_export, |
|
361 |
- ['.vault'], |
|
362 |
- ) |
|
363 |
- assert isinstance(result.exception, SystemExit) |
|
364 |
- assert result.exit_code |
|
365 |
- assert result.stderr_bytes |
|
366 |
- assert ( |
|
367 |
- b"Cannot parse '.vault' as a valid config." in result.stderr_bytes |
|
368 |
- ) |
|
369 |
- assert CANNOT_LOAD_CRYPTOGRAPHY not in result.stderr_bytes |
|
370 |
- |
|
371 |
- |
|
372 |
-class TestStoreroomExporter: |
|
373 |
- pass # TODO(the-13th-letter): Fill in once design is stable. |
|
374 |
- |
|
375 |
- |
|
376 |
-class TestV02Exporter: |
|
377 |
- pass # TODO(the-13th-letter): Fill in once design is stable. |
|
378 |
- |
|
379 |
- |
|
380 |
-class TestV03Exporter: |
|
381 |
- pass # TODO(the-13th-letter): Fill in once design is stable. |
... | ... |
@@ -0,0 +1,146 @@ |
1 |
+# SPDX-FileCopyrightText: 2024 Marco Ricci <m@the13thletter.info> |
|
2 |
+# |
|
3 |
+# SPDX-License-Identifier: MIT |
|
4 |
+ |
|
5 |
+from __future__ import annotations |
|
6 |
+ |
|
7 |
+import json |
|
8 |
+import os |
|
9 |
+from typing import Any |
|
10 |
+ |
|
11 |
+import click.testing |
|
12 |
+import pytest |
|
13 |
+ |
|
14 |
+import tests |
|
15 |
+from derivepassphrase import exporter |
|
16 |
+from derivepassphrase.exporter import cli |
|
17 |
+ |
|
18 |
+cryptography = pytest.importorskip('cryptography', minversion='38.0') |
|
19 |
+ |
|
20 |
+ |
|
21 |
+class TestCLI: |
|
22 |
+ def test_200_path_parameter(self, monkeypatch: pytest.MonkeyPatch) -> None: |
|
23 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
24 |
+ with tests.isolated_vault_exporter_config( |
|
25 |
+ monkeypatch=monkeypatch, |
|
26 |
+ runner=runner, |
|
27 |
+ vault_config=tests.VAULT_V03_CONFIG, |
|
28 |
+ vault_key=tests.VAULT_MASTER_KEY, |
|
29 |
+ ): |
|
30 |
+ monkeypatch.setenv('VAULT_KEY', tests.VAULT_MASTER_KEY) |
|
31 |
+ result = runner.invoke( |
|
32 |
+ cli.derivepassphrase_export, |
|
33 |
+ ['VAULT_PATH'], |
|
34 |
+ ) |
|
35 |
+ assert not result.exception |
|
36 |
+ assert (result.exit_code, result.stderr_bytes) == (0, b'') |
|
37 |
+ assert json.loads(result.stdout) == tests.VAULT_V03_CONFIG_DATA |
|
38 |
+ |
|
39 |
+ def test_201_key_parameter(self, monkeypatch: pytest.MonkeyPatch) -> None: |
|
40 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
41 |
+ with tests.isolated_vault_exporter_config( |
|
42 |
+ monkeypatch=monkeypatch, |
|
43 |
+ runner=runner, |
|
44 |
+ vault_config=tests.VAULT_V03_CONFIG, |
|
45 |
+ ): |
|
46 |
+ result = runner.invoke( |
|
47 |
+ cli.derivepassphrase_export, |
|
48 |
+ ['-k', tests.VAULT_MASTER_KEY, '.vault'], |
|
49 |
+ ) |
|
50 |
+ assert not result.exception |
|
51 |
+ assert (result.exit_code, result.stderr_bytes) == (0, b'') |
|
52 |
+ assert json.loads(result.stdout) == tests.VAULT_V03_CONFIG_DATA |
|
53 |
+ |
|
54 |
+ @pytest.mark.parametrize( |
|
55 |
+ ['format', 'config', 'config_data'], |
|
56 |
+ [ |
|
57 |
+ pytest.param( |
|
58 |
+ 'v0.2', |
|
59 |
+ tests.VAULT_V02_CONFIG, |
|
60 |
+ tests.VAULT_V02_CONFIG_DATA, |
|
61 |
+ id='0.2', |
|
62 |
+ ), |
|
63 |
+ pytest.param( |
|
64 |
+ 'v0.3', |
|
65 |
+ tests.VAULT_V03_CONFIG, |
|
66 |
+ tests.VAULT_V03_CONFIG_DATA, |
|
67 |
+ id='0.3', |
|
68 |
+ ), |
|
69 |
+ pytest.param( |
|
70 |
+ 'storeroom', |
|
71 |
+ tests.VAULT_STOREROOM_CONFIG_ZIPPED, |
|
72 |
+ tests.VAULT_STOREROOM_CONFIG_DATA, |
|
73 |
+ id='storeroom', |
|
74 |
+ ), |
|
75 |
+ ], |
|
76 |
+ ) |
|
77 |
+ def test_210_load_vault_v02_v03_storeroom( |
|
78 |
+ self, |
|
79 |
+ monkeypatch: pytest.MonkeyPatch, |
|
80 |
+ format: str, |
|
81 |
+ config: str | bytes, |
|
82 |
+ config_data: dict[str, Any], |
|
83 |
+ ) -> None: |
|
84 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
85 |
+ with tests.isolated_vault_exporter_config( |
|
86 |
+ monkeypatch=monkeypatch, |
|
87 |
+ runner=runner, |
|
88 |
+ vault_config=config, |
|
89 |
+ ): |
|
90 |
+ result = runner.invoke( |
|
91 |
+ cli.derivepassphrase_export, |
|
92 |
+ ['-f', format, '-k', tests.VAULT_MASTER_KEY, 'VAULT_PATH'], |
|
93 |
+ ) |
|
94 |
+ assert not result.exception |
|
95 |
+ assert (result.exit_code, result.stderr_bytes) == (0, b'') |
|
96 |
+ assert json.loads(result.stdout) == config_data |
|
97 |
+ |
|
98 |
+ # test_300_invalid_format is found in |
|
99 |
+ # tests.test_derivepassphrase_export::Test002CLI |
|
100 |
+ |
|
101 |
+ def test_301_vault_config_not_found( |
|
102 |
+ self, |
|
103 |
+ monkeypatch: pytest.MonkeyPatch, |
|
104 |
+ ) -> None: |
|
105 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
106 |
+ with tests.isolated_vault_exporter_config( |
|
107 |
+ monkeypatch=monkeypatch, |
|
108 |
+ runner=runner, |
|
109 |
+ vault_config=tests.VAULT_V03_CONFIG, |
|
110 |
+ vault_key=tests.VAULT_MASTER_KEY, |
|
111 |
+ ): |
|
112 |
+ result = runner.invoke( |
|
113 |
+ cli.derivepassphrase_export, |
|
114 |
+ ['does-not-exist.txt'], |
|
115 |
+ ) |
|
116 |
+ assert isinstance(result.exception, SystemExit) |
|
117 |
+ assert result.exit_code |
|
118 |
+ assert result.stderr_bytes |
|
119 |
+ assert ( |
|
120 |
+ b"Cannot parse 'does-not-exist.txt' as a valid config" |
|
121 |
+ in result.stderr_bytes |
|
122 |
+ ) |
|
123 |
+ assert tests.CANNOT_LOAD_CRYPTOGRAPHY not in result.stderr_bytes |
|
124 |
+ |
|
125 |
+ def test_302_vault_config_invalid( |
|
126 |
+ self, |
|
127 |
+ monkeypatch: pytest.MonkeyPatch, |
|
128 |
+ ) -> None: |
|
129 |
+ runner = click.testing.CliRunner(mix_stderr=False) |
|
130 |
+ with tests.isolated_vault_exporter_config( |
|
131 |
+ monkeypatch=monkeypatch, |
|
132 |
+ runner=runner, |
|
133 |
+ vault_config='', |
|
134 |
+ vault_key=tests.VAULT_MASTER_KEY, |
|
135 |
+ ): |
|
136 |
+ result = runner.invoke( |
|
137 |
+ cli.derivepassphrase_export, |
|
138 |
+ ['.vault'], |
|
139 |
+ ) |
|
140 |
+ assert isinstance(result.exception, SystemExit) |
|
141 |
+ assert result.exit_code |
|
142 |
+ assert result.stderr_bytes |
|
143 |
+ assert ( |
|
144 |
+ b"Cannot parse '.vault' as a valid config." in result.stderr_bytes |
|
145 |
+ ) |
|
146 |
+ assert tests.CANNOT_LOAD_CRYPTOGRAPHY not in result.stderr_bytes |
|
0 | 147 |