Skip to content

Latest commit

 

History

History
127 lines (90 loc) · 3.19 KB

writing_policies.md

File metadata and controls

127 lines (90 loc) · 3.19 KB

Writing Policies

Policy class contains predicate methods (rules) which are used to authorize activities.

A Policy is instantiated with the target record (authorization object) and the authorization context (by default equals to user):

class PostPolicy < ActionPolicy::Base
  def index?
    # allow everyone to perform "index" activity on posts
    true
  end

  def update?
    # here we can access our context and record
    user.admin? || (user.id == record.user_id)
  end
end

Initializing policies

NOTE: it is not recommended to manually initialize policy objects and use them directly (one exclusion–tests). Use authorize! / allowed_to? methods instead.

To initialize policy object, you should specify target record and context:

policy = PostPolicy.new(post, user: user)

# simply call rule method
policy.update?

You can omit the first argument (in that case record would be nil).

Instead of calling rules directly, it is better to call the apply method (which wraps rule method with some useful functionality, such as caching, pre-checks, and failure reasons tracking):

policy.apply(:update?)

Calling other policies

Sometimes it is useful to call other resources policies from within a policy. Action Policy provides the allowed_to? method as a part of ActionPolicy::Base:

class CommentPolicy < ApplicationPolicy
  def update?
    user.admin? || (user.id == record.id) ||
      allowed_to?(:update?, record.post)
  end
end

You can also specify all the usual options (such as with).

There is also a check? method which is just an "alias"* for allowed_to? added for better readability:

class PostPolicy < ApplicationPolicy
  def show?
    user.admin? || check?(:publicly_visible?)
  end

  def publicly_visible?
    # ...
  end
end

* It's not a Ruby alias but a wrapper; we can't use alias or alias_method, 'cause allowed_to? could be extended by some extensions.

Fail-fast or pass-fast

For better readability, we provide an alternative API (originally introduced for pre-checks) to allow or deny a particular action. There are two methods—allow! and deny!:

class PostPolicy < ApplicationPolicy
  def show?
    allow! if user.admin?

    check?(:publicly_visible?)
  end

  def destroy?
    deny! if record.subscribers.any?

    # some general logic
  end
end

Identifiers

Each policy class has an identifier, which is by default just an underscored class name:

class CommentPolicy < ApplicationPolicy
end

CommentPolicy.identifier #=> :comment

For namespaced policies it has a form of:

module ActiveAdmin
  class UserPolicy < ApplicationPolicy
  end
end

ActiveAdmin::UserPolicy.identifier # => :"active_admin/user"

You can specify your own identifier:

module MyVeryLong
  class LongLongNamePolicy < ApplicationPolicy
    self.identifier = :long_name
  end
end

MyVeryLong::LongLongNamePolicy.identifier #=> :long_name

Identifiers are required for some modules, such as failure reasons tracking and i18n.