Update copyright notices to...
Marco Ricci authored 2 months ago
|
1) # SPDX-FileCopyrightText: 2025 Marco Ricci <software@the13thletter.info>
|
Add command-line interface...
Marco Ricci authored 6 months ago
|
2) #
|
Update copyright notices to...
Marco Ricci authored 2 months ago
|
3) # SPDX-License-Identifier: Zlib
|
Add command-line interface...
Marco Ricci authored 6 months ago
|
4)
|
Move exporter command-line...
Marco Ricci authored 6 months ago
|
5) """Foreign configuration exporter for derivepassphrase."""
|
Add command-line interface...
Marco Ricci authored 6 months ago
|
6)
7) from __future__ import annotations
8)
|
Harmonize the interface for...
Marco Ricci authored 2 months ago
|
9) import importlib
|
Move vault key and path det...
Marco Ricci authored 6 months ago
|
10) import os
|
Harmonize the interface for...
Marco Ricci authored 2 months ago
|
11) from typing import TYPE_CHECKING, Protocol
|
Add command-line interface...
Marco Ricci authored 6 months ago
|
12)
13) import derivepassphrase as dpp
14)
|
Harmonize the interface for...
Marco Ricci authored 2 months ago
|
15) if TYPE_CHECKING:
16) from collections.abc import Callable
17) from typing import Any
18)
19) from typing_extensions import Buffer
20)
|
Add command-line interface...
Marco Ricci authored 6 months ago
|
21) __author__ = dpp.__author__
22) __version__ = dpp.__version__
23)
|
Move exporter command-line...
Marco Ricci authored 6 months ago
|
24) __all__ = ()
|
Move vault key and path det...
Marco Ricci authored 6 months ago
|
25)
26)
|
Harmonize the interface for...
Marco Ricci authored 2 months ago
|
27) INVALID_VAULT_NATIVE_CONFIGURATION_FORMAT = (
28) 'Invalid vault native configuration format: {fmt!r}'
29) )
30)
31)
32) class NotAVaultConfigError(ValueError):
33) """The `path` does not hold a `format`-type vault configuration."""
34)
35) def __init__(
36) self,
37) path: str | bytes,
38) format: str | None = None, # noqa: A002
39) ) -> None:
40) self.path = path
41) self.format = format
42)
43) def __str__(self) -> str: # pragma: no cover
44) formatted_format = (
45) f'vault {self.format} configuration'
46) if self.format
47) else 'vault configuration'
48) )
49) return f'Not a {formatted_format}: {self.path!r}'
50)
51)
|
Move vault key and path det...
Marco Ricci authored 6 months ago
|
52) def get_vault_key() -> bytes:
|
Fix miscellaneous small doc...
Marco Ricci authored 6 months ago
|
53) """Automatically determine the vault(1) master key/password.
|
Move vault key and path det...
Marco Ricci authored 6 months ago
|
54)
55) Query the `VAULT_KEY`, `LOGNAME`, `USER` and `USERNAME` environment
|
Fix miscellaneous small doc...
Marco Ricci authored 6 months ago
|
56) variables, in that order. This is the same algorithm that vault
57) uses.
|
Move vault key and path det...
Marco Ricci authored 6 months ago
|
58)
59) Returns:
60) The master key/password. This is generally used as input to
61) a key-derivation function to determine the *actual* encryption
62) and signing keys for the vault configuration.
63)
64) Raises:
65) KeyError:
66) We cannot find any of the named environment variables.
67) Please set `VAULT_KEY` manually to the desired value.
68)
69) """
70) username = (
71) os.environb.get(b'VAULT_KEY')
72) or os.environb.get(b'LOGNAME')
73) or os.environb.get(b'USER')
74) or os.environb.get(b'USERNAME')
75) )
76) if not username:
77) env_var = 'VAULT_KEY'
78) raise KeyError(env_var)
79) return username
80)
81)
82) def get_vault_path() -> str | bytes | os.PathLike:
|
Fix miscellaneous small doc...
Marco Ricci authored 6 months ago
|
83) """Automatically determine the vault(1) configuration path.
|
Move vault key and path det...
Marco Ricci authored 6 months ago
|
84)
85) Query the `VAULT_PATH` environment variable, or default to
|
Fix miscellaneous small doc...
Marco Ricci authored 6 months ago
|
86) `~/.vault`. This is the same algorithm that vault uses. If not
|
Move vault key and path det...
Marco Ricci authored 6 months ago
|
87) absolute, then `VAULT_PATH` is relative to the home directory.
88)
89) Returns:
90) The vault configuration path. Depending on the vault version,
91) this may be a file or a directory.
92)
93) Raises:
94) RuntimeError:
95) We cannot determine the home directory. Please set `HOME`
96) manually to the correct value.
97)
98) """
99) result = os.path.join(
100) os.path.expanduser('~'), os.environ.get('VAULT_PATH', '.vault')
101) )
102) if result.startswith('~'):
103) msg = 'Cannot determine home directory'
104) raise RuntimeError(msg)
105) return result
|
Harmonize the interface for...
Marco Ricci authored 2 months ago
|
106)
107)
108) class ExportVaultConfigDataFunction(Protocol): # pragma: no cover
|
Consolidate ExportVaultConf...
Marco Ricci authored 1 month ago
|
109) """Typing protocol for vault config data export handlers."""
110)
|
Harmonize the interface for...
Marco Ricci authored 2 months ago
|
111) def __call__(
112) self,
113) path: str | bytes | os.PathLike | None = None,
114) key: str | Buffer | None = None,
115) *,
116) format: str, # noqa: A002
|
Consolidate ExportVaultConf...
Marco Ricci authored 1 month ago
|
117) ) -> Any: # noqa: ANN401
118) """Export the full vault-native configuration stored in `path`.
119)
120) Args:
121) path:
122) The path to the vault configuration file or directory.
123) If not given, then query [`get_vault_path`][] for the
124) correct value.
125) key:
126) Encryption key/password for the configuration file or
127) directory, usually the username, or passed via the
128) `VAULT_KEY` environment variable. If not given, then
129) query [`get_vault_key`][] for the value.
130) format:
131) The format to attempt parsing as. Must be `v0.2`,
132) `v0.3` or `storeroom`.
133)
134) Returns:
135) The vault configuration, as recorded in the configuration
136) file.
137)
138) This may or may not be a valid configuration according to
139) `vault` or `derivepassphrase`.
140)
141) Raises:
142) IsADirectoryError:
143) The requested format requires a configuration file, but
144) `path` points to a directory instead.
145) NotADirectoryError:
146) The requested format requires a configuration directory,
147) but `path` points to something else instead.
148) OSError:
149) There was an OS error while accessing the configuration
150) file/directory.
151) RuntimeError:
152) Something went wrong during data collection, e.g. we
153) encountered unsupported or corrupted data in the
154) configuration file/directory.
155) json.JSONDecodeError:
156) An internal JSON data structure failed to parse from
157) disk. The configuration file/directory is probably
158) corrupted.
159) exporter.NotAVaultConfigError:
160) The file/directory contents are not in the claimed
161) configuration format.
162) ValueError:
163) The requested format is invalid.
164) ModuleNotFoundError:
165) The requested format requires support code, which failed
166) to load because of missing Python libraries.
167)
168) """
|
Harmonize the interface for...
Marco Ricci authored 2 months ago
|
169)
170)
171) _export_vault_config_data_registry: dict[
172) str,
173) ExportVaultConfigDataFunction,
174) ] = {}
175)
176)
177) def register_export_vault_config_data_handler(
178) *names: str,
179) ) -> Callable[[ExportVaultConfigDataFunction], ExportVaultConfigDataFunction]:
180) if not names:
181) msg = 'No names given to export_data handler registry'
182) raise ValueError(msg)
183) if '' in names:
184) msg = 'Cannot register export_data handler under an empty name'
185) raise ValueError(msg)
186)
187) def wrapper(
188) f: ExportVaultConfigDataFunction,
189) ) -> ExportVaultConfigDataFunction:
190) for name in names:
191) if name in _export_vault_config_data_registry:
192) msg = f'export_data handler already registered: {name!r}'
193) raise ValueError(msg)
194) _export_vault_config_data_registry[name] = f
195) return f
196)
197) return wrapper
198)
199)
200) def find_vault_config_data_handlers() -> None:
201) """Find all export handlers for vault config data.
202)
203) (This function is idempotent.)
204)
205) Raises:
206) ModuleNotFoundError:
207) A required module was not found.
208)
209) """
210) # Defer imports (and handler registrations) to avoid circular
211) # imports. The modules themselves contain function definitions that
212) # register themselves automatically with
213) # `_export_vault_config_data_registry`.
214) importlib.import_module('derivepassphrase.exporter.storeroom')
215) importlib.import_module('derivepassphrase.exporter.vault_native')
216)
217)
218) def export_vault_config_data(
219) path: str | bytes | os.PathLike | None = None,
220) key: str | Buffer | None = None,
221) *,
222) format: str, # noqa: A002
223) ) -> Any: # noqa: ANN401
224) """Export the full vault-native configuration stored in `path`.
225)
|
Consolidate ExportVaultConf...
Marco Ricci authored 1 month ago
|
226) See [`ExportVaultConfigDataFunction`][] for an explanation of the
227) call signature, and the exceptions to expect.
|
Harmonize the interface for...
Marco Ricci authored 2 months ago
|
228)
|
Consolidate ExportVaultConf...
Marco Ricci authored 1 month ago
|
229) """ # noqa: DOC201,DOC501
|