Skip to content

fix(auth): close last-admin TOCTOU (count + mutate admins in one transaction) #919

Description

@cristim

Summary

The last-admin protection has a time-of-check/time-of-use race. checkLastAdminConstraint (used by UpdateUser) and DeleteUser both run CountGroupMembers in the service layer, then perform the UpdateUser/DeleteUser write in a separate statement with no surrounding transaction or row lock. There is no DB-level "≥1 Administrators member" constraint (only the per-user cardinality(group_ids) >= 1 CHECK).

Two concurrent privileged requests, each removing a different one of the last two admins, can both observe count == 2, both pass the guard, and leave the system with zero admins.

Severity is LOW (narrow window, requires two simultaneous privileged actors), but the outcome (no admins) is unrecoverable without DB surgery.

Surfaced by the post-merge security review of #912 (2026-06-02), finding LOW-1.

Fix options

  • Perform the count + mutation inside a single transaction with SELECT ... FOR UPDATE on the Administrators-group members, OR
  • Add a deferred DB constraint / trigger enforcing at least one Administrators-group member.

Add a regression test exercising concurrent removal of the last two admins.

Location

internal/auth/service_user.go (checkLastAdminConstraint, DeleteUser).

No activity

Activity on this issue will appear here.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/sHoursimpact/all-usersAffects every userpr-createdA PR has been opened for this issue (dedup guard for the auto-PR loop)pr-mergedThe PR for this issue has been mergedpriority/p3Polish / idea / may never shipseverity/lowMinor harmtriagedItem has been triagedtype/bugDefecturgency/eventuallyNo deadline

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions