fix: make only the very first user admin, add REGISTRATION_ENABLED #38
Loading…
Reference in a new issue
No description provided.
Delete branch "jtk/registration-first-admin"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #12.
Problems (from the audit):
ensure_first_user_is_admin/0ran after every registration and re-promoted the oldest user to admin, even if an admin had demoted them on purpose.Fix:
Accounts.insert_user/1, used byregister_user/1,register_user_with_password/1andcreate_user_from_changeset/1(OIDC). It takes a Postgres advisory transaction lock, and setsrole: "admin"only if the users table is empty. The first user is logged (First user ID … made admin).ensure_first_user_is_admin/0and its two calls in the registration controller are removed.REGISTRATION_ENABLED=true|false(also 1/0, yes/no, on/off; anything else fails at boot) fixes registration on or off. When it's unset, the admin setting decides, as before. On Admin → Users the toggle is disabled with a note when the env var is set, and the event handler refuses it too. Added to.env.sample.config/test.exssetsfirst_user_admin: falseso fixtures stay plain users and async tests don't queue on the lock; the new first-user tests turn it back on.Default when nothing is set (Jeremy's call, Oct 18): open only until the first user exists.
registration_enabled?/0now resolves:REGISTRATION_ENABLEDset → env wins; admin setting saved → setting wins; neither → open only while the users table is empty. A new migration drops theregistration_enabled = truerow that the original settings migration seeded, but only when it's untouched (value = 'true' AND inserted_at = updated_at), so the default applies; rows an admin saved are kept. Admin → Users shows "Not set yet: by default sign-ups are open only until the first account exists" until the setting is saved..env.sampleandconfig/runtime.exscomments say the same.Also (from Nye's re-review): OIDC sign-ups are always created as
"user";insert_user/1is the only place that grants first-user admin, inside the lock, and forces"user"for everyone else.create_user_by_adminbypasses it so admin-chosen roles are kept.Release note: Registration now defaults to open only until the first account exists. Existing instances that never saved the registration setting will close registration after upgrading; set
REGISTRATION_ENABLED=trueor turn it on in Admin → Users to keep it open. OIDC sign-ups still ignore this setting (tracked in #42).Tests:
first_user_admin_test.exs(first user admin / second not, email path, demoted first user stays demoted, OIDC changeset path, env override vs setting) andRegistrationEnvTest(no users → open; one user, nothing set → closed; saved true → open; saved false → closed even with no users; env true/false override both),admin_users_live_test.exs(default hint, toggle unlocked / locked), registration controller (redirects once a user exists).mix precommit: 531 passed.Review by Nye: APPROVED (at
0c8e3bb)Precedence is right: env REGISTRATION_ENABLED → saved setting (parse_value) → Accounts.first_user?(). OIDC never calls registration_enabled?, so OIDC installs are unaffected (release note says so, #42).
Race: two concurrent sign-ups on an empty install both pass the open check, but insert_user's advisory lock makes only the first admin and forces 'user' on the second, so the worst case is an extra plain user, never two admins. Acceptable.
Migration: the 2025 seed inserted both timestamps with NOW() in one statement, so inserted_at = updated_at holds for untouched rows; any admin toggle changes value and so bumps updated_at, so saved rows survive; down re-inserts true with ON CONFLICT DO NOTHING.
Release note is accurate (existing instances that never saved the setting close after upgrade; set REGISTRATION_ENABLED=true or toggle in Admin → Users). Tests cover all 6 precedence cases plus UI hint and the /users/register redirect. CI green on
0c8e3bb, mergeable, no conflicts with maine473a95.Non-blocking nit (no change needed for v0.6.0): with nothing saved and users present, the layout's registration_enabled? does a users COUNT per page render; trivial cost, could be cached later.