From c4e2de5efa5e763e325c914012844032d3d517ef Mon Sep 17 00:00:00 2001 From: Bendik Aagaard Lynghaug Date: Wed, 30 Sep 2026 14:06:55 +0200 Subject: [PATCH] Memberships beside Kanidm: the invite makes the account too Kanidm makes people, portal makes members. A site can switch to memberships before its sign-in provider is one everyone already has. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01GT37Z1Xtfd9pUuQtMTg6Yt --- CHANGELOG.md | 4 ++++ docs/memberships.md | 9 +++++---- src/people.rs | 20 +++++++++++++++++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ef7f4b..13ab6f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ One line per change, grouped by the release that shipped it. Newest first. A release is a Cargo.toml bump plus a `v` tag; the content repos pin the tag they run (see the end of this file). +## Unreleased + +- With memberships as the people backend and a Kanidm token still set, an invite makes the Kanidm account as well (no group, the one-time link in the mail): Kanidm makes people, portal makes members, and a site can switch to memberships before its sign-in provider is one everyone already has. + ## 0.5.8 (2026-09-30) - Fixed: 0.5.7 asked every provider for the `phoneNumber` scope, and Kanidm denies a sign-in that asks for a scope the person does not hold, so nobody could sign in. The scope is asked for only with `OIDC_EXTRA_SCOPES=phoneNumber` (Vipps). 0.5.7 must not be deployed on a Kanidm site. diff --git a/docs/memberships.md b/docs/memberships.md index a619a02..2c6080a 100644 --- a/docs/memberships.md +++ b/docs/memberships.md @@ -64,10 +64,11 @@ Unset, a `KANIDM_API_TOKEN` means Kanidm and no token means memberships. With memberships: - A desk invite writes an `invited` record and mails "log in at the - site, and your place is there". No account is made: the provider - makes people, portal makes members. So the provider has to be one - everyone can already sign in to, which is what Vipps Login is for a - Norwegian association, and what Kanidm is not. + site, and your place is there". The provider makes people, portal + makes members: with Vipps Login everyone can already sign in. A + Kanidm still configured beside memberships (`KANIDM_API_TOKEN`) + makes the account too, with no group and the one-time link in the + mail, so a site can switch to memberships before its provider does. - `grants` writes a record for the person the case is about, from the form's name and phone number or address. - `SEED_ADMIN_PHONE` or `SEED_ADMIN_EMAIL` writes the first person's. diff --git a/src/people.rs b/src/people.rs index e7dfda7..8a4a71a 100644 --- a/src/people.rs +++ b/src/people.rs @@ -61,7 +61,25 @@ impl People { let email = who.email.as_deref().ok_or_else(|| anyhow::anyhow!("Kanidm knows people by their address, and this one has none"))?; kanidm.invite(name, email, group).await } - People::Memberships => crate::memberships::grant(state, group, name, who, by).await, + People::Memberships => { + let mut invited = crate::memberships::grant(state, group, name, who, by).await?; + // A Kanidm beside memberships makes the person, not the + // member: an account with no group, and the one-time + // link, so someone invited by address can sign in + // before the provider is one everyone already has. + if let (Some(kanidm), Some(email)) = (Kanidm::from_env(), who.email.as_deref()) { + match kanidm.account(name, email).await { + Ok(account) => { + invited.username = account.username; + invited.reset_link = account.reset_link; + invited.link_expires_ms = account.link_expires_ms; + invited.created = account.created; + } + Err(e) => tracing::warn!(group, %email, "the membership is on file, but no Kanidm account could be made: {e}"), + } + } + Ok(invited) + } } }