feat(rbac): initial stable release v0.9.0
Foundational identity and permission types for role-based access control — bit-set PermissionMask, immutable Identity value type, and PermissionProvider interface. What's included: - `Identity` value type with NewIdentity / WithTenant constructors and SetInContext / FromContext context helpers - `Permission` (int64 bit position) and `PermissionMask` (int64 bit-set) with O(1) Has and non-mutating Grant - `PermissionProvider` interface for DB-backed ResolveMask(ctx, uid, resource) resolution Tested-via: todo-api POC integration Reviewed-against: docs/adr/
This commit is contained in:
55
identity.go
Normal file
55
identity.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package rbac
|
||||
|
||||
import "context"
|
||||
|
||||
// Identity represents the authenticated principal for a request.
|
||||
//
|
||||
// It is a value type — always copied, never a pointer — to prevent nil-check
|
||||
// burden and avoid accidental mutation of a shared context value.
|
||||
//
|
||||
// Construction follows a two-step pattern:
|
||||
// 1. [NewIdentity] populates authentication data from the token (uid, name, email).
|
||||
// 2. [Identity.WithTenant] optionally enriches with a tenant ID in a later
|
||||
// middleware step, returning a new value without mutating the original.
|
||||
type Identity struct {
|
||||
UID string
|
||||
TenantID string
|
||||
DisplayName string
|
||||
Email string
|
||||
}
|
||||
|
||||
// authContextKey is an unexported type used as the context key for Identity.
|
||||
// Using a private type prevents collisions with keys from other packages.
|
||||
type authContextKey struct{}
|
||||
|
||||
var authKey = authContextKey{}
|
||||
|
||||
// NewIdentity creates an Identity from token authentication data.
|
||||
// TenantID is intentionally left empty — populate it later with [Identity.WithTenant]
|
||||
// once the enrichment middleware has resolved it.
|
||||
func NewIdentity(uid, displayName, email string) Identity {
|
||||
return Identity{
|
||||
UID: uid,
|
||||
DisplayName: displayName,
|
||||
Email: email,
|
||||
}
|
||||
}
|
||||
|
||||
// WithTenant returns a copy of the Identity with TenantID set to id.
|
||||
// The receiver is not mutated — safe to call from concurrent middleware.
|
||||
func (i Identity) WithTenant(id string) Identity {
|
||||
i.TenantID = id
|
||||
return i
|
||||
}
|
||||
|
||||
// SetInContext stores the Identity in ctx and returns the enriched context.
|
||||
func SetInContext(ctx context.Context, id Identity) context.Context {
|
||||
return context.WithValue(ctx, authKey, id)
|
||||
}
|
||||
|
||||
// FromContext retrieves the Identity stored by [SetInContext].
|
||||
// Returns the zero-value Identity and false if no identity is present.
|
||||
func FromContext(ctx context.Context) (Identity, bool) {
|
||||
id, ok := ctx.Value(authKey).(Identity)
|
||||
return id, ok
|
||||
}
|
||||
Reference in New Issue
Block a user