Documentation
¶
Overview ¶
Package pundit is a pure-Go (no cgo) model of the authorization ENGINE of Ruby's Pundit gem, faithful to the observable behaviour of Pundit 2.x on MRI 4.0.5. It owns the parts of Pundit that are pure logic — resolving a record to its policy and scope class names (PolicyFinder), the authorize / policy / policy_scope protocol, and the Pundit::Error hierarchy — while the Ruby-specific work of actually invoking a policy's predicate method or running a scope is delegated through injectable seams. The host (rbgo) plugs its Ruby method dispatch into those seams, so `include Pundit::Authorization` can behave exactly like MRI without this package embedding a Ruby runtime.
Index ¶
- func VerifyAuthorized(performed bool) error
- func VerifyPolicyScoped(performed bool) error
- type AuthorizationNotPerformedError
- type Defined
- type Dispatch
- type Engine
- func (e *Engine) Authorize(user any, record Subject, query string) (Subject, error)
- func (e *Engine) Policy(record Subject) (string, bool)
- func (e *Engine) PolicyBang(record Subject) (string, error)
- func (e *Engine) PolicyScope(user any, scope Subject) (any, error)
- func (e *Engine) PolicyScopeBang(user any, scope Subject) (any, error)
- func (e *Engine) Scope(record Subject) (string, bool)
- func (e *Engine) ScopeBang(record Subject) (string, error)
- type NotAuthorizedError
- type NotDefinedError
- type PolicyFinder
- type PolicyScopingNotPerformedError
- type ResolveScope
- type Subject
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func VerifyAuthorized ¶
VerifyAuthorized returns a *AuthorizationNotPerformedError when performed is false, modeling Pundit's verify_authorized controller hook, which raises unless authorize (or skip_authorization) ran during the action.
func VerifyPolicyScoped ¶
VerifyPolicyScoped returns a *PolicyScopingNotPerformedError when performed is false, modeling Pundit's verify_policy_scoped controller hook, which raises unless policy_scope (or skip_policy_scope) ran during the action.
Types ¶
type AuthorizationNotPerformedError ¶
type AuthorizationNotPerformedError struct{ Msg string }
AuthorizationNotPerformedError mirrors Pundit::AuthorizationNotPerformedError. VerifyAuthorized returns it when an authorization check was never performed, exactly as Pundit's controller hook raises it when neither authorize nor skip_authorization ran.
func (*AuthorizationNotPerformedError) Error ¶
func (e *AuthorizationNotPerformedError) Error() string
type Defined ¶
Defined reports whether a policy or scope class of the given name is defined. It models Ruby's String#safe_constantize: the host answers from its constant table so the engine can distinguish "no such policy" (Pundit#policy returning nil, Pundit#policy! raising NotDefinedError) from a genuine authorization result. When an Engine's Defined seam is nil every derived name is treated as defined, which suits hosts that have already validated their policies.
type Dispatch ¶
Dispatch invokes a policy query method (e.g. "update?") on the policy class named policyClass, for the given user and record, and reports the boolean the method returned. It is the seam through which the engine performs the `policy.public_send(query)` step of Pundit#authorize: the host (rbgo) instantiates the Ruby policy with (user, record) and sends it the query. The record passed is the pundit_model Subject (see Engine.Authorize).
type Engine ¶
type Engine struct {
// Dispatch performs the policy predicate call. Required by Authorize.
Dispatch Dispatch
// ResolveScope runs a policy scope. Required by PolicyScope / PolicyScopeBang.
ResolveScope ResolveScope
// Defined models safe_constantize; when nil, all derived names are defined.
Defined Defined
}
Engine is the Pundit authorization engine. Construct it with the seams the host supplies; the zero value with only Dispatch set is enough to authorize.
func (*Engine) Authorize ¶
Authorize resolves the policy for record, dispatches query against it and, following Pundit#authorize, returns the record (the pundit_model) on a true result. It returns a *NotDefinedError when no policy is defined, a *NotAuthorizedError when the query answered false, or whatever error Dispatch itself reports. For a namespaced Array subject the returned record is its last element, exactly as Pundit#pundit_model unwraps it.
func (*Engine) Policy ¶
Policy resolves the policy class name for record, returning ("", false) when no such policy is defined. It mirrors Pundit#policy, which returns nil in that case.
func (*Engine) PolicyBang ¶
PolicyBang resolves the policy class name for record or returns a *NotDefinedError, mirroring Pundit#policy!.
func (*Engine) PolicyScope ¶
PolicyScope resolves the scope for scope and runs it through ResolveScope, returning nil when no scope is defined — mirroring Pundit#policy_scope, which returns nil in that case.
func (*Engine) PolicyScopeBang ¶
PolicyScopeBang resolves the scope for scope and runs it, or returns a *NotDefinedError when no scope is defined — mirroring Pundit#policy_scope!.
type NotAuthorizedError ¶
type NotAuthorizedError struct {
// Query is the policy predicate that was checked (e.g. "update?").
Query string
// Record is the pundit_model that was checked (the Subject authorized, or
// the last element of a namespaced Array subject).
Record Subject
// Policy is the name of the policy class that answered the query.
Policy string
// Msg is the rendered message, matching MRI's NotAuthorizedError#message.
Msg string
}
NotAuthorizedError mirrors Pundit::NotAuthorizedError. Authorize returns it when the resolved policy's query method answered false. Like the gem it carries the Query that was checked, the Record that was checked, and the Policy class name that produced the answer, and its message reads "not allowed to <Policy>#<query> this <Record class>".
func (*NotAuthorizedError) Error ¶
func (e *NotAuthorizedError) Error() string
type NotDefinedError ¶
type NotDefinedError struct{ Msg string }
NotDefinedError mirrors Pundit::NotDefinedError. The bang resolvers (PolicyBang / ScopeBang and therefore Authorize / PolicyScopeBang) return it when no policy or scope class of the derived name is defined.
func (*NotDefinedError) Error ¶
func (e *NotDefinedError) Error() string
type PolicyFinder ¶
type PolicyFinder struct {
// Object is the subject being resolved.
Object Subject
}
PolicyFinder derives the policy and scope class names for a Subject. It is a faithful port of Pundit::PolicyFinder — pure string/naming logic, matching the gem's inflection exactly.
func NewPolicyFinder ¶
func NewPolicyFinder(object Subject) *PolicyFinder
NewPolicyFinder returns a PolicyFinder for the given subject, mirroring PolicyFinder.new(object).
func (*PolicyFinder) PolicyName ¶
func (f *PolicyFinder) PolicyName() string
PolicyName returns the policy class name for the subject, e.g. "PostPolicy". It mirrors the string that Pundit::PolicyFinder#policy would constantize.
func (*PolicyFinder) ScopeName ¶
func (f *PolicyFinder) ScopeName() string
ScopeName returns the scope class name for the subject, e.g. "PostPolicy::Scope", mirroring Pundit::PolicyFinder#scope ("#{policy}::Scope").
type PolicyScopingNotPerformedError ¶
type PolicyScopingNotPerformedError struct{ AuthorizationNotPerformedError }
PolicyScopingNotPerformedError mirrors Pundit::PolicyScopingNotPerformedError, which in the gem is a subclass of AuthorizationNotPerformedError. It embeds that type here to model the same is-a relationship. VerifyPolicyScoped returns it when a scoping check was never performed.
type ResolveScope ¶
ResolveScope runs the scope class named scopeClass for the given user over the scope subject and returns the resolved collection. It is the seam for `PolicyScope::Scope.new(user, scope).resolve` — the host instantiates and resolves the Ruby scope. The scope passed is the pundit_model Subject.
type Subject ¶
type Subject struct {
// Name is the subject's class name for a plain object or a Class subject
// (e.g. "Post"), or the symbol text for a Symbol subject (e.g. "blog_post").
Name string
// IsClass reports that the subject is itself a Ruby Class; Name is that
// class's own name.
IsClass bool
// IsSymbol reports that the subject is a Ruby Symbol; Name is its text and
// is camelized during resolution.
IsSymbol bool
// Elements, when non-nil, marks the subject as a Ruby Array used for
// namespaced lookups such as [:admin, post]; the last element is the record
// and the earlier ones form the module context. Must be non-empty.
Elements []Subject
// PolicyClass, when non-empty, means the object responds to :policy_class
// and returns this policy class name, overriding name derivation entirely.
PolicyClass string
// ClassPolicyClass, when non-empty, means the object's class responds to
// :policy_class and returns this name (checked after the object itself).
ClassPolicyClass string
// ModelName, when non-empty, means the object responds to :model_name
// (ActiveModel) and returns this class name.
ModelName string
// ClassModelName, when non-empty, means the object's class responds to
// :model_name and returns this name (checked after the object itself).
ClassModelName string
// Ruby is an opaque payload the host may attach — typically the underlying
// Ruby object — so the Dispatch and ResolveScope seams can reach it. The
// engine never inspects it.
Ruby any
}
Subject is the reflective view of a Ruby object that PolicyFinder inspects to derive a policy or scope class name. It captures exactly the questions the gem's PolicyFinder#find and #find_class_name ask of a subject, so the naming logic here can be pure Go with no Ruby runtime.
The host (rbgo) builds a Subject by reflecting on a real Ruby object; plain Go callers use the constructors Object, Class, Symbol and Array. The precedence among the fields matches the gem: an Array is handled first, then a :policy_class override (on the object, then its class), otherwise the class name is derived from :model_name (object, then class), a Class, a Symbol (camelized) or the object's class in turn.
func Array ¶
Array returns a namespaced Subject, e.g. Array(Symbol("admin"), Object("Post")) resolves to "Admin::PostPolicy". It must be given at least one element; the last is the record and the earlier ones form the module context.
func Class ¶
Class returns a Subject for a Ruby Class itself, e.g. Class("Post") (the Post class, not an instance) — it too resolves to "PostPolicy".
