Skip to content

fix(auth): auto-assign bootstrap admin to Administrators group (closes #351)#533

Open
cristim wants to merge 1 commit into
feat/multicloud-web-frontendfrom
fix/issue-351-admin-group
Open

fix(auth): auto-assign bootstrap admin to Administrators group (closes #351)#533
cristim wants to merge 1 commit into
feat/multicloud-web-frontendfrom
fix/issue-351-admin-group

Conversation

@cristim
Copy link
Copy Markdown
Member

@cristim cristim commented May 20, 2026

Summary

  • Seeds group_ids with the Administrators group UUID on both ensureAdminUser INSERT paths (no-password and with-password) so a bootstrap admin has group-based permissions from the first boot.
  • Adds assignAdminGroupAndWarn helper that runs an idempotent backfill UPDATE after each ensureAdminUser call, repairing any pre-existing admin rows whose group_ids drifted to empty (e.g. from an out-of-band manual DB seed).
  • Logs a WARN after the backfill if any admin rows still have empty group_ids (signals the Administrators group row is missing from the DB), giving operators visibility in container logs.

The defaultAdminGroupID constant is duplicated as a package-private literal instead of imported from internal/auth to preserve the correct dependency direction (auth depends on DB, not the reverse).

Integration test (ensure_admin_user_test.go, build tag integration) covers five scenarios: fresh insert no-password, fresh insert with-password, post-migration drift repair, idempotency under repeated boots, and operator-customisation preservation.

Test plan

  • go test -tags integration ./internal/database/postgres/migrations/... -run TestEnsureAdminUser_GroupAssignment passes (requires a running postgres)
  • go build ./... passes
  • go vet ./... passes

Closes #351

Summary by CodeRabbit

Release Notes

  • Tests

    • Added integration tests for admin user provisioning and group assignment during database migrations.
  • Bug Fixes

    • Improved admin user initialization to ensure admins are assigned to the Administrators group by default.
    • Added safeguards to prevent admin users from losing group permissions during migration operations.

Review Change Stack

ensureAdminUser and ensureAdminUserWithPassword in
internal/database/postgres/migrations/migrate.go insert admin rows
without populating group_ids. A bootstrap admin (via ADMIN_EMAIL +
ADMIN_PASSWORD_SECRET) ended up with role='admin' but empty
group_ids, so the permissions system saw no group memberships and
group-based features (frontend rendering, group-based authorisation)
behaved incorrectly.

Migration 000024_seed_default_groups already backfills existing
admins at migration time, but it runs only once. The bootstrap path
fires on every container boot, AFTER migrations are at head, so any
admin inserted by ensureAdminUser bypassed the backfill entirely.

Fix:

- INSERT statements in both ensureAdminUser variants now seed
  group_ids with the Administrators group UUID
  (00000000-0000-5000-8000-000000000001).
- A new assignAdminGroupAndWarn helper runs an idempotent backfill
  UPDATE after each ensureAdminUser call. It targets any admin row
  whose group_ids drifted to empty (NULL or zero-length) - e.g.
  from an out-of-band manual DB seed - so post-migration drift
  self-heals on the next container boot. The DISTINCT(unnest(...))
  dedupe makes the UPDATE safe to run repeatedly.
- After the backfill, a defensive SELECT counts admins still
  showing empty group_ids and logs a WARN so operators see drift
  in container logs rather than only via a broken UI. This is the
  "defence-in-depth invariant" described in the issue body.
- Operator customisation (non-empty group_ids that deliberately
  omits the default admin group) is preserved - the WHERE clause
  is gated on cardinality(group_ids) = 0.

The defaultAdminGroupID constant is duplicated as a package-private
literal rather than imported from internal/auth, because the
migrations package must not depend on the auth package - auth
depends on the DB, not the reverse.

Integration test (ensure_admin_user_test.go, build tag
'integration') covers five scenarios: fresh insert (no-password),
fresh insert (with-password), post-migration drift repair,
idempotency under repeated boots, and operator-customisation
preservation. All five pass against a postgres:16-alpine test
container.

Closes #351
@cristim cristim added triaged Item has been triaged priority/p2 Backlog-worthy severity/medium Moderate harm urgency/this-sprint Within the current sprint impact/many Affects most users effort/s Hours type/bug Defect labels May 20, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a133266-6966-425d-8598-30fecc9b7a4f

📥 Commits

Reviewing files that changed from the base of the PR and between b1ea4b1 and 0e92300.

📒 Files selected for processing (2)
  • internal/database/postgres/migrations/ensure_admin_user_test.go
  • internal/database/postgres/migrations/migrate.go

📝 Walkthrough

Walkthrough

This PR fixes a gap in admin user bootstrap: admin rows created via the migration helper did not receive the default Administrators group. The change adds group_ids seeding to both creation paths, introduces an idempotent backfill/validation step, and includes integration tests covering fresh creation, self-healing, idempotency, and preservation of operator customization.

Changes

Admin Group Assignment in Bootstrap

Layer / File(s) Summary
Admin group constant and backfill/validation helper
internal/database/postgres/migrations/migrate.go
Defines the hardcoded Administrators UUID as a package constant and adds assignAdminGroupAndWarn, which appends the group ID to any admin rows with empty group_ids and logs a WARN if drift persists.
Admin creation paths with group seeding
internal/database/postgres/migrations/migrate.go
Updates ensureAdminUser (no-password) and ensureAdminUserWithPassword (password) to seed group_ids on insert, calls the backfill helper after creation, and clarifies that existing customized group_ids are not overwritten on conflict.
Integration test for admin group scenarios
internal/database/postgres/migrations/ensure_admin_user_test.go
Integration test with multiple subtests that verify fresh admin creation includes the default group, empty group_ids are self-healed, repeated migrations avoid duplicates, and operator-customized group_ids are preserved.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • LeanerCloud/CUDly#393: Implements the same admin group seeding fix with identical backfill helper and integration test scenarios.

Poem

🐰 An admin bootstraps, yet groups lie bare—
Now migrations mend with seed and care.
Self-heal thy drift, idempotent and true,
No duplicates haunt, no customization's lost in the zoo!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(auth): auto-assign bootstrap admin to Administrators group (closes #351)' directly and clearly summarizes the main change: ensuring bootstrap admins are assigned to the Administrators group.
Linked Issues check ✅ Passed All four acceptance criteria from #351 are met: group_ids populated on insert, idempotent backfill for existing admin rows, startup WARN invariant check added, and integration test validates the scenarios.
Out of Scope Changes check ✅ Passed All changes are in-scope and directly address #351 objectives: seeding group_ids on insert, adding backfill logic, implementing startup invariant, and testing the fix.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-351-admin-group

Comment @coderabbitai help to get the list of available commands and usage tips.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 20, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
Copy link
Copy Markdown
Member Author

cristim commented May 22, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/many Affects most users priority/p2 Backlog-worthy severity/medium Moderate harm triaged Item has been triaged type/bug Defect urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant