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 <noreply@anthropic.com>
This commit is contained in:
chrisfu 2026-05-09 22:15:50 -07:00
parent 5d545d238c
commit d5f6e8f6a9

View File

@ -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 ─────────────────────────────────────────────────