bug(user): admin seed ignores APP_ADMIN_USERNAME / PASSWORD — falls back to defaults (HIGH, prod-blocking) #513

Closed
opened 2026-05-11 16:35:26 +02:00 by marcel · 0 comments
Owner

Summary

UserDataInitializer and application.yaml use different property keys. The Java code reads app.admin.email, but the yaml maps the env var to app.admin.username. They never connect — the env vars APP_ADMIN_USERNAME and APP_ADMIN_PASSWORD are silently ignored, and the admin user is always seeded with the hardcoded defaults admin@familyarchive.local / admin123.

Evidence (from running staging stack)

$ docker exec archiv-staging-backend-1 env | grep APP_ADMIN
APP_ADMIN_USERNAME=marcel@raddatz.cloud
APP_ADMIN_PASSWORD=8V3pHX4AP3nvPw8X54J_ff98kL7FMxit

$ docker logs archiv-staging-backend-1 | grep UserDataInit
UserDataInitializer: Kein Admin-User 'admin@familyarchive.local' gefunden. Erstelle Default-Admin...
UserDataInitializer: Default Admin erstellt: Email='admin@familyarchive.local'

$ psql -c "SELECT email FROM app_users;"
 admin@familyarchive.local

Env vars correctly set in the container; the seeded admin uses defaults anyway.

Code

// UserDataInitializer.java:34
@Value("${app.admin.email:admin@familyarchive.local}")    // reads app.admin.email
private String adminEmail;
# application.yaml:71-73
admin:
  username: ${APP_ADMIN_USERNAME:admin}    # exposes app.admin.username (WRONG KEY)
  password: ${APP_ADMIN_PASSWORD:admin123} # exposes app.admin.password (matches)

Impact

  • 🔴 PRODUCTION BLOCKER. DEPLOYMENT.md §3.5 + ADR-011 explicitly warn that the admin password is locked permanently on first deploy. With this bug, the lock-in is on admin@familyarchive.local / admin123the dev defaults. An operator who sets PROD_APP_ADMIN_PASSWORD to a strong value in Gitea secrets gets a false sense of security; the actual admin credentials are the same dev defaults shipped in the repo.
  • 🟡 Staging confusion: I cannot log in with the credentials we set in Gitea secrets. The admin user exists as admin@familyarchive.local / admin123 (so login works — just not with the intended credentials).

Fix

Rename the yaml key from username: to email: so the Spring property app.admin.email actually exists. Keep env var name APP_ADMIN_USERNAME (Gitea secrets already use it). Also fix the default to be an email-shape:

admin:
  email: ${APP_ADMIN_USERNAME:admin@familienarchiv.local}
  password: ${APP_ADMIN_PASSWORD:admin123}

Alternative considered: change UserDataInitializer.java to read ${app.admin.username:...}. Rejected because the domain field is email, not username — the yaml key was the source of confusion.

After-merge cleanup for staging

The existing admin@familyarchive.local user in the staging DB will persist (UserDataInitializer only seeds if missing). Options:

  • Live with it on staging; log in with admin@familyarchive.local / admin123.
  • Or wipe archiv-staging_postgres-data and re-deploy to get a fresh seed.

For production: the bug must land before v1.0.0, otherwise we permanently lock the prod admin password to admin123.

Discovered

While trying to log into staging with the credentials we set in Gitea secrets via the bootstrap procedure for #497.

## Summary **`UserDataInitializer` and `application.yaml` use different property keys.** The Java code reads `app.admin.email`, but the yaml maps the env var to `app.admin.username`. They never connect — the env vars `APP_ADMIN_USERNAME` and `APP_ADMIN_PASSWORD` are silently ignored, and the admin user is always seeded with the hardcoded defaults `admin@familyarchive.local` / `admin123`. ## Evidence (from running staging stack) ``` $ docker exec archiv-staging-backend-1 env | grep APP_ADMIN APP_ADMIN_USERNAME=marcel@raddatz.cloud APP_ADMIN_PASSWORD=8V3pHX4AP3nvPw8X54J_ff98kL7FMxit $ docker logs archiv-staging-backend-1 | grep UserDataInit UserDataInitializer: Kein Admin-User 'admin@familyarchive.local' gefunden. Erstelle Default-Admin... UserDataInitializer: Default Admin erstellt: Email='admin@familyarchive.local' $ psql -c "SELECT email FROM app_users;" admin@familyarchive.local ``` Env vars correctly set in the container; the seeded admin uses defaults anyway. ## Code ```java // UserDataInitializer.java:34 @Value("${app.admin.email:admin@familyarchive.local}") // reads app.admin.email private String adminEmail; ``` ```yaml # application.yaml:71-73 admin: username: ${APP_ADMIN_USERNAME:admin} # exposes app.admin.username (WRONG KEY) password: ${APP_ADMIN_PASSWORD:admin123} # exposes app.admin.password (matches) ``` ## Impact - **🔴 PRODUCTION BLOCKER.** DEPLOYMENT.md §3.5 + ADR-011 explicitly warn that the admin password is **locked permanently on first deploy**. With this bug, the lock-in is on `admin@familyarchive.local` / `admin123` — *the dev defaults*. An operator who sets `PROD_APP_ADMIN_PASSWORD` to a strong value in Gitea secrets gets a false sense of security; the actual admin credentials are the same dev defaults shipped in the repo. - **🟡 Staging confusion**: I cannot log in with the credentials we set in Gitea secrets. The admin user exists as `admin@familyarchive.local` / `admin123` (so login works — just not with the intended credentials). ## Fix Rename the yaml key from `username:` to `email:` so the Spring property `app.admin.email` actually exists. Keep env var name `APP_ADMIN_USERNAME` (Gitea secrets already use it). Also fix the default to be an email-shape: ```yaml admin: email: ${APP_ADMIN_USERNAME:admin@familienarchiv.local} password: ${APP_ADMIN_PASSWORD:admin123} ``` Alternative considered: change `UserDataInitializer.java` to read `${app.admin.username:...}`. Rejected because the domain field is `email`, not `username` — the yaml key was the source of confusion. ## After-merge cleanup for staging The existing `admin@familyarchive.local` user in the staging DB will persist (UserDataInitializer only seeds if missing). Options: - Live with it on staging; log in with `admin@familyarchive.local` / `admin123`. - Or wipe `archiv-staging_postgres-data` and re-deploy to get a fresh seed. For production: the bug must land **before** `v1.0.0`, otherwise we permanently lock the prod admin password to `admin123`. ## Discovered While trying to log into staging with the credentials we set in Gitea secrets via the bootstrap procedure for #497.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#513