fix(auth): make founding-admin bootstrap atomic #96

Merged
icub3d merged 2 commits from feature/79-bootstrap-race into main 2026-07-09 03:03:04 +00:00
Owner

Summary

Fixes #79. The first user became admin via a check-then-act: create_user, then if list_users().len() == 1. Two concurrent first-time registrations can interleave (A creates, B creates, then A lists → 2) so that neither becomes admin — leaving the server with no administrator (or, with the opposite interleaving, two admins).

Fix

New try_claim_admin(user_id) runs a single atomic conditional insert:

INSERT INTO member_roles (user_id, role_id)
SELECT ?, 'admin'
WHERE NOT EXISTS (SELECT 1 FROM member_roles WHERE role_id = 'admin')

It grants @admin only if no member already holds it and returns whether this call won. verify uses the boolean to decide the founding-admin path. Exactly one registration wins regardless of concurrency.

Tests

  • only_one_user_can_claim_admin (storage): second claim returns false; exactly one admin.
  • concurrent_first_registrations_produce_exactly_one_admin (e2e): two tokio::join!-ed registrations yield exactly one @admin.

Full server suite green (117 tests); clippy -D warnings clean.

Closes #79

## Summary Fixes #79. The first user became admin via a check-then-act: `create_user`, then `if list_users().len() == 1`. Two concurrent first-time registrations can interleave (A creates, B creates, then A lists → 2) so that **neither** becomes admin — leaving the server with no administrator (or, with the opposite interleaving, two admins). ## Fix New `try_claim_admin(user_id)` runs a single atomic conditional insert: ```sql INSERT INTO member_roles (user_id, role_id) SELECT ?, 'admin' WHERE NOT EXISTS (SELECT 1 FROM member_roles WHERE role_id = 'admin') ``` It grants `@admin` only if no member already holds it and returns whether this call won. `verify` uses the boolean to decide the founding-admin path. Exactly one registration wins regardless of concurrency. ## Tests - `only_one_user_can_claim_admin` (storage): second claim returns false; exactly one admin. - `concurrent_first_registrations_produce_exactly_one_admin` (e2e): two `tokio::join!`-ed registrations yield exactly one `@admin`. Full server suite green (117 tests); `clippy -D warnings` clean. Closes #79
fix(auth): make founding-admin bootstrap atomic
Some checks failed
CI / Client — test & lint (pull_request) Successful in 49s
Security Audit / Semgrep (SAST) (pull_request) Failing after 58s
Security Audit / Gitleaks (Secrets) (pull_request) Successful in 7s
Security Audit / Trivy (SCA & IaC) (pull_request) Failing after 15s
Security Audit / PNPM Audit (JS SCA) (pull_request) Failing after 21s
CI / Server — test & lint (pull_request) Successful in 3m48s
Security Audit / Cargo Audit (Rust SCA) (pull_request) Failing after 5m23s
b8bd9fe647
The first user became admin via a check-then-act on list_users().len() == 1
performed after create_user. Two concurrent first-time registrations could
interleave so that both saw a count > 1 and neither became admin (leaving
the server with no administrator) — or, with the opposite ordering, both.

Replace it with SessionStore-free atomic claim: try_claim_admin runs a
single conditional INSERT granting @admin only if no member already holds
it, and reports whether this call won. Exactly one registration becomes the
founding admin regardless of concurrency.

Tests: storage-level only_one_user_can_claim_admin (second claim denied,
exactly one admin), and an end-to-end concurrent-registration test that
asserts exactly one admin.

Closes #79

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge branch 'main' into feature/79-bootstrap-race
Some checks failed
Security Audit / Gitleaks (Secrets) (pull_request) Successful in 10s
Security Audit / PNPM Audit (JS SCA) (pull_request) Failing after 23s
Security Audit / Trivy (SCA & IaC) (pull_request) Failing after 25s
Security Audit / Semgrep (SAST) (pull_request) Failing after 57s
CI / Client — test & lint (pull_request) Successful in 1m12s
CI / Server — test & lint (pull_request) Successful in 4m53s
Security Audit / Cargo Audit (Rust SCA) (pull_request) Failing after 8m16s
2c93bcefc9
# Conflicts:
#	server/src/main.rs
icub3d merged commit b4a126706c into main 2026-07-09 03:03:04 +00:00
icub3d deleted branch feature/79-bootstrap-race 2026-07-09 03:03:04 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
icub3d/decentcom!96
No description provided.