Documentation
¶
Overview ¶
Package cancancan is a pure-Go (no cgo) reimplementation of the rule ENGINE of Ruby's CanCanCan authorization gem (v3.6, MRI 4.0.5) — the Ability rule store and the can?/cannot?/authorize! matching semantics — without any Ruby runtime. It is the CanCanCan backend for go-embedded-ruby but is a standalone, reusable module with no dependency on the Ruby runtime.
What it models ¶
An Ability collects can/cannot rules over (action, subject) pairs. A query (CanQ) resolves by scanning the rules in REVERSE definition order and taking the first whose action, subject, and conditions all match — so a later rule overrides an earlier one and a cannot overrides a can, exactly as CanCanCan's Ability#relevant_rules + detect do.
- Actions are symbols. Manage is the wildcard (:manage) matching every action. Action aliases expand a declared action to the actions it grants: the CanCanCan defaults are read→{index,show}, create→{new}, update→{edit}, and AliasAction adds more (the gem's alias_action).
- Subjects are class tokens (Class), the wildcard All (:all), or specific instances. A Class rule matches that class and its instances; an instance rule matches by value equality.
- Conditions are a hash (attribute matcher) or a block/lambda. Neither can be evaluated when the subject is a class rather than an instance, so — as in CanCanCan — a class query against a conditional rule is treated as possible (it means "you can act on SOME such subject").
The Ruby seams ¶
The engine owns the rule store and the matching semantics; the two things it cannot know — how to read a Ruby object's attribute and how to run a Ruby condition block — are injected as function seams on the Ability:
- AttrGet(subject, key) reads an attribute for hash-condition matching. It stands in for `subject.send(key)`. The go-embedded-ruby binding dispatches the Ruby reader here.
- BlockEval(ruleID, subject) evaluates a rule's Ruby condition block against an instance. Can/Cannot return the rule's ID so the host can register the corresponding Ruby proc under it. It stands in for `block.call(subject)`.
Index ¶
- Constants
- Variables
- func IsCanCanError(err error) bool
- type Ability
- func (a *Ability) AliasAction(to Action, actions ...Action)
- func (a *Ability) AuthorizeBang(action Action, subject any) error
- func (a *Ability) Can(action, subject any, conditions ...any) int
- func (a *Ability) CanQ(action Action, subject any) bool
- func (a *Ability) Cannot(action, subject any, conditions ...any) int
- func (a *Ability) CannotQ(action Action, subject any) bool
- type AccessDenied
- type Action
- type AuthorizationNotPerformed
- type Block
- type Class
- type Classified
- type Error
Constants ¶
const DefaultAccessDeniedMessage = "You are not authorized to access this page."
DefaultAccessDeniedMessage is the message CanCan::AccessDenied uses when the caller supplies none, matching the gem's default.
Variables ¶
var All = allT{}
All is the wildcard subject (:all): a rule declared with it matches every subject, mirroring CanCanCan's :all.
var Manage = manageT{}
Manage is the wildcard action (:manage): a rule declared with it matches every action, mirroring CanCanCan's :manage.
Functions ¶
func IsCanCanError ¶
IsCanCanError reports whether err is one of the modeled CanCan error types, mirroring the gem's `rescue CanCan::Error` (which catches AccessDenied and AuthorizationNotPerformed too, since both subclass CanCan::Error).
Types ¶
type Ability ¶
type Ability struct {
// AttrGet reads subject's attribute key for hash-condition matching. It
// stands in for `subject.send(key)`; required only when hash-conditioned
// rules are evaluated against instances.
AttrGet func(subject any, key string) any
// BlockEval evaluates the rule ruleID's Ruby condition block against
// subject. It stands in for `block.call(subject)`; required only when
// block-conditioned rules are evaluated against instances.
BlockEval func(ruleID int, subject any) bool
// contains filtered or unexported fields
}
Ability is a store of can/cannot rules plus the matching engine, mirroring a class that `include CanCan::Ability`. Construct it with New; set AttrGet and BlockEval before evaluating hash- or block-conditioned rules.
func New ¶
func New() *Ability
New returns an empty Ability seeded with CanCanCan's default action aliases (read→index,show; create→new; update→edit).
func (*Ability) AliasAction ¶
AliasAction declares that granting `to` also grants `actions`, mirroring the gem's `alias_action *actions, to: to`. It appends to any existing aliases for `to` (the defaults included).
func (*Ability) AuthorizeBang ¶
AuthorizeBang returns nil when the ability permits action on subject, or an *AccessDenied otherwise, mirroring `authorize!(action, subject)` (which raises CanCan::AccessDenied on denial). The returned error carries the action and subject.
func (*Ability) Can ¶
Can adds a permission rule. action is an Action/string, Manage, or a slice of these; subject is a Class, All, an instance, or a slice of these; conditions are an optional hash (map[string]any) and/or Block. It returns the new rule's ID (for wiring a Ruby block via BlockEval). It mirrors `can action, subject, conditions, &block`.
func (*Ability) CanQ ¶
CanQ reports whether the ability permits action on subject, mirroring `can?(action, subject)`. It scans rules from last-defined to first-defined and returns the base_behavior of the first whose action, subject, and conditions all match; if none matches it returns false.
type AccessDenied ¶
AccessDenied mirrors CanCan::AccessDenied (a CanCan::Error subclass). It is returned by AuthorizeBang when the ability denies the (Action, Subject) pair. It carries the denied action and subject so a host can render a tailored message, exactly as the gem exposes #action and #subject.
func NewAccessDenied ¶
func NewAccessDenied(message string, action Action, subject any) *AccessDenied
NewAccessDenied builds an AccessDenied for the denied (action, subject). An empty message is replaced by DefaultAccessDeniedMessage, mirroring CanCan::AccessDenied.new(message, action, subject).
func (*AccessDenied) Error ¶
func (e *AccessDenied) Error() string
type AuthorizationNotPerformed ¶
type AuthorizationNotPerformed struct{ Msg string }
AuthorizationNotPerformed mirrors CanCan::AuthorizationNotPerformed (a CanCan::Error subclass). The gem raises it from check_authorization when a controller action finishes without ever calling authorize!; it is provided here so a host controller layer can model the same guarantee.
func (*AuthorizationNotPerformed) Error ¶
func (e *AuthorizationNotPerformed) Error() string
type Block ¶
type Block struct{}
Block marks a rule as carrying a Ruby condition block/lambda. Pass it as a condition to Can/Cannot; at match time against an instance the engine calls Ability.BlockEval(ruleID, subject). It stands in for the gem's `&block`.
type Class ¶
type Class string
Class is a subject class token — a Ruby class identified by name. A rule declared with a Class matches that class itself and any of its instances (see Classified).
type Classified ¶
Classified is the optional interface a subject instance may implement so the engine can match it against a Class rule. The go-embedded-ruby binding wraps a Ruby object to report its class and ancestor chain here, letting a rule on a superclass match an instance of a subclass — the gem's matches_subject_class?.
