chore(kanidm): declare a standing test account for passkey-page checks #484

Sammanfogat
supernaut sammanfogade 1 incheckning från chore/test-passkey-check-user in i main 2026-09-19 13:35:06 +00:00
Ägare

Why

ADR 0038 conceals identity fields with CSS. The health-check role blocks a Forgejo upgrade until those surfaces are re-verified against the new tag, and one of them is the Kanidm passkey-enrolment page (/ui/reset?token=…). That page is the only check that catches a selector hiding too much — the direction that broke passkey enrolment entirely on 2026-08-04 when .row:has(input[name="name"]) was unscoped.

Reaching that page needs a live credential-reset token, which needs an account. forgejo_users was empty, so the check could only be run by issuing a reset link against a real person's account.

What

One declared user:

forgejo_users:
  - { username: "test-passkey-check", email: "test-passkey-check@bitborg.se", role: "user", displayname: "Passkey check" }

forgejo_users is the single source of truth for both the Kanidm person and the Forgejo account (vars.yml header, kanidm-state.json.j2:5), so this is the only sanctioned route. The repo has no separate test-account concept and this is an ordinary user in every respect.

role: "user" keeps it out of forgejo_admins. The address needs no mailbox; the domain has a catch-all.

Quota and seat impact

  • No paid seat. reconciler_user_exempt is derived from forgejo_users (roles/reconciler/defaults/main.yml:90), and the exempt branch is evaluated before the tier branches in projection.ts classifyUser(), so the account gets the unlimited first-party group rather than the paid 10 GiB one.
  • It does join Kanidm tier_basic, because kanidm-state.json.j2:10 layers every forgejo_users entry into that group. roles/reconciler/defaults/main.yml:23 labels tier_basic "paid seat". The Forgejo-side quota is unaffected per the point above, but anything outside this repo that counts tier_basic membership as a paying user would count this account. Worth confirming against the billing side before the seat count is trusted.

After merge

Provisioning is deliberately not a full site.yml run: health-check is tagged always and sits at the end of the play, so a full run would apply the pending Forgejo 16.0.5 upgrade and only then fail the concealment gate. Scope it:

ansible-playbook site.yml --tags kanidm --skip-tags health-check \
  -e '{"kanidm_onboard_users":["test-passkey-check"]}'

That creates the Kanidm person and prints one reset link (TTL 24 h). The Forgejo account is not created by a kanidm-tagged run and is not needed for the passkey check; it lands on the next full apply.

Every later check mints its own token, no playbook:

kanidm person credential create-reset-token --ttl 86400 test-passkey-check --name idm_admin

Notes

  • Removing the entry later deprovisions nothing. Convergence is additive. Decommissioning means deleting the Kanidm person and the Forgejo account by hand.
  • The Forgejo-side password is random and unrecoverable by design. Sign in via Bitborg Auth only.

ansible-lint passes (production profile), site.yml --syntax-check passes.

## Why ADR 0038 conceals identity fields with CSS. The `health-check` role blocks a Forgejo upgrade until those surfaces are re-verified against the new tag, and one of them is the Kanidm passkey-enrolment page (`/ui/reset?token=…`). That page is the only check that catches a selector hiding **too much** — the direction that broke passkey enrolment entirely on 2026-08-04 when `.row:has(input[name="name"])` was unscoped. Reaching that page needs a live credential-reset token, which needs an account. `forgejo_users` was empty, so the check could only be run by issuing a reset link against a real person's account. ## What One declared user: ```yaml forgejo_users: - { username: "test-passkey-check", email: "test-passkey-check@bitborg.se", role: "user", displayname: "Passkey check" } ``` `forgejo_users` is the single source of truth for both the Kanidm person and the Forgejo account (`vars.yml` header, `kanidm-state.json.j2:5`), so this is the only sanctioned route. The repo has no separate test-account concept and this is an ordinary user in every respect. `role: "user"` keeps it out of `forgejo_admins`. The address needs no mailbox; the domain has a catch-all. ## Quota and seat impact - **No paid seat.** `reconciler_user_exempt` is derived from `forgejo_users` (`roles/reconciler/defaults/main.yml:90`), and the exempt branch is evaluated before the tier branches in `projection.ts` `classifyUser()`, so the account gets the unlimited first-party group rather than the paid 10 GiB one. - **It does join Kanidm `tier_basic`**, because `kanidm-state.json.j2:10` layers every `forgejo_users` entry into that group. `roles/reconciler/defaults/main.yml:23` labels `tier_basic` "paid seat". The Forgejo-side quota is unaffected per the point above, but anything outside this repo that counts `tier_basic` membership as a paying user would count this account. Worth confirming against the billing side before the seat count is trusted. ## After merge Provisioning is deliberately **not** a full `site.yml` run: `health-check` is tagged `always` and sits at the end of the play, so a full run would apply the pending Forgejo 16.0.5 upgrade and only then fail the concealment gate. Scope it: ```bash ansible-playbook site.yml --tags kanidm --skip-tags health-check \ -e '{"kanidm_onboard_users":["test-passkey-check"]}' ``` That creates the Kanidm person and prints one reset link (TTL 24 h). The Forgejo account is not created by a kanidm-tagged run and is not needed for the passkey check; it lands on the next full apply. Every later check mints its own token, no playbook: ```bash kanidm person credential create-reset-token --ttl 86400 test-passkey-check --name idm_admin ``` ## Notes - Removing the entry later deprovisions nothing. Convergence is additive. Decommissioning means deleting the Kanidm person and the Forgejo account by hand. - The Forgejo-side password is random and unrecoverable by design. Sign in via Bitborg Auth only. `ansible-lint` passes (production profile), `site.yml --syntax-check` passes.
supernaut lade till 1 incheckning 2026-09-19 13:23:36 +00:00
chore(kanidm): declare a standing test account for passkey-page checks
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m48s
43c3d415e5
ADR 0038 conceals identity fields with CSS, and the health-check gate blocks a
Forgejo upgrade until the concealment surfaces are re-verified. One of those
surfaces is the Kanidm passkey-enrolment page, which needs a live
credential-reset token, which needs an account. There was none, so that check
could only be run by issuing a reset link against a real person.

Adds one declared user. forgejo_users drives both the Kanidm person and the
Forgejo account, so this is the only sanctioned route; there is no separate
test-account concept in the repo.

The account is exempt from tier quota automatically (reconciler_user_exempt is
derived from forgejo_users), so it holds no paid seat. It does land in the
Kanidm tier_basic group as a side effect of being declared - the reconciler's
exempt check wins over the tier check, so the Forgejo quota is unlimited, but
anything counting tier_basic membership as a paying user would see it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
supernaut sammanfogade incheckning 95c5308340 till main 2026-09-19 13:35:06 +00:00
supernaut tog bort grenen chore/test-passkey-check-user 2026-09-19 13:35:06 +00:00
Logga in för att delta i denna konversation.
Inga granskare
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-infra!484
Ingen beskrivning angiven.