Documentation
¶
Overview ¶
Package authz decides what an authenticated user may do: gates (named abilities) and policies (abilities on a model type). Decisions fail closed: an ability or policy that was never registered denies.
gate := authz.New(func(ctx context.Context) (*models.User, error) {
u, err := auth.UserFrom[models.User](ctx, "web") // the session guard stores a value
return &u, err
})
gate.Define("edit-settings", func(ctx context.Context, u *models.User) bool { return u.Role == "admin" })
gate.Register(authz.For[*models.Post](policies.PostPolicy{}))
r.Services().Authorizer = gate
if err := c.Authorize("update", post); err != nil { return err } // 403 / 404
See https://github.com/AlexGalhardo/vukem/blob/main/docs/authentication.md.
Example ¶
package main
import (
"context"
"fmt"
"github.com/AlexGalhardo/vukem/authz"
)
type exUser struct {
ID int
Admin bool
}
type exPost struct{ AuthorID int }
type exPostPolicy struct{}
func (exPostPolicy) Update(_ context.Context, u *exUser, p *exPost) bool {
return u.ID == p.AuthorID
}
func main() {
// In an app the resolver reads auth.UserFrom[models.User](ctx, "web")
// (the session guard stores the user by value) and returns &u.
gate := authz.New(func(context.Context) (*exUser, error) { return &exUser{ID: 1}, nil })
gate.Define("edit-settings", func(_ context.Context, u *exUser) bool { return u.Admin })
if err := gate.Register(authz.For[*exPost](exPostPolicy{})); err != nil {
fmt.Println(err)
return
}
ctx := context.Background()
alice := &exUser{ID: 1}
fmt.Println(gate.Allows(ctx, alice, "update", &exPost{AuthorID: 1}))
fmt.Println(gate.Allows(ctx, alice, "update", &exPost{AuthorID: 2}))
fmt.Println(gate.Allows(ctx, alice, "edit-settings", nil))
fmt.Println(gate.Allows(ctx, alice, "never-defined", nil))
}
Output: true false false false
Index ¶
- Variables
- type Gate
- func (g *Gate[U]) Allows(ctx context.Context, u U, ability string, subject any) bool
- func (g *Gate[U]) Authorize(ctx context.Context, ability string, subject any) error
- func (g *Gate[U]) Before(fn func(ctx context.Context, u U, ability string) *Result)
- func (g *Gate[U]) Check(ctx context.Context, u U, ability string, subject any) (Result, error)
- func (g *Gate[U]) Define(ability string, fn func(ctx context.Context, u U) bool)
- func (g *Gate[U]) DefineResult(ability string, fn func(ctx context.Context, u U) Result)
- func (g *Gate[U]) Register(policies ...Policy) error
- type Policy
- type Result
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrForbidden = &errs.HTTPError{Status: http.StatusForbidden, Message: "This action is unauthorized."}
ErrForbidden is the default denial (403).
var ErrNoUser = &errs.HTTPError{Status: http.StatusUnauthorized, Message: "Unauthenticated."}
ErrNoUser is returned (401) when the user resolver finds nobody.
Functions ¶
This section is empty.
Types ¶
type Gate ¶
type Gate[U any] struct { // contains filtered or unexported fields }
Gate holds abilities and policies for users of type U.
func New ¶
New returns a gate; user resolves the current user from the request context (e.g. auth.UserFrom).
func (*Gate[U]) Authorize ¶
Authorize implements web.Authorizer: it resolves the current user from ctx and returns nil, ErrNoUser (401), a 403 or a 404. Unregistered abilities and policies deny (fail closed) and are reported in the returned error's cause for logs.
func (*Gate[U]) DefineResult ¶
DefineResult registers a named ability returning a Result (for 404s or custom messages).
type Policy ¶
type Policy struct {
// contains filtered or unexported fields
}
Policy is a policy bound to a subject type, created by For.
func For ¶
For binds policy to subject type T (usually a model pointer): ability "update" calls policy.Update(ctx, user, subject T) returning Result or bool. Methods without the subject parameter (Create(ctx, user)) serve type-level abilities: c.Authorize("create", (*models.Post)(nil)). A policy bound to *T also serves T values (a template ranging over []models.Post): the method receives a pointer to a copy.
type Result ¶
type Result struct {
// contains filtered or unexported fields
}
Result is an authorization decision.
func DenyAsNotFound ¶
func DenyAsNotFound() Result
DenyAsNotFound refuses with 404, hiding that the resource exists.