refactor(authz): readability + maintainability sweep#62
Merged
Conversation
Extends the mockery config introduced in PR #61 to cover the second root-package port consumed by an adapter under refactor (authz/role). The pattern is now: one entry per root interface that consumer-package tests need to mock. Adapter-local ports (e.g. casbin.Enforcer) stay hand-rolled; mockery covers root ports only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s, and mock the resolver
- Add compile-time assertion that *Authorizer satisfies authkit.Authorizer.
- Tighten the Authorizer godoc into a contract statement ("decides authorization
checks by matching check.Action against the principal's effective actions").
- Document the deniedReason constant.
- Annotate the post-resolver ctx.Err() check explaining why we re-check after
the external call (cancellation surfaces as cancellation, not stale decision).
- Replace the hand-rolled fakeActionResolver with the generated
authkitmocks.PrincipalActionResolver. Each table case now configures its mock
expectations explicitly; the previous test's tt.resolver.requests assertion is
subsumed by mockery's AssertExpectations on Cleanup.
The TestAuthorizerAllowsHTTPMiddlewareThroughAccessJWT integration test still
uses memory.Store as the resolver — that's the right shape for an end-to-end
test (matching access pass's TestVerifiedTokenUsesStorageBackedAuthorization).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…document private symbols - Add compile-time assertion that *Authorizer satisfies authkit.Authorizer. - Change deniedReason from "casbin policy denied" to "policy denied". The adapter's implementation name does not belong in the API response; consumers asserting on Decision.Reason should not learn the backend through a string literal. (No code in the repo grepped for the old literal.) - Document deniedReason, resourceObject, options, defaultOptions; expand the WithRequestBuilder godoc to call out the nil-leaves-default behaviour. - Tighten Can's godoc into a contract statement. - Annotate fail-closed input validation in DefaultRequestBuilder; comment why the post-projection ctx.Err() re-check exists; explain the type-only resource form in resourceObject. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the per-type split applied to access/jwt in PR #61. The monolithic 433-line file mixed three concerns: - authorizer_test.go: NewAuthorizer validation + Can() behaviour (allow, deny, custom builder, projection/enforcer/context errors, real Casbin integration). - request_builder_test.go: DefaultRequestBuilder projection + validation. - helpers_test.go: test fixtures (testPrincipal, testCheck, testResource, testEnforcer + allow/deny helpers, newAuthorizer, newCasbinEnforcer). Mechanical lift: every test function moves verbatim with its imports. No tests added, removed, or reorganised internally. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 27, 2026
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
Second slice of the multi-session refactor sweep. PR #61 brought
access/up to the bar; this PR applies the same 10 criteria toauthz/role/andauthz/casbin/. Same non-goals: bugs, correctness, performance.Highlights
authz/role/var _ authkit.Authorizer = (*Authorizer)(nil).Authorizergodoc into a contract statement; documented thedeniedReasonconstant.ctx.Err()re-check explaining the cancellation-mid-resolution rationale.fakeActionResolverwith the generatedauthkitmocks.PrincipalActionResolver. Each table case now configures its mock expectations explicitly. TheTestAuthorizerAllowsHTTPMiddlewareThroughAccessJWTend-to-end test still usesmemory.Store— the right shape for an integration test (matches access pass'sTestVerifiedTokenUsesStorageBackedAuthorization).authz/casbin/"casbin policy denied"→"policy denied". The adapter's implementation name does not belong in the API response; grep confirms no consumer asserts on the literal.deniedReason,resourceObject,options,defaultOptions); expandedWithRequestBuildergodoc to call out the nil-leaves-default behaviour.Cangodoc.DefaultRequestBuilder, the post-projectionctx.Err()re-check, and the type-only-resource form inresourceObject.authorizer_test.go→authorizer_test.go(Can + NewAuthorizer cases),request_builder_test.go(DefaultRequestBuilder cases),helpers_test.go(test fixtures + real Casbin enforcer helper). Mechanical lift; every test function preserved verbatim.Mockery
authkit.PrincipalActionResolverto.mockery.yaml. The pattern is now: one entry per root-package port that consumer tests need to mock. Adapter-local ports (e.g.casbin.Enforcer) stay hand-rolled.mocks/authkit/principal_action_resolver.go.Deliberately NOT changed
casbin.Enforcerandcasbin.RequestBuilderalready serve as extension points; promoting them to aports.gofile would mirror nothing else in the codebase.casbin.Enforcer. Adapter-local port for a third-party library; the hand-rolledtestEnforceris small and clear.casbin/options.gointoauthorizer.go. Five other packages in the repo use a dedicatedoptions.go(proof/oidc/,proof/apikey/,proof/passkey/session/,http/auth/, this one). Consistency wins.TestDefaultRequestBuilderValidatesRequiredInputsvsTestAuthorizerCanReturnsDefaultProjectionErrors. They cover the same input rules at different layers; the second test also asserts the enforcer is never called — defense in depth for a security-critical path.role.deniedReason("action not granted"). Already neutral and accurate.Test plan
moon run root:check --summary minimal— format, lint, build, unit, Testcontainers integration. All green.go test -v ./authz/...— all sub-tests pass (role + casbin), same case count as pre-split for casbin.go tool mockeryis idempotent; second run produces no diff.role.Authorizer.Canandcasbin.Authorizer.Can, plus one observable string change (Decision.Reasonfrom"casbin policy denied"→"policy denied").🤖 Generated with Claude Code