feat(wyctl): gsettings fallback for mfa --store/--keyprovider#334
Merged
Conversation
…ys for mfa subcommands Issue #333. The `wyctl mfa enroll` / `mfa reset` subcommands previously required `--store` and `--keyprovider` on every invocation, the lone gap in the otherwise uniform "CLI > GSettings > error" pipeline the rest of wyctl uses (see `wyctl_resolve_string_option` and the canonical call site in `run_status`). Operators who set every other default once via `gsettings set` had to keep retyping these two flags. Changes: * `wyrelog/wyctl/org.wyrelog.wyctl.gschema.xml` declares two new string keys, default empty: - `default-policy-store` - `default-keyprovider` Naming asymmetry note: the CLI flag is `--store`, the GSettings key is `default-policy-store`. The flag is unambiguous inside an offline subcommand (wyctl mfa only operates on the policy store) but the schema key lives outside that context — wyrelog persists multiple kinds of stores (policy, fact, ...) and a flat `default-store` key would invite confusion in operator config. `default-keyprovider` matches the CLI flag verbatim because there is only one kind of keyprovider in wyrelog. * `wyrelog/wyctl/wyctl.c` threads the existing `WyctlOptions` (carrying the GSettings handle opened once in `main`) into `run_mfa`, `run_mfa_enroll`, and `run_mfa_reset`. Both subcommands call `wyctl_resolve_string_option` for `--store` and `--keyprovider` after `g_option_context_parse` and before `wyctl_mfa_validate_common_options`, mirroring the ownership pattern in `run_status` (g_autofree locals; opts.* rebound to the resolved values so the GOptionContext-owned slots are not reassigned). * `--subject` is deliberately NOT a GSettings key: it changes between enrollments and is therefore a per-invocation identity, not a default. * No third precedence tier and no new env vars. The existing `WYCTL_DISABLE_GSETTINGS=1` kill switch is still the only override. Tests (TDD): * `tests/test-wyctl-config.c` extends the `fresh_settings` reset array with the two new key names and adds six unit tests covering CLI-wins, fall-back-to-settings, and empty-settings-is-unset for both keys. * `tests/test-wyctl-mfa.c` adds eight subprocess-level functional tests exercising the resolver call sites in the real wyctl binary: - CLI --store wins over GSettings - GSettings supplies --store when CLI omits it - Both unset surfaces the existing "missing --store" diagnostic - WYCTL_DISABLE_GSETTINGS=1 kill switch suppresses the fallback - Empty-string GSettings is treated as unset (symmetry test) - `mfa reset` consumes the GSettings value end-to-end - GSettings supplies --keyprovider when CLI omits it - Defense-in-depth: after a fully-GSettings-resolved enrollment, the operator-supplied keyfile contains the store path and nothing else — no seed bytes, no otpauth URI, no enrollment UUIDv7, no subject string. Pins the invariant that the GSettings keys are operator-config only, never a sink for any enrollment artifact. * `tests/meson.build` wires the wyctl-mfa test against the compiled schema directory and the memory backend, the same env the wyctl-config and wyctl-basic tests already use. The per-test XDG-keyfile fixtures then layer a real GSettings keyfile on top. Documentation lives in a follow-up commit (issue #333 commit 2) so this change remains code-only per the project's atomic-commit policy and so the runbook does not document a non-feature.
Document the two GSettings keys (`default-policy-store`, `default-keyprovider`) added in commit 1 of #333, and tighten the inaccurate comment that claimed to mirror `run_status`'s ownership pattern. operator-runbook.md: - New subsection "Setting defaults via GSettings" under the MFA section walking through the two-line gsettings setup and noting that --subject stays explicit and the WYCTL_DISABLE_GSETTINGS=1 kill switch covers mfa subcommands the same way it covers the rest of wyctl. - Extended the GSettings key reference table with the two new keys (type `s`, default `""`), matching the row format used by the existing keys and matching the schema XML byte-for-byte. - Extended the canonical "paths/specs only, never secret bytes" policy statement to explicitly cover the two new keys. - Extended the precedence-rule paragraph to cross-reference the mfa subcommands as participants in the same resolver pipeline. tests/test-wyctl-gschema.c: - Added `default-policy-store` and `default-keyprovider` to `expected[]` (keys-and-types test) and to `string_keys[]` (defaults-safe test) so the schema-pin and safe-default coverage extend to the new keys. Test count unchanged. wyrelog/wyctl/wyctl.c: - Rewrote the comment blocks above the resolver calls in `run_mfa_enroll` and `run_mfa_reset` to describe what the code actually does (overwrites the GOptionContext-owned slot with the resolved-and-owned local, accepting the small one-shot leak of the original — same pattern as elsewhere in wyctl) instead of the misleading "mirroring run_status" claim. No behaviour change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #333. Follow-up to PR #332.
Summary
Wires
wyctl mfa enroll/resetinto the existingwyctl_resolve_string_optionGSettings pipeline so operators can set policy-store path and keyprovider spec once and stop retyping them. Two atomic commits, both reviewer-ratified.Commit map
wyctl: read default-policy-store and default-keyprovider GSettings keys for mfa subcommands— adds twoskeys (default"") toorg.wyrelog.wyctl.gschema.xml; threadsWyctlOptions *global_optsthroughrun_mfaand into both subcommands; calls the existing resolver beforewyctl_mfa_validate_common_options; +14 subtests (6 unit + 8 subprocess incl. defense-in-depth no-secrets-in-keyfile scan).docs: document gsettings fallback for wyctl mfa subcommands— operator-runbook MFA section getsSetting defaults via GSettings; key table extended; "paths/specs only, never secrets" policy statement extended to cover the new keys; pin backfill intests/test-wyctl-gschema.c; corrected an inaccurate ownership-pattern comment inwyctl.cthat claimed run_status-style mirroring (run_status does not rebindopts, the mfa subcommands do).Design (locked)
default-policy-store(backs--store),default-keyprovider(backs--keyprovider). Boths, default"".--subjectdeliberately NOT GSettings-backed — every enrollment targets a different principal.WYCTL_DISABLE_GSETTINGS=1covers mfa uniformly with the rest of wyctl.operator-runbook.md:903-922rationale).Test plan
meson test -C builddir --suite wyreloggreen locally (75 OK / 2 skipped / 0 fail).Threat model
default-policy-storeanddefault-keyproviderstore only paths and spec strings. The KeyProvider key material, the TOTP seed bytes, and policy-store contents never live in GSettings — verified by the defense-in-depth test and documented in the runbook policy section.access-token-filekey already accepts.Out of scope (unchanged from issue)
--subjectas a GSettings defaultWYCTL_DISABLE_GSETTINGS