Add NewChainPermissionProvider: tries each rbac.PermissionProvider in order, returns the first non-zero mask, propagates errors immediately. Primary use case: ClaimsPermissionProvider (JWT fast-path, no DB call) chained with CachedPermissionProvider (DB fallback). Bump rbac dependency to v1.0.0. API committed as stable.
4.7 KiB
4.7 KiB
Changelog
All notable changes to this module will be documented in this file.
The format is based on Keep a Changelog, and this module adheres to Semantic Versioning.
1.0.0 — 2026-05-08
Added
NewChainPermissionProvider(providers ...rbac.PermissionProvider) rbac.PermissionProvider— tries each provider in order and returns the first non-zero mask; propagates errors immediately without consulting subsequent providers; primary use case is a JWT claims fast-path (ClaimsPermissionProvider) chained with a DB-backed fallback (CachedPermissionProvider)
Changed
- Dependency
code.nochebuena.dev/go/rbacbumped from v0.9.0 to v1.0.0
Unchanged
SetTokenData, EnrichmentMiddleware, AuthzMiddleware, IdentityEnricher,
WithTenantHeader, Cache, NewClaimsPermissionProvider, and
NewCachedPermissionProvider are API-compatible with v0.1.0.
0.1.0 - 2026-05-08
Added
SetTokenData(ctx, uid, claims) context.Context— injects verified uid and raw claims into the request context; called by provider-specific AuthMiddleware implementations (e.g.httpauth-firebase,httpauth-jwt) after token verification; downstream middleware reads these values via unexported helpers in the same packageIdentityEnricherinterface — application-implemented; receivesuid stringandclaims map[string]any, returnsrbac.Identity; called byEnrichmentMiddlewareon every authenticated requestEnrichOptfunctional option type for configuringEnrichmentMiddlewareWithTenantHeader(header string) EnrichOpt— reads a tenant ID from the named request header and attaches it to the identity viarbac.Identity.WithTenant; absent header leavesTenantIDas an empty string with no errorEnrichmentMiddleware(enricher IdentityEnricher, opts ...EnrichOpt) func(http.Handler) http.Handler— reads uid and claims stored by any upstream AuthMiddleware viaSetTokenData, callsenricher.Enrich, and stores the resultingrbac.Identityin context viarbac.SetInContext; returns 401 if no uid is present; returns 500 if the enricher failsAuthzMiddleware(provider rbac.PermissionProvider, resource string, required rbac.Permission) func(http.Handler) http.Handler— readsrbac.Identityfrom context viarbac.FromContext, resolves the permission mask via the providedrbac.PermissionProvider, and gates the request against the required permission bit; returns 401 if no identity is in context; returns 403 if the permission check fails or the provider returns an error; usesrbac.PermissionProviderdirectly without redefining itCacheinterface — abstracts the caching backend for permission masks;Get(ctx, key) (int64, bool, error)andSet(ctx, key, value, ttl) error; implementations are typically backed by Valkey or RedisNewCachedPermissionProvider(inner rbac.PermissionProvider, cache Cache, ttl time.Duration) rbac.PermissionProvider— wraps anyrbac.PermissionProviderwith a TTL-based cache layer; cache key format isrbac:{uid}:{resource}; on cache miss falls through to inner and populates the cache; on cache error falls through silently — never fails due to cache unavailabilityNewClaimsPermissionProvider(claimsKey string) rbac.PermissionProvider— reads pre-computed permission masks from JWT claims stored in the request context bySetTokenData; expectsclaims[claimsKey]to be amap[string]anywhere each key is a resource name and the value is the bitmask asint64orfloat64(JSON decodes numbers as float64); returns 0 without error if the claim is absent
Design Notes
AuthzMiddlewareusesrbac.PermissionProviderdirectly rather than redefining a local interface;rbacis the single source of truth for this contractEnrichmentMiddlewareandAuthzMiddlewareare provider-agnostic — they depend only onSetTokenDatahaving been called upstream; anyAuthMiddlewarethat callsSetTokenData(Firebase, JWT, API key, etc.) is compatible without changes to the enrichment or authorization layer- Two
rbac.PermissionProviderimplementations ship with this module for the two common architectures:ClaimsPermissionProviderfor simple applications that embed permissions in the JWT (no per-request DB or network call), andCachedPermissionProviderfor applications where the permission set is too large to embed or needs to be independently revocable CachedPermissionProvideruses TTL-based expiry exclusively; explicit invalidation is left to callers who can interact with theCachedirectly using therbac:{uid}:{resource}key format