75ea7844415f5c78114860f7958bc39a8e4c6704
Marco Ricci Update copyright notices to...

Marco Ricci authored 2 months ago

1) # SPDX-FileCopyrightText: 2025 Marco Ricci <software@the13thletter.info>
Marco Ricci Add command-line interface...

Marco Ricci authored 6 months ago

2) #
Marco Ricci Update copyright notices to...

Marco Ricci authored 2 months ago

3) # SPDX-License-Identifier: Zlib
Marco Ricci Add command-line interface...

Marco Ricci authored 6 months ago

4) 
Marco Ricci Move exporter command-line...

Marco Ricci authored 6 months ago

5) """Foreign configuration exporter for derivepassphrase."""
Marco Ricci Add command-line interface...

Marco Ricci authored 6 months ago

6) 
7) from __future__ import annotations
8) 
Marco Ricci Harmonize the interface for...

Marco Ricci authored 2 months ago

9) import importlib
Marco Ricci Move vault key and path det...

Marco Ricci authored 6 months ago

10) import os
Marco Ricci Harmonize the interface for...

Marco Ricci authored 2 months ago

11) from typing import TYPE_CHECKING, Protocol
Marco Ricci Add command-line interface...

Marco Ricci authored 6 months ago

12) 
13) import derivepassphrase as dpp
14) 
Marco Ricci 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) 
Marco Ricci Add command-line interface...

Marco Ricci authored 6 months ago

21) __author__ = dpp.__author__
22) __version__ = dpp.__version__
23) 
Marco Ricci Move exporter command-line...

Marco Ricci authored 6 months ago

24) __all__ = ()
Marco Ricci Move vault key and path det...

Marco Ricci authored 6 months ago

25) 
26) 
Marco Ricci 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) 
Marco Ricci Move vault key and path det...

Marco Ricci authored 6 months ago

52) def get_vault_key() -> bytes:
Marco Ricci Fix miscellaneous small doc...

Marco Ricci authored 6 months ago

53)     """Automatically determine the vault(1) master key/password.
Marco Ricci Move vault key and path det...

Marco Ricci authored 6 months ago

54) 
55)     Query the `VAULT_KEY`, `LOGNAME`, `USER` and `USERNAME` environment
Marco Ricci Fix miscellaneous small doc...

Marco Ricci authored 6 months ago

56)     variables, in that order.  This is the same algorithm that vault
57)     uses.
Marco Ricci 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:
Marco Ricci Fix miscellaneous small doc...

Marco Ricci authored 6 months ago

83)     """Automatically determine the vault(1) configuration path.
Marco Ricci Move vault key and path det...

Marco Ricci authored 6 months ago

84) 
85)     Query the `VAULT_PATH` environment variable, or default to
Marco Ricci Fix miscellaneous small doc...

Marco Ricci authored 6 months ago

86)     `~/.vault`.  This is the same algorithm that vault uses.  If not
Marco Ricci 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
Marco Ricci Harmonize the interface for...

Marco Ricci authored 2 months ago

106) 
107) 
108) class ExportVaultConfigDataFunction(Protocol):  # pragma: no cover
Marco Ricci Consolidate ExportVaultConf...

Marco Ricci authored 1 month ago

109)     """Typing protocol for vault config data export handlers."""
110) 
Marco Ricci 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
Marco Ricci 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)         """
Marco Ricci 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) 
Marco Ricci Consolidate ExportVaultConf...

Marco Ricci authored 1 month ago

226)     See [`ExportVaultConfigDataFunction`][] for an explanation of the
227)     call signature, and the exceptions to expect.
Marco Ricci Harmonize the interface for...

Marco Ricci authored 2 months ago

228) 
Marco Ricci Consolidate ExportVaultConf...

Marco Ricci authored 1 month ago

229)     """  # noqa: DOC201,DOC501