Skip to content

Take UPDATE_LOCK in querier role DELETE and before password resets - #77

Merged
VibeSWE merged 4 commits into
mainfrom
fix/querier-role-delete-and-gen-password
Oct 25, 2026
Merged

VibeSWE merged 4 commits into
mainfrom
fix/querier-role-delete-and-gen-password

Conversation

@VibeSWE

@VibeSWE VibeSWE commented Oct 25, 2026 •

Copy link
Copy Markdown

Summary

Two follow-ups from the #67 review, both pre-existing, both fixed test-first.

  1. Medium: Query-node role DELETE never took UPDATE_LOCK. Standalone role::delete does. Without it, a concurrent post_user (read metadata → modify → write, under the lock) and a role delete could each overwrite the other's metadata write. querier_role::delete now holds the lock through persistence and the in-memory update, and releases it before the cluster sync, like querier_role::put.
  2. Low: post_gen_password read metadata before taking UPDATE_LOCK. That has the same lost-update risk.
    • Query node: the handler also skipped standalone's protected-user check. It now rejects protected users with ProtectedUser (400) and locks before reading metadata.
    • Standalone: had the same read-before-lock order, and now locks first too.
  3. Medium (found in review): Query-node role DELETE didn't reject the default OIDC role. Standalone returns 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 PARSEABLE from the test arguments.

Verification

  • cargo fmt --check ✅
  • cargo clippy -- -D warnings ✅
  • cargo test --lib: 1025 passed, 0 failed, 2 ignored

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

VibeSWE and others added 3 commits October 24, 2026 22:26
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 VibeSWE left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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::delete calls before drop(guard) takes UPDATE_LOCK again, 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_LOCK from before the read through publication: users, roles, default role, API keys, OIDC, and group cleanup.
  • The bounded-timeout tests can't pass falsely: timeout polls first, and TEST_SERIAL is always taken before UPDATE_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

@VibeSWE
VibeSWE merged commit c6576a6 into main Oct 25, 2026
9 checks passed
@VibeSWE
VibeSWE deleted the fix/querier-role-delete-and-gen-password branch October 25, 2026 04:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant