Correctly model vault global and service settings
Marco Ricci

Marco Ricci commited on 2024-10-14 22:31:40
Zeige 2 geänderte Dateien mit 51 Einfügungen und 33 Löschungen.


Excluding extensions due to `derivepassphrase`, in vault(1), the service
settings are a superset of the global settings… though some settings
might be weird in a global setting.  The abstract data types don't
accurately model this, though.  So fix those.

(The sole current `derivepassphrase` extension, the
`unicode_normalization_form` setting, modifies the `phrase` setting, and
so also makes sense to specify globally or locally.  So use the same
inheritance and scoping rules as `phrase`.)

Incidentally, the command-line interface did not respect this
inheritance either… in a more destructive way, asserting that some
options require a service when they don't, or don't take a service when
they do.  Redo the categorization there into three groups instead of
two: service name mandatory, forbidden, or optional (new).
... ...
@@ -50,26 +50,6 @@ class VaultConfigGlobalSettings(TypedDict, total=False):
50 50
             The preferred Unicode normalization form; we warn the user
51 51
             if textual passphrases do not match their normalized forms.
52 52
             Optional, and a `derivepassphrase` extension.
53
-
54
-    """
55
-
56
-    key: NotRequired[str]
57
-    """"""
58
-    phrase: NotRequired[str]
59
-    """"""
60
-    unicode_normalization_form: NotRequired[
61
-        Literal['NFC', 'NFD', 'NFKC', 'NFKD']
62
-    ]
63
-    """"""
64
-
65
-
66
-class VaultConfigServicesSettings(VaultConfigGlobalSettings, total=False):
67
-    r"""Configuration for vault: services settings.
68
-
69
-    Attributes:
70
-        notes:
71
-            Optional notes for this service, to display to the user when
72
-            generating the passphrase.
73 53
         length:
74 54
             Desired passphrase length.
75 55
         repeat:
... ...
@@ -95,7 +75,13 @@ class VaultConfigServicesSettings(VaultConfigGlobalSettings, total=False):
95 75
 
96 76
     """
97 77
 
98
-    notes: NotRequired[str]
78
+    key: NotRequired[str]
79
+    """"""
80
+    phrase: NotRequired[str]
81
+    """"""
82
+    unicode_normalization_form: NotRequired[
83
+        Literal['NFC', 'NFD', 'NFKC', 'NFKD']
84
+    ]
99 85
     """"""
100 86
     length: NotRequired[int]
101 87
     """"""
... ...
@@ -115,6 +101,42 @@ class VaultConfigServicesSettings(VaultConfigGlobalSettings, total=False):
115 101
     """"""
116 102
 
117 103
 
104
+class VaultConfigServicesSettings(VaultConfigGlobalSettings, total=False):
105
+    r"""Configuration for vault: services settings.
106
+
107
+    Attributes:
108
+        notes:
109
+            Optional notes for this service, to display to the user when
110
+            generating the passphrase.
111
+        key:
112
+            As per the global settings.
113
+        phrase:
114
+            As per the global settings.
115
+        unicode_normalization_form:
116
+            As per the global settings.
117
+        length:
118
+            As per the global settings.
119
+        repeat:
120
+            As per the global settings.
121
+        lower:
122
+            As per the global settings.
123
+        upper:
124
+            As per the global settings.
125
+        number:
126
+            As per the global settings.
127
+        space:
128
+            As per the global settings.
129
+        dash:
130
+            As per the global settings.
131
+        symbol:
132
+            As per the global settings.
133
+
134
+    """
135
+
136
+    notes: NotRequired[str]
137
+    """"""
138
+
139
+
118 140
 _VaultConfig = TypedDict(
119 141
     '_VaultConfig',
120 142
     {'global': NotRequired[VaultConfigGlobalSettings]},
... ...
@@ -1333,23 +1333,19 @@ def derivepassphrase_vault(  # noqa: C901,PLR0912,PLR0913,PLR0914,PLR0915
1333 1333
                 *options_in_group[ConfigurationOption],
1334 1334
                 *options_in_group[StorageManagementOption],
1335 1335
             )
1336
-    sv_options = options_in_group[PasswordGenerationOption] + [
1337
-        params_by_str['--notes'],
1338
-        params_by_str['--delete'],
1339
-    ]
1340
-    sv_options.remove(params_by_str['--key'])
1341
-    sv_options.remove(params_by_str['--phrase'])
1342
-    for param in sv_options:
1343
-        if is_param_set(param) and not service:
1344
-            opt_str = param.opts[0]
1345
-            msg = f'{opt_str} requires a SERVICE'
1346
-            raise click.UsageError(msg)  # noqa: DOC501
1347
-    for param in [params_by_str['--key'], params_by_str['--phrase']]:
1336
+    sv_or_global_options = options_in_group[PasswordGenerationOption]
1337
+    for param in sv_or_global_options:
1348 1338
         if is_param_set(param) and not (
1349 1339
             service or is_param_set(params_by_str['--config'])
1350 1340
         ):
1351 1341
             opt_str = param.opts[0]
1352 1342
             msg = f'{opt_str} requires a SERVICE or --config'
1343
+            raise click.UsageError(msg)  # noqa: DOC501
1344
+    sv_options = [params_by_str['--notes'], params_by_str['--delete']]
1345
+    for param in sv_options:
1346
+        if is_param_set(param) and not service:
1347
+            opt_str = param.opts[0]
1348
+            msg = f'{opt_str} requires a SERVICE'
1353 1349
             raise click.UsageError(msg)
1354 1350
     no_sv_options = [
1355 1351
         params_by_str['--delete-globals'],
1356 1352