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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GT37Z1Xtfd9pUuQtMTg6Yt
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-30 14:06:55 +02:00
co-authored by Claude Fable 5.1
parent 5bc021de77
commit c4e2de5efa
3 changed files with 28 additions and 5 deletions
+4
View File
@@ -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<version>` tag; the first. A release is a Cargo.toml bump plus a `v<version>` tag; the
content repos pin the tag they run (see the end of this file). 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) ## 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. - 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.
+5 -4
View File
@@ -64,10 +64,11 @@ Unset, a `KANIDM_API_TOKEN` means Kanidm and no token means
memberships. With memberships: memberships. With memberships:
- A desk invite writes an `invited` record and mails "log in at the - 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 site, and your place is there". The provider makes people, portal
makes people, portal makes members. So the provider has to be one makes members: with Vipps Login everyone can already sign in. A
everyone can already sign in to, which is what Vipps Login is for a Kanidm still configured beside memberships (`KANIDM_API_TOKEN`)
Norwegian association, and what Kanidm is not. 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 - `grants` writes a record for the person the case is about, from the
form's name and phone number or address. form's name and phone number or address.
- `SEED_ADMIN_PHONE` or `SEED_ADMIN_EMAIL` writes the first person's. - `SEED_ADMIN_PHONE` or `SEED_ADMIN_EMAIL` writes the first person's.
+19 -1
View File
@@ -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"))?; 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 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)
}
} }
} }