Skip to content

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

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

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

Conversation

@cristim

@cristim cristim commented May 20, 2026

Copy link
Copy Markdown
Member

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

@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

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@cristim, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 59 minutes and 40 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae08ce61-80e7-431e-99e9-30c428d6b601

📥 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 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

cristim commented May 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor
✅ 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

cristim commented May 20, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor
✅ 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

cristim commented May 22, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor
✅ 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

cristim commented May 22, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor
✅ 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

cristim commented May 27, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
✅ 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

cristim commented May 27, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@cristim

cristim commented May 27, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@cristim

cristim commented May 28, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

cristim added a commit that referenced this pull request May 28, 2026
… + test

Issue #351 acceptance criterion 2 asked for a migration-level idempotent
backfill. PR #533 backfilled only in Go (assignAdminGroupAndWarn), which fires
only when RunMigrations gets a non-empty admin email. A DB restored from a
backup, or migrated without ADMIN_EMAIL set, never re-applied the backfill to
pre-existing drifted admin rows. Migration 000024's backfill is one-shot at its
version and does not re-run on an already-migrated DB.

- Add migration 000053_backfill_admin_group_ids: the same idempotent backfill
  as 000024 (DISTINCT unnest dedupe, EXISTS guard, only touches empty group_ids
  so operator customisation is preserved), applied at migrate time regardless of
  how the deployment invokes migrations. Down is a documented no-op (additive
  backfill has no safe reverse).
- Add integration test TestMigration_BackfillAdminGroupIDs covering the
  restore / no-admin-email path: it runs migrations with NO admin email so the
  Go backfill cannot fire, proving the SQL migration repairs a drifted admin row,
  and asserts idempotency on re-apply. Verified to fail when 000053 is neutered.

The #351 group-assignment invariant already runs in default CI: ci.yml's
integration-tests job runs `go test -tags=integration ./...` against a postgres
service and is required by the ci-success gate, so TestEnsureAdminUser_GroupAssignment
is exercised on every PR.

Refs #546
cristim added a commit that referenced this pull request May 28, 2026
… admin group (closes #545, #546) (#579)

* test(database): #440 route admin group backfill log to stderr + cover it

The #440 stdout-leak fix routed the per-user admin messages to the stdlib
logger (stderr) but left the group_ids backfill line in assignAdminGroupAndWarn
on fmt.Printf, which writes to stdout. The existing unit regression test could
not catch it because it uses an unreachable pool, so the backfill branch never
runs.

- migrate.go: switch the "Backfilled ..." line from fmt.Printf to log.Printf so
  every admin-activity message in the file stays on the stderr-bound logger.
- Add integration regression test TestAssignAdminGroup_BackfillLogsToStderr_NotStdout
  that seeds a drifted admin against a real container, runs the real
  ensureAdminUser path, and asserts the backfill message lands on stderr and
  never on stdout. Verified to fail when the line is reverted to fmt.Printf.

Refs #545

* test(database): #351 add SQL-level admin group_ids backfill migration + test

Issue #351 acceptance criterion 2 asked for a migration-level idempotent
backfill. PR #533 backfilled only in Go (assignAdminGroupAndWarn), which fires
only when RunMigrations gets a non-empty admin email. A DB restored from a
backup, or migrated without ADMIN_EMAIL set, never re-applied the backfill to
pre-existing drifted admin rows. Migration 000024's backfill is one-shot at its
version and does not re-run on an already-migrated DB.

- Add migration 000053_backfill_admin_group_ids: the same idempotent backfill
  as 000024 (DISTINCT unnest dedupe, EXISTS guard, only touches empty group_ids
  so operator customisation is preserved), applied at migrate time regardless of
  how the deployment invokes migrations. Down is a documented no-op (additive
  backfill has no safe reverse).
- Add integration test TestMigration_BackfillAdminGroupIDs covering the
  restore / no-admin-email path: it runs migrations with NO admin email so the
  Go backfill cannot fire, proving the SQL migration repairs a drifted admin row,
  and asserts idempotency on re-apply. Verified to fail when 000053 is neutered.

The #351 group-assignment invariant already runs in default CI: ci.yml's
integration-tests job runs `go test -tags=integration ./...` against a postgres
service and is required by the ci-success gate, so TestEnsureAdminUser_GroupAssignment
is exercised on every PR.

Refs #546

* test(database): renumber backfill_admin_group_ids migration 000053 -> 000056

PR #614 merged 000053_executions_account_fk_restrict on the base branch
while this PR was open. Renumber the backfill migration to 000056 (next
free slot after 000055_add_paused_status) to clear the collision, and
update the test comment references to track the new number.

The migration files are renamed via `git mv` to preserve history. The
SQL contents are unchanged.

* test(database): consolidate stdout/log capture helpers into helpers_test.go

Address CodeRabbit nitpick on PR #579: drop the duplicate
captureStdoutIntegration / captureLogOutputIntegration helpers from
migrate_security_integration_test.go and centralise the integration-tag
copies in helpers_test.go (same package, same build tag).

The duplication with migrate_security_test.go's captureStdout /
captureLogOutput is forced by a package boundary (that file lives in
`package migrations`, while integration tests live in `package
migrations_test`), so the helpers cannot be shared across files; the
new copies in helpers_test.go reuse the same names and carry a comment
pointing at the unit-test originals.
@cristim

cristim commented May 30, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@cristim have exceeded the limit for the number of chat messages per hour. Please wait 54 minutes and 10 seconds before sending another message.

@cristim

cristim commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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 cristim closed this Jun 5, 2026
@cristim
cristim force-pushed the fix/issue-351-admin-group branch from 0e92300 to 69b857a Compare June 5, 2026 09:37
@cristim

cristim commented Jun 5, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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