Rule Aliases
Action Policy allows you to add rule aliases. It is useful when you rely on implicit rules in controllers. For example:
class PostsController < ApplicationController
before_action :load_post, only: [:edit, :update, :destroy]
private
def load_post
@post = Post.find(params[:id])
# depending on action, an `edit?`, `update?` or `destroy?`
# rule would be applied
authorize! @post
end
endIn your policy, you can create aliases to avoid duplication:
class PostPolicy < ApplicationPolicy
alias_rule :edit?, :destroy?, to: :update?
endNOTE: alias_rule is available only if you inherit from ActionPolicy::Base or include ActionPolicy::Policy::Aliases into your ApplicationPolicy.
Why not just use Ruby's alias?
An alias created with alias_rule is resolved at authorization time (during an authorize! or allowed_to? call), and it does not add an alias method to the class.
That allows us to write tests easier, as we should only test the rule, not the alias–and to leverage caching better.
By default, ActionPolicy::Base adds one alias: alias_rule :new?, to: :create?.
Default rule
You can add a default rule–the rule that would be applied if the rule specified during authorization is missing (like a "wildcard" alias):
class PostPolicy < ApplicationPolicy
# For an ApplicationPolicy, makes :manage? match anything that is
# not :index?, :create? or :new?
default_rule :manage?
# If you want manage? to catch really everything, place this alias
#alias_rule :index?, :create?, :new?, to: :manage?
def manage?
# ...
end
endNow when you call authorize! post with any rule not defined in the policy class, the manage? rule is applied. Note that index? create? and new? are already defined in the superclass by default (returning false) - if you want the same behaviour for all actions, define aliases like in the example above (commented out).
By default, ActionPolicy::Base sets manage? as a default rule.
Mistypes & Default
In case you forget to add ? at the end of :new in a code like allowed_to?(:new, User), a wrong rule will be called. Instead of the:new?, the :manage? rule will be applied. You can set ActionPolicy.enforce_predicate_rules_naming = true to raise an error when the called rule doesn't end with a question mark.
Aliases and Private Methods
Rules in action_policy can only be public methods. Trying to use a private method as a rule will raise an error. Thus, aliases can also only point to public methods.
Rule resolution with subclasses
Here's the order in which aliases and concrete rule methods are resolved in regards to subclasses:
- If there is a concrete rule method on the subclass, this is called, else
- If there is a matching alias then this is called, else
- When aliases are defined on the subclass they will overwrite matching aliases on the superclass.
- If there is a concrete rule method on the superclass, then this is called, else
- If there is a default rule defined, then this is called, else
- If the default rule is unaltered, then the
manage?rule is called - If the default rule is set to
nil,ActionPolicy::UnknownRuleis raised.
Here's an example with the expected results:
class SuperPolicy < ApplicationPolicy
alias_rule :update?, :destroy?, :create?, to: :edit?
def manage?
end
def edit?
end
def index?
end
end
class SubPolicy < AbstractPolicy
default_rule nil
alias_rule :index?, :update?, to: :manage?
def create?
end
endAuthorizing against the SuperPolicy:
update?will resolve toedit?destroy?will resolve toedit?create?will resolve toedit?manage?will resolve tomanage?edit?will resolve toedit?index?will resolve toindex?something?will resolve to thedefault_rule:manage?
Authorizing against the SubPolicy:
index?will resolve tomanage?update?will resolve tomanage?create?will resolve tocreate?destroy?will resolve toedit?manage?will resolve tomanage?edit?will resolve toedit?index?will resolve tomanage?something?will raiseActionPolicy::UnknownRule