Repository navigation
Take UPDATE_LOCK in querier role DELETE and before password resets - #77
Merged
Merged
Conversation
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Hold UPDATE_LOCK through role deletion persistence and in-memory publication, releasing it before cluster synchronization. Reject protected password reset targets and acquire UPDATE_LOCK before reading metadata. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
The handler read metadata before locking, so a concurrent RBAC write could be overwritten by the password reset's stale copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Standalone role::delete returns DefaultRoleInUse (400) when the role is the default OIDC role; the querier handler deleted it, leaving a dangling default. The check reads stored metadata, so tests cannot reach it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VibeSWE
commented
Oct 25, 2026
VibeSWE
left a comment
Author
There was a problem hiding this comment.
Adversarial review of PR #77 (at 54adf114)
One Opus 5.5 and one Sol 6.1 reviewer. Neither found anything blocking in this PR's changes. Both checked:
- Nothing
querier_role::deletecalls beforedrop(guard)takesUPDATE_LOCKagain, so there is no re-entrant lock. - No parking_lot guard is held across an
.await, and every early return happens before any mutation. - The guard covers the whole read-modify-write and the in-memory publication, matching standalone
role::delete. - Every RBAC metadata writer now holds
UPDATE_LOCKfrom before the read through publication: users, roles, default role, API keys, OIDC, and group cleanup. - The bounded-timeout tests can't pass falsely:
timeoutpolls first, andTEST_SERIALis always taken beforeUPDATE_LOCK.
| Severity | Finding | Outcome |
|---|---|---|
| Medium | Querier role::delete skipped standalone's DefaultRoleInUse check, so deleting the default OIDC role succeeded and left a dangling default |
Fixed in 77feb033. The check reads stored metadata, which tests can't reach, so it is a direct port with no test |
| Medium (pre-existing, separate) | User PATCH (rbac.rs:216) updates the live user before persisting |
Separate PR in progress |
| Low | The tests prove the handler waits for the lock, not that it keeps holding it through put_metadata |
Accepted; proving that needs a storage seam |
| Nit | Querier post_gen_password holds the lock through the ingestor sync, unlike delete |
Kept: querier post_user and user delete do the same |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two follow-ups from the #67 review, both pre-existing, both fixed test-first.
UPDATE_LOCK. Standalonerole::deletedoes. Without it, a concurrentpost_user(read metadata → modify → write, under the lock) and a role delete could each overwrite the other's metadata write.querier_role::deletenow holds the lock through persistence and the in-memory update, and releases it before the cluster sync, likequerier_role::put.post_gen_passwordread metadata before takingUPDATE_LOCK. That has the same lost-update risk.ProtectedUser(400) and locks before reading metadata.DefaultRoleInUse(400); the querier deleted the role and left a dangling default. Ported as-is. The check reads stored metadata, which tests can't reach.Tests
delete_waits_for_update_lock_before_checking_role: with the lock held, the delete doesn't return; after release it completes.post_gen_password_rejects_protected_user_before_storage: the password hash is unchanged.post_gen_password_waits_for_update_lock_before_storage: covers both the querier and standalone handlers.Without the fixes, the lock-wait tests fail by assertion or by the test binary exiting with code 2. The exit happens when a handler reads metadata before the lock: that initialises
PARSEABLEfrom the test arguments.Verification
cargo fmt --check✅cargo clippy -- -D warnings✅cargo test --lib: 1025 passed, 0 failed, 2 ignored🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.