fix(authz): symmetric SpiceDB cleanup on policy delete#1624
fix(authz): symmetric SpiceDB cleanup on policy delete#1624whoAbhishekSah wants to merge 1 commit into
Conversation
policy.AssignRole writes three tuples per policy but policy.Delete and DeleteWithMinRoleGuard only swept the two where the rolebinding is the Object (role_bearer, role). The resource-grant tuple — where the rolebinding is the Subject — leaked on every per-member removal, ownership swap, or cross-resource principal cleanup. Add a second relation.Delete keyed by Subject on both delete paths via a shared helper. The Subject-side delete tolerates relation.ErrNotExist so resource-level wildcard sweeps in group.DeleteModel / project.DeleteModel (which currently paper over the bug at full-resource teardown) don't cause failures during the overlap period; those sweeps become redundant for this tuple and can be removed in a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe PR refactors role-binding cleanup in the policy service by extracting dual-tuple deletion logic into a helper function. ChangesRole-Binding Cleanup Refactoring
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/policy/service_test.go (1)
33-51:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTest names claim call ordering that isn’t asserted.
Line 33 and Line 133 describe strict sequencing, but these cases currently only verify invocation, not order. Please enforce order explicitly so these tests fail on regressions in delete sequencing.
Also applies to: 133-152
🧹 Nitpick comments (1)
core/policy/service_test.go (1)
155-188: ⚡ Quick winAdd the guarded-path non-
ErrNotExistsubject-delete failure case.
TestService_DeleteWithMinRoleGuardshould also cover subject-side delete returning a non-ErrNotExisterror, to lock in the helper’s error semantics on this path.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2049107e-41d1-40af-8ebc-20092ea7982c
📒 Files selected for processing (2)
core/policy/service.gocore/policy/service_test.go
Coverage Report for CI Build 26089607399Coverage increased (+0.04%) to 42.379%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Problem
policy.AssignRolewrites three SpiceDB tuples per policy:policy.Delete(andDeleteWithMinRoleGuard) only swept by Object on the rolebinding, catching T1+T2. T3 has the rolebinding as Subject, so it leaked on every per-member removal, ownership swap, and cross-resource principal cleanup.Full-resource teardown (
group.DeleteModel,project.DeleteModel) papered over it with a wildcard sweep on the resource — but only for T3 tied to the deleted resource. Per-member removals leaked forever.Fix
Both delete paths now route through a
deleteRoleBindingRelationshelper that issues tworelationService.Deletecalls: one by Object (T1, T2), one by Subject (T3). The Subject-side delete toleratesrelation.ErrNotExistso resource-level sweeps that already removed T3 don't fail the policy delete.Test plan
core/policyunit tests cover happy path,ErrNotExisttolerance, non-ErrNotExistfailure semantics for bothDeleteandDeleteWithMinRoleGuard.organization,group,project,membership,userpat,deleter,v1beta1connectpolicy handler.Follow-ups
group.DeleteModelandproject.DeleteModel.grant_relationfromprincipal_typeinsidepolicy.Service.Createso the A2 invariant is structural.Closes #1617.
🤖 Generated with Claude Code