Files
infrastructure/kanidm-setup.sh
T
blandClaude Opus 5 b7e8e0313e
deploy / deploy (push) Successful in 33s
Match the Kanidm server to the CLI, and say why a token failed
Three things in one setup run, two of them mine.

The server image was 1.11.1 while the host's CLI is 1.11.2, and a
1.11.2 client looks up the domain entry at a UUID 1.11.1 does not
have. So `system domain set-displayname` and `set-image` both failed
with "Item not found", which says nothing about versions. The CLI had
been warning about the mismatch on every single call. Image bumped to
1.11.2; the README says to keep them together and which one to move.

The onboarding token asked for `--rw`, and the flag is spelled
`--readwrite`. The script swallowed stderr and reported a bare warning,
so a step that leaves every invite failing closed said nothing about
why. It now passes the right flag, and if it still fails it says what
the CLI said and prints the command to retry by hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 22:24:08 +02:00

106 lines
5.1 KiB
Bash
Executable File

#!/bin/sh
# The portal's OAuth2 client and one Kanidm group per desk, mapped into
# the `groups` claim under the names the pages use in `qualifies`.
# Portal reads that claim at login (portal src/auth.rs). Rerunnable.
#
# Logs in first (interactive, idm_admin's password from bootstrap.sh),
# then writes the client secret into .env and portal.env and restarts
# the portal. Needs the kanidm CLI on this machine.
set -eu
cd "$(dirname "$0")"
. ./.env
K="kanidm -D idm_admin -H https://$ID_HOST"
C=$OAUTH2_CLIENT_ID
# Already signed in? Then do not ask again: this script is meant to be
# rerunnable, and the session outlives a single run.
$K self whoami >/dev/null 2>&1 || $K login
has_client() { $K system oauth2 get "$1" 2>/dev/null | grep -q '^name:'; }
has_group() { $K group get "$1" 2>/dev/null | grep -q '^name:'; }
has_client $C || $K system oauth2 create $C "$SITE_NAME" "https://$PORTAL_HOST"
$K system oauth2 add-redirect-url $C "https://$PORTAL_HOST/auth/callback" || true
# The desk groups: every group the content gates a directory on. Keep
# this list equal to the `qualifies` values under questions/.
has_group tomtervel_members || $K group create tomtervel_members
for g in kasserer styret komiteer nabohjelp arrangementer elvesti horingsinstans lekeplasser miljogate pendlerforhold trafikk; do
has_group tomtervel_$g || $K group create tomtervel_$g
$K group add-members tomtervel_members tomtervel_$g
done
# Portal asks for openid, profile and email; groups arrive as a claim.
$K system oauth2 update-scope-map $C tomtervel_members openid profile email
for g in kasserer styret komiteer nabohjelp arrangementer elvesti horingsinstans lekeplasser miljogate pendlerforhold trafikk; do
$K system oauth2 update-claim-map $C groups tomtervel_$g $g
done
$K system oauth2 update-claim-map-join $C groups array
# What a member sees when they land on the login page. Out of the box
# Kanidm says "kanidm" and shows its own mark, which tells a neighbour
# nothing about whose site they are signing in to - and a login page
# that looks like it belongs to no one is the kind of thing people
# rightly hesitate over.
#
# The logo comes from the content repo, because that is already where
# this site's identity lives: the same file site.yaml uses as the
# favicon. Branding follows the content, not this repo.
$K system domain set-displayname "$SITE_NAME" || true
$K system oauth2 set-displayname $C "$SITE_NAME" || true
logo=$(mktemp --suffix=.svg)
if curl -sfL "$CONTENT_REPO/raw/branch/$CONTENT_BRANCH/images/logo.svg" -o "$logo" && [ -s "$logo" ]; then
$K system domain set-image "$logo" svg || echo "note: could not set the instance logo"
$K system oauth2 set-image $C "$logo" svg || echo "note: could not set the client logo"
else
echo "note: no images/logo.svg in the content repo - leaving the default mark"
fi
rm -f "$logo"
secret=$($K system oauth2 show-basic-secret $C 2>/dev/null | tail -1)
sed -i "s|^OAUTH2_CLIENT_SECRET=.*|OAUTH2_CLIENT_SECRET=$secret|" .env portal.env
# Onboarding from a desk. A committee inviting a neighbour, and any
# state whose `grants:` makes someone a member, both go through Kanidm
# as this service account - not as a person, and not as an admin. It is
# in idm_people_on_boarding, which may create people and issue a first
# credential reset and nothing else, so the token cannot touch anyone's
# existing credentials. idm_people_pii_read lets an invite find someone
# who already has an account by their email, and add them to the group
# instead of making a second account for the same person.
#
# The token is shown once, by Kanidm, at creation. Written straight into
# the env files here and never printed.
SA=portal-onboarding
if ! $K service-account get $SA >/dev/null 2>&1; then
$K service-account create $SA "Portal onboarding" idm_admin
$K group add-members idm_people_on_boarding $SA
$K group add-members idm_people_pii_read $SA || true
fi
if ! grep -q '^KANIDM_API_TOKEN=.' portal.env 2>/dev/null; then
token=$($K service-account api-token generate $SA "portal" --readwrite | tail -1)
if [ -n "$token" ]; then
grep -q '^KANIDM_API_TOKEN=' portal.env \
&& sed -i "s|^KANIDM_API_TOKEN=.*|KANIDM_API_TOKEN=$token|" portal.env \
|| printf 'KANIDM_API_TOKEN=%s\n' "$token" >> portal.env
echo "onboarding token written to portal.env"
else
# Loudly, and with whatever the CLI said: the first version of this
# passed `--rw` for a flag that is spelled `--readwrite`, swallowed
# the error, and reported a bare warning that said nothing about
# why. A step that leaves invites broken should not be quiet about
# how it failed.
echo "warning: could not generate the onboarding token - invites will fail closed until it is set" >&2
echo " retry by hand: $K service-account api-token generate $SA portal --readwrite" >&2
fi
fi
podman compose restart portal
echo "client $C configured; portal restarted with its secret"
echo
echo "Give people their desk (membership is read at login):"
echo " $K group add-members tomtervel_styret <person>"
echo "Create a person:"
echo " $K person create <name> '<Display Name>' && $K person update <name> --mail <email>"
echo " $K person credential create-reset-token <name>"