Repository navigation
fix(rbac): gate Add Purchases button and createPlannedPurchases on update:purchases - #1426
Merged
Merged
Conversation
…int on update:purchases
Two RBAC gaps allowed Plan Authors (who hold plan verbs only) to either
see action buttons or hit endpoints they should not reach:
1. frontend/src/plans.ts -- "Add Purchases" button was gated on
canManagePlan (update:plans) alone. Plan Authors hold update:plans
but not update:purchases. Added a new canAddPurchases parameter that
requires BOTH update:plans AND update:purchases.
2. internal/api/handler_plans.go -- createPlannedPurchases used
requirePermission("create","plans"), which Plan Authors pass.
Changed to requirePermission("update","purchases") so only roles
with the purchase-write verb (Standard Users, Purchasers) can
schedule purchases.
3. frontend/src/history.ts -- canRetryFailedRow was missing an explicit
retry-own:purchases check before the creator-match branch. A user
holding no retry permission at all could see the Retry button on rows
they created. Added the same guard pattern used by canCancelPendingRow.
New backend test: TestCreatePlannedPurchases_PermissionGate (3 sub-tests
covering pass / 403 / admin bypass). New frontend tests in
plans-permissions.test.ts and history-retry-button.test.ts verify the
button is hidden for Plan Authors and shown for Standard Users.
Closes #1418
Member
Author
|
@coderabbitai review |
Member
Author
|
Merged to main after independent adversarial security review (SHIP; backend enforcement + verb-change-per-role-definitions verified, fail-before/pass-after) and all CI green (closes #1418). Gates action buttons on the correct per-resource permissions: Add-Purchases + createPlannedPurchases on update:purchases (Plan Authors correctly excluded), Retry on explicit retry-own. |
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
update:plansalone, allowing Plan Authors (who hold plan verbs but not purchase verbs) to see and trigger purchase scheduling. Now requires bothupdate:plansANDupdate:purchases(canAddPurchasesparameter added torenderPlanCard).createPlannedPurchasesusedrequirePermission("create","plans"), which Plan Authors pass. Changed torequirePermission("update","purchases")so only purchase-write roles can schedule purchases (real security boundary).canRetryFailedRowwas missing an explicitretry-own:purchasescheck before the creator-match branch. A user with no retry permission at all could see Retry on rows they created. Added the same guard used bycanCancelPendingRowandcanApprovePendingRow.Closes #1418
Gaps fixed
frontend/src/plans.tscanAddPurchases = canManagePlan && canAccess('update','purchases')createPlannedPurchasesacceptedcreate:plansinternal/api/handler_plans.goupdate:purchasesfrontend/src/history.tsif (!canAccess('retry-own','purchases')) return falsebefore creator matchTest plan
TestCreatePlannedPurchases_PermissionGate(3 sub-tests): pass (404 proves gate cleared), 403 for no-permission, admin bypass -- all passPlans page Add Purchases permission gate: Plan Authors see Edit Plan but not Add Purchases (fail-before/pass-after confirmed)Standard User (update:plans + update:purchases) sees both Edit Plan and Add PurchasesHistory retry-own permission gate: user withoutretry-own:purchasessees no Retry on own failed rowuser with retry-own:purchases sees Retry on own failed rowsnpm run build: compiled (exit 0, pre-existing size warning only)go build ./...,go vet ./...,go test ./internal/api/...(1728 tests): exit 0gocyclo -over 10on touched files: exit 0 (no findings)golangci-lint run ./internal/api/...: exit 0, no issues