refactor(api): requireAuth returns resolved Principal (closes #194)#864
refactor(api): requireAuth returns resolved Principal (closes #194)#864cristim wants to merge 6 commits into
Conversation
|
Important Review skippedNo new commits to review since the last review. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughIntroduces a ChangesPrincipal identity model and requireAuth refactor
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Extract principalFromUserAPIKey and principalFromBearerToken helpers from authenticatePrincipal to bring its cyclomatic complexity from 11 down to 4, passing the gocyclo <=10 gate.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
Rate Limit Exceeded
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
Adversarial review summaryUNSTABLE state is from Root cause is base divergence, not a fixable-in-place defect. PR #907 (
There's also a smaller signature drift in base ( Risk-surface walk
Action taken
Once #1010's design is settled the rebase + ship can land as a follow-up commit on this PR. @coderabbitai review |
|
A few notes to complement your summary:
Adding a learning about the group-membership model so future reviews don't flag ✏️ Learnings added
🧠 Learnings used✅ Action performedReview finished.
|
Extract principalFromUserAPIKey and principalFromBearerToken helpers from authenticatePrincipal to bring its cyclomatic complexity from 11 down to 4, passing the gocyclo <=10 gate.
Session no longer carries Role (removed upstream when the auth model moved to group-based permissions via HasPermissionAPI). Remove the stale session.Role reference from principalFromBearerToken and drop the corresponding test assertion that expected a role value the session mock never set. Closes the go vet compile error blocking pre-commit on PR #864.
authenticatePrincipal resolves the caller's identity once across all three credential kinds (admin API key, user API key, bearer session) and returns a *Principal. requireAuth now delegates to it instead of calling authenticate() which threw the resolved identity away. Router updated to _, err := r.h.requireAuth(...). Nil-auth guard moved before the API-key path so a missing auth service fails closed (401) rather than falling through. Dead userIface/userFields locals removed. TestRequireAuth_* tests extended to assert the returned Principal (Kind, Role, UserID, Session pointer) for each credential type.
Extract principalFromUserAPIKey and principalFromBearerToken helpers from authenticatePrincipal to bring its cyclomatic complexity from 11 down to 4, passing the gocyclo <=10 gate.
…I-key and nil-auth paths (refs #194) When ValidateUserAPIKeyAPI returns a userRaw value whose concrete type does not satisfy the GetID/GetEmail/GetRole interface, principalFromUserAPIKey previously returned a partially-populated Principal (Role:"user", empty UserID/Email). Any handler trusting the Principal would have granted access to an unresolved identity. Fix: return nil (deny) when the assertion fails. Adds three tests: - TestRequireAuth_UserAPIKey: valid user API key yields PrincipalUserAPIKey with populated UserID/Email/Role (zero coverage on the #178 hot-spot). - TestRequireAuth_UserAPIKey_BadRecord: unexpected userRaw type yields 401; fails pre-fix (Principal returned), passes post-fix (nil returned). - TestRequireAuth_NilAuth_Rejects: Handler with auth==nil returns 401 for a non-admin key, confirming the fail-closed nil-auth branch.
Session no longer carries Role (removed upstream when the auth model moved to group-based permissions via HasPermissionAPI). Remove the stale session.Role reference from principalFromBearerToken and drop the corresponding test assertion that expected a role value the session mock never set. Closes the go vet compile error blocking pre-commit on PR #864.
The duck-typed interface assertion (GetID/GetEmail/GetRole) always fails at runtime because *auth.User exposes only struct fields, not methods. Every user-API-key request was silently 401ing. Fix: assert userRaw to *auth.User directly (middleware.go already imports auth). Update TestRequireAuth_UserAPIKey to use the real *auth.User concrete type instead of the testUserRecord stand-in, so the test now exercises the same type assertion as production code. Drop the stale Role assertion (role system removed in #907) and the testUserRecord helper.
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
internal/api/router_authuser_test.go (1)
205-219: ⚡ Quick winAdd nil-auth coverage for bearer-token credentials too.
Current nil-auth coverage only exercises
X-API-Key(Lines 212-213). Add a bearer-token request case so both non-admin credential paths are pinned to fail closed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/api/router_authuser_test.go` around lines 205 - 219, The TestRequireAuth_NilAuth_Rejects test currently only exercises the X-API-Key credential path when auth is nil. Add an additional test case or extend the existing test to also verify that bearer-token credentials fail with a 401 ClientError when auth is nil. This ensures both non-admin credential paths (X-API-Key and bearer token) are pinned to fail closed in the requireAuth method.internal/api/router.go (1)
376-376: 🏗️ Heavy liftPropagate the resolved
Principalinstead of dropping it.Line 376 ignores the returned principal, so handlers still can’t consume caller identity directly and may continue re-resolving auth context. Consider threading
*Principalvia handler params or request context.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/api/router.go` at line 376, The `requireAuth` call in the router is discarding the returned `Principal` value using the blank identifier, which forces handlers to repeatedly resolve authentication context instead of consuming the caller identity directly. Capture the returned `Principal` from the `requireAuth` method instead of dropping it, and thread it through the handler parameters or store it in the request context so that downstream handlers can access the resolved identity without needing to re-resolve the auth context.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@internal/api/middleware.go`:
- Around line 65-70: The Principal struct contains a deprecated Role field that
encodes outdated role-based authorization semantics, which conflicts with the
new group-based auth model. Remove the Role field definition from the Principal
struct (currently at line 69 with the comment about "admin" for
PrincipalAdminAPIKey) and remove the code that sets Role to "admin" when
creating Principal instances (at line 96). This will eliminate stale
authorization signals and allow proper enforcement of group-based authorization
invariants.
---
Nitpick comments:
In `@internal/api/router_authuser_test.go`:
- Around line 205-219: The TestRequireAuth_NilAuth_Rejects test currently only
exercises the X-API-Key credential path when auth is nil. Add an additional test
case or extend the existing test to also verify that bearer-token credentials
fail with a 401 ClientError when auth is nil. This ensures both non-admin
credential paths (X-API-Key and bearer token) are pinned to fail closed in the
requireAuth method.
In `@internal/api/router.go`:
- Line 376: The `requireAuth` call in the router is discarding the returned
`Principal` value using the blank identifier, which forces handlers to
repeatedly resolve authentication context instead of consuming the caller
identity directly. Capture the returned `Principal` from the `requireAuth`
method instead of dropping it, and thread it through the handler parameters or
store it in the request context so that downstream handlers can access the
resolved identity without needing to re-resolve the auth context.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 02a9daaf-cf11-47ac-aa19-b48d324df676
📒 Files selected for processing (3)
internal/api/middleware.gointernal/api/router.gointernal/api/router_authuser_test.go
Principal.Role encoded deprecated role-based authorization semantics that the group-based model (issue #907) made obsolete. The only non-test reader of requireAuth's returned Principal is Router.Route, which discards it and checks only the error; admin is enforced via the stateless admin-key check and HasPermissionAPI(ActionAdmin), never via Principal.Role. Remove the field, its lone "admin" assignment on the admin-key path, and the stale test assertion.
|
@coderabbitai review |
✅ Action performedReview finished.
|
Summary
requireAuthnow returns(*Principal, error)instead of bareerrorby delegating to the newauthenticatePrincipalhelper (already added in this branch)authenticatePrincipalcleaned up: nil-auth guard moved before the API-key path (fail-closed perfeedback_fail_closed_middleware.md), deaduserIface/userFieldslocals removed_, err := r.h.requireAuth(...)to handle the new return shapeTestRequireAuth_*tests upgraded to assert the returnedPrincipal(kind, role, UserID, Session pointer) for each credential typeTest plan
go build ./...cleango test github.com/LeanerCloud/CUDly/internal/api/... github.com/LeanerCloud/CUDly/internal/auth/...— 1852 passedTestRequireAuth_AdminAPIKeynow assertsPrincipalAdminAPIKey/role=adminTestRequireAuth_UserSessionnow assertsPrincipalSession,UserID,Role,SessionpointerTestRequireAuth_NoCredential_Rejectsstill asserts 401 ClientErrorCloses #194
Summary by CodeRabbit