Commit Graph
2 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 ea2e373e1c fix(discourse): only set the admin password when it is not already the one set
Job 104153193860 died at "Ensure the administrator account carries the current
password" with:

    Validation failed: Password is the same as your current password.

Discourse's UserPasswordValidator adds :same_as_current when password_hash_
changed? and confirm_password?(value) are both true. Assigning the current
password again satisfies both, because the reassignment salts a fresh hash. The
script assigned unconditionally, so any run that did not follow a rotation was a
hard failure rather than a no-op, and the task was never idempotent.

The assignment is now guarded by confirm_password?, which makes the rule
unreachable: it requires confirm_password? to be true, and that is exactly when
nothing is assigned. A new user is covered without a new_record? check, since
User#confirm_password? returns false when no user_password row exists.

web-app-openproject/files/ruby/ensure_admin_user.rb already guards the same way
with check_password?; this brings Discourse in line with it.

The two saves collapse into one while we are here. The second existed only to
set `active`, and one save writes username, password and active together.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 00:56:01 +02:00
kevinveenbirkenbachandClaude Opus 5 e11c9517b2 fix(discourse): set runtime state at runtime, not during bootstrap
All 22 run: hooks in app.yml were rails and rake calls against the database,
yet the launcher executes them only while building the image. Two of them
carried rotating values, the OIDC client secret and the administrator
password, so every rotation forced a full rebuild with bundle install and
asset precompile: 961.82s in one pass and 13m16s in the next, on a job that
then missed its wall-clock budget by 201s.

They now run against the running instance through container exec, following
the pattern web-app-openproject already uses: real .rb files under files/ruby,
values through ENV.fetch, a readback that prints booleans rather than values,
and an assert that fails the deploy when a rotated credential did not arrive.
ensure_admin_user.rb mirrors the Y/Y path of admin:create using upstream's own
grant_admin!, change_trust_level! and activate; setting the flags by hand had
skipped auto_approve_user, the staff welcome message, the notification levels,
trust level 1 and the email confirmation.

What stays in run: is the CA trust call, which writes into the image and would
not survive a recreate. With the rest gone, the bootstrap digest f47ea0e5bb
records covers templates, expose, params, volumes, after_code and docker_args,
and nothing in that set rotates.

00_core.yml carried the same IS_STACK_HOST condition three times; it now
includes 04_stack_host.yml once, which holds the three with their own
conditions, and their implementations moved under tasks/stack_host/.

The OIDC values are read only when OIDC is enabled. Ansible resolves vars
lazily, so referencing DISCOURSE_OIDC_LOGOUT_REDIRECT_URL unconditionally
would have run its lookup against web-svc-logout on a deployment that has no
SSO, failing where the template did not.

test_oidc_setting_order.py follows the settings into apply_settings.rb. The
invariant 88218fc740 introduced is unchanged: openid_connect_enabled comes
after the discovery document, the client id and the client secret.

Four explanatory "# Exception:" comments are removed, two of them from
f47ea0e5bb. They described mechanisms rather than naming a trip-wire, which
belongs in a commit body.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-14 16:07:15 +02:00