Skip to content

Latest commit

 

History

History
125 lines (89 loc) · 4.23 KB

aliases.md

File metadata and controls

125 lines (89 loc) · 4.23 KB

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
end

In your policy, you can create aliases to avoid duplication:

class PostPolicy < ApplicationPolicy
  alias_rule :edit?, :destroy?, to: :update?
end

NOTE: 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
end

Now 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:

  1. If there is a concrete rule method on the subclass, this is called, else
  2. 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.
  1. If there is a concrete rule method on the superclass, then this is called, else
  2. If there is a default rule defined, then this is called, else
  3. If the default rule is unaltered, then the manage? rule is called
  4. If the default rule is set to nil, ActionPolicy::UnknownRule is 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
end

Authorizing against the SuperPolicy:

  • update? will resolve to edit?
  • destroy? will resolve to edit?
  • create? will resolve to edit?
  • manage? will resolve to manage?
  • edit? will resolve to edit?
  • index? will resolve to index?
  • something? will resolve to the default_rule: manage?

Authorizing against the SubPolicy:

  • index? will resolve to manage?
  • update? will resolve to manage?
  • create? will resolve to create?
  • destroy? will resolve to edit?
  • manage? will resolve to manage?
  • edit? will resolve to edit?
  • index? will resolve to manage?
  • something? will raise ActionPolicy::UnknownRule