From d5f6e8f6a912bd74a6906c6fb4e500736d5585a7 Mon Sep 17 00:00:00 2001 From: chrisfu Date: Sat, 9 May 2026 22:15:50 -0700 Subject: [PATCH] fix(gitea): set recovery password after admin promotion, not before Gitea's AdminEditUser PATCH with source_id:0 resets the password field as a side effect when applied to a SPNEGO-registered user (the auth record re-initialisation clears the local password). Setting the password before the promotion PATCH meant it was immediately wiped. Move the 1Password recovery password step to run after the admin promotion PATCH so the final Gitea state matches 1Password. Co-Authored-By: Claude Sonnet 4.6 --- etc/init_knoe_users.sh | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/etc/init_knoe_users.sh b/etc/init_knoe_users.sh index 7297d50..5a3a8bd 100755 --- a/etc/init_knoe_users.sh +++ b/etc/init_knoe_users.sh @@ -1165,24 +1165,6 @@ promote_gitea_admin() { return 0 fi - # Set KNOE_ADMIN_PRINCIPAL's recovery password unconditionally — regardless of - # which token path succeeded above. Primary auth is Kerberos SPNEGO; this gives - # every admin a 1Password-backed fallback for break-glass access. - # Skipped gracefully if op is unavailable (automated CI, no 1Password session). - if _op_ensure_auth 2>/dev/null; then - local _pw="" - if _pw=$(gitea_ensure_password "${KNOE_ADMIN_PRINCIPAL}" 2>/dev/null); then - log "Gitea: setting recovery password for '${KNOE_ADMIN_PRINCIPAL}' via admin REST API ..." - gitea_api_set_password "$token" "${KNOE_ADMIN_PRINCIPAL}" "$_pw" \ - && log "Gitea: recovery password set for '${KNOE_ADMIN_PRINCIPAL}'" \ - || true - _pw="" - else - warn " Gitea: 1Password unavailable (op not signed in?) — skipping fallback password set" - warn " Run 'op signin' on $(hostname) and re-run to store recovery credentials" - fi - fi - # Persist token in k8s secret for automation reuse (tokens are revocable) kubectl -n "$GITEA_NAMESPACE" create secret generic gitea-admin-token \ --from-literal=token="$token" \ @@ -1221,6 +1203,10 @@ promote_gitea_admin() { existing_email="${KNOE_ADMIN_EMAIL:-${KNOE_ADMIN_PRINCIPAL}@${GITEA_HOST}}" fi + # Admin promotion — sets admin:true. Must happen BEFORE the recovery password + # is set, because Gitea's AdminEditUser with source_id:0 on a SPNEGO-registered + # user can reset the password field as a side effect of re-initialising the auth + # record. Setting the password last guarantees it survives the promotion PATCH. resp=$(curl -s -w '\n__HTTP_STATUS__%{http_code}' \ -X PATCH \ -H "Authorization: token ${token}" \ @@ -1235,6 +1221,24 @@ promote_gitea_admin() { else warn "Gitea admin promotion returned HTTP ${http_code:-???} (${resp:0:200})" fi + + # Set recovery password AFTER admin promotion. The promotion PATCH (source_id:0) + # can reset the password as a side effect on SPNEGO-registered users; setting it + # last ensures 1Password and Gitea agree on the same value. + # Skipped gracefully if op is unavailable (automated CI, no 1Password session). + if _op_ensure_auth 2>/dev/null; then + local _pw="" + if _pw=$(gitea_ensure_password "${KNOE_ADMIN_PRINCIPAL}" 2>/dev/null); then + log "Gitea: setting recovery password for '${KNOE_ADMIN_PRINCIPAL}' via admin REST API ..." + gitea_api_set_password "$token" "${KNOE_ADMIN_PRINCIPAL}" "$_pw" \ + && log "Gitea: recovery password set for '${KNOE_ADMIN_PRINCIPAL}'" \ + || true + _pw="" + else + warn " Gitea: 1Password unavailable (op not signed in?) — skipping recovery password set" + warn " Run 'op signin' on $(hostname) and re-run to store recovery credentials" + fi + fi } # ── GitLab admin promotion ─────────────────────────────────────────────────