Repository navigation
fix(auth): keep API key permission constraints on create - #957
Conversation
|
@coderabbitai review |
|
Warning Review limit reached
This review includes 4 billable files and costs up to $1.00.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Or wait 45 minutes for your next included review. View limit details
Comment |
|
Independent review verdict: CLEAN at cc492d3Reviewer: fresh Claude agent (Sonnet, high effort), not the author, cold brief. The PR body and the earlier author-side review were treated as claims. Coordinator: claude-cc-reviewer. Blocking findings: none. Non-blocking notes:
What was attacked and how (local macOS; unit and mock tests through the server adapter and auth service to the store mock, no DB, cloud or deploy):
Gate: labels are mirrored from #950. CodeRabbit is optional because exact-revision local verification plus this review cover the SHA. The operator note in the body says keys created before deploy keep their broad scope, and the follow-up is #956. Merge waits on CI being green at this SHA. |
|
Automated review of
Posted by a read-only review sweep. It stays silent on this commit and re-reviews only after a new push. |
CreateAPIKeyAPI re-encoded the handler's api.CreateAPIKeyRequest through JSON into auth.APICreateAPIKeyRequest, whose permissions were the storage type auth.Permission. The constraint JSON tags differ (api: accounts, max_amount; auth: account_ids, max_purchase_amount), so account scoping and the purchase amount cap were silently dropped and the key was created broader than requested. APICreateAPIKeyRequest.Permissions is now []APIPermission (the wire shape, matching the group requests) and is converted with the existing apiPermissionsToPermissions mapper. Regression tests cover the real api.CreateAPIKeyRequest path through the server adapter and a reflection completeness check (reusing requireFieldsCovered) for the request fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cc492d3 to
d177d1d
Compare
|
Rebased onto main (server-side
|
|
CodeRabbit gave no response within 40 minutes of the review ping, so it is skipped here. The gate is covered by the two-pass independent review at cc492d3 and the local verification in the PR body. The CI failure on this head is the inherited main regression (TestScheduler_ListRecommendations_StaleCacheKicksBackgroundRefresh, from #938), which #973 fixes; it is not caused by this change. [claude-cc-impl] |
|
Rebased onto main 2ef2f5d+ to pick up #960, which fixes the inherited scheduler test failure. |
|
Correction to my previous comment: my push was rejected (the lease check did its job), so the head is NOT 8f8965ef. The branch had already been rebased onto 2ef2f5d as d177d1d. That head's |
LeanerCloud#957 landed the create-side fix (APICreateAPIKeyRequest.Permissions as []APIPermission), so main's CreateAPIKeyAPI is kept as-is. This branch now carries only the response-shape change (APIKeyInfo.Permissions as []APIPermission) and its HTTP round-trip tests; the create test is cut down to the response assertions that LeanerCloud#957's tests don't cover. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Closes #950.
API key permission constraints were silently dropped on create.
CreateAPIKeyAPI(internal/auth/service_apikeys_api.go) JSON re-encodedapi.CreateAPIKeyRequestintoauth.APICreateAPIKeyRequest. That request carried[]auth.Permission, whose constraint tags areaccount_idsandmax_purchase_amount. The api request (and the frontend,frontend/src/api/types.ts:556-564) sendsaccountsandmax_amount. As a result, a key requested as "only account 123, max $500" was stored with no account scoping and no amount cap. Providers, services and regions survived because their tags match.The fix:
APICreateAPIKeyRequest.Permissionsis now[]APIPermission, the wire shape the group requests already use.CreateAPIKeyAPIconverts it with the existingapiPermissionsToPermissionsmapper, the same oneCreateGroupAPIuses.The new test confirms the restored constraints are enforced.
HasAPIKeyPermissionForConstraintsAPIchecks each constraint set against both the key's own permissions, which now keep their constraints, and the owner's permissions. A key's constraints therefore narrow the owner's and never widen them.Tests
internal/server/adapter_test.go(TestAuthServiceAdapter_CreateAPIKeyAPI_KeepsPermissionConstraints) sends a realapi.CreateAPIKeyRequestthrough the server adapter andauth.Serviceto the store mock, with accounts, providers, services, regions and max_amount all set.AccountIDscame out nil andMaxPurchaseAmountcame out 0.TestAPIAdapters_CreateAPIKey_AllFieldsReachStoreapplies the test(auth): fail when an API adapter drops a request field #923requireFieldsCoveredcompleteness check to the key request, so a future field or constraint drop fails CI.accountstag toaccount_ids. Only the server adapter test catches this one, which is the right layer.Verification (local, macOS)
go build ./...andgo vet ./...are clean.go test -race -count=1 ./internal/auth/... ./internal/api/... ./internal/server/...passes.Operator note
This fixes keys created after deploy. Keys created before it keep the broader, unconstrained scope they were stored with, still bounded by their owner's permissions. Consider auditing existing keys whose creators intended constraints, and revoking and recreating them.
Review
An independent non-author reviewed it in two passes and found no blocking findings at cc492d3.
Pre-existing gaps found during review are filed separately as #956:
{admin, *}key permissions ignore their constraints on non-strict checks.validateAPIKeyPermissionsdoes not check constraints at creation.🤖 Generated with Claude Code