agentleFS
izkrenyizkreny/agentifico/skills/action-policy-guru/assets/llms-full.txt

agentifico / assets

llms.txt4 starsChanged 15 days agoMITView on GitHub

[](https://badge.fury.io/rb/action_policy) Authorization is an act of giving someone official permission to do something (to not be confused with authentication). Action Policy provides flexible tools to build an authorization layer for your application. NOTE: Action Policy does not force you to use a specific authorization model (i.e., roles, permissions, etc.) and does not provide one. It only answers a single question: How to verify access? The project is being used in production since mid 2018. Major features have been implemented, API…

The file

Show its rules and text

We split this llms-full.txt into the rules it gives an agent

76 rules. About 0 are common, 28 are distinctive, and 48 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈28

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 48

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Composition & Customization: use only the features you need or easily extend the functionality–it's just Ruby classes and modules, (almost) …

    line 52
  2. Distinctive:

    Code Organization: use namespaces to organize your policies (for example, when you have multiple authorization strategies).

    line 54
  3. Very specific:

    All three instance methods (authorize!, allowed_to?, authorized) uses the same policy_for to lookup a policy class for authorization target.

    line 153
  4. Very specific:

    If you want to allow passing nil as account value, you must add allow_nil: true option to authorize.

    line 255
  5. Very specific:

    If you want to be able not to pass account at all, you must add optional: true:

    line 256
  6. Very specific:

    When you call another policy from the policy object (e.g.

    line 297
  7. Distinctive:

    all authorizations use the same authorization context.

    line 396
  8. Distinctive:

    Consider a more complex situation:

    line 406
  9. Very specific:

    If you want to add this behavior to your custom authorization-aware class, you should care about cleaning up the thread store manually (by …

    line 447
  10. Very specific:

    If any object does not respond to #policy_cache_key, we fallback to #cache_key (or #cache_key_with_version for modern Rails versions).

    line 529
  11. Very specific:

    NOTE: if your #cache_key method is performance-heavy (e.g.

    line 531
  12. Distinctive:

    Case #1: no invalidation required.

    line 557
  13. Very specific:

    Keep tracking the last Access added/updated/deleted for resource (e.g.

    line 567
  14. Distinctive:

    Use the following cache keys:

    line 568
  15. Very specific:

    If you have a limited number of places in your application where you update access control, you can invalidate policies cache manually.

    line 606
  16. Very specific:

    NOTE: When using with Rails, custom scope matchers are also registered within the Base class.

    line 713
  17. Distinctive:

    If you plan to use them, you need to copy setup code from the corresponding files.

    line 714
  18. Distinctive:

    Call the first probe;

    line 727
  19. Distinctive:

    When dealing with complex policies, it could be hard to figure out which predicate/check made policy to fail.

    line 818
  20. Distinctive:

    Consider a (synthetic) example:

    line 822
  21. Distinctive:

    When you have complex policy rules, it could be helpful to have an ability to define an exact reason for why a specific authorization was …

    line 882
  22. Very specific:

    When ApplicantPolicy#show?

    line 900
  23. Very specific:

    NOTE: when using detailed reasons, the details array contains as the last element a hash with ALL details reasons for the policy (in a form …

    line 999
  24. Very specific:

    If you want to return a nil instead, you should add raise: false to the options:

    line 1142
  25. Very specific:

    If you want to perform authorization before resolving the field value, you can use preauthorize: * option:

    line 1161
  26. Very specific:

    If you want to authorize the mutation, you call authorize!

    line 1222
  27. Very specific:

    Check out this issue on how you can implement a verify_authorized callback for your mutations: #28.

    line 1244
  28. Very specific:

    When using Rails, all you need is to add translations to any file under the config/locales folder (or create a new file, e.g.

    line 1430
  29. Distinctive:

    Use a default message provided by the gem

    line 1448
  30. Very specific:

    when authorize! is called

    line 1469
  31. Very specific:

    when allowed_to? is called within the policy or the behaviour

    line 1470
  32. Very specific:

    when apply_rule is called explicitly (i.e.

    line 1471
  33. Very specific:

    If you don't use Rails itself but have ActiveSupport::Notifications available in your application, you can use the instrumentation feature …

    line 1537
  34. Very specific:

    Remove include Pundit from ApplicationController

    line 1563
  35. Very specific:

    Add policy and policy_scope helpers:

    line 1578
  36. Very specific:

    Add include ActionPolicy::Policy::Core to ApplicationPolicy

    line 1602
  37. Distinctive:

    When everything is green, it's time to fully migrate to ActionPolicy:

    line 1630
  38. Very specific:

    add authorization tests (add require 'action_policy/rspec')

    line 1635
  39. Distinctive:

    use Reasons, I18n integration, cache and other Action Policy features!

    line 1636
  40. Very specific:

    When using Rails it's enabled only in production mode but could be configured through setting the …

    line 1719
  41. Very specific:

    If the target is a Symbol:

    line 1730
  42. Very specific:

    If the target responds to policy_class, then use it.

    line 1736
  43. Very specific:

    If the target's class responds to policy_class, then use it.

    line 1738
  44. Very specific:

    If the target or the target's class responds to policy_name, then use it (the policy_name should end with Policy as it's not appended …

    line 1740
  45. Very specific:

    When no policy class is found, an ActionPolicy::NotFound error is raised.

    line 1748
  46. Distinctive:

    Consider a typical situation when you start most—or even all—of your rules with the same predicates.

    line 1759
  47. Very specific:

    put policies into the app/policies folder (when using with Rails).

    line 1848
  48. Distinctive:

    Consider a simple example:

    line 1869
  49. Very specific:

    When authorization is successful (i.e., the corresponding rule returns true), nothing happens, but in case of an authorization failure …

    line 1907
  50. Distinctive:

    If there is a concrete rule method on the subclass, this is called, else

    line 2018
  51. Distinctive:

    If there is a matching alias then this is called, else

    line 2019
  52. Distinctive:

    When aliases are defined on the subclass they will overwrite matching aliases on the superclass.

    line 2021
  53. Distinctive:

    If there is a concrete rule method on the superclass, then this is called, else

    line 2023
  54. Distinctive:

    If there is a default rule defined, then this is called, else

    line 2024
  55. Very specific:

    If the default rule is unaltered, then the manage?

    line 2025
  56. Very specific:

    If the default rule is set to nil, ActionPolicy::UnknownRule is raised.

    line 2026
  57. Very specific:

    When the second argument is not specified, the :default is implied as the scope name.

    line 2174
  58. Very specific:

    When authorized_scope is called without the explicit scope type, Action Policy uses matchers (in the order they're defined) to infer the …

    line 2305
  59. Very specific:

    Use scopes of type :params if your strong parameters filtering depends on the current user:

    line 2350
  60. Distinctive:

    When you use policies for authorization, it is possible to split testing into the following parts:

    line 2393
  61. Distinctive:

    Test the policy class itself

    line 2395
  62. Distinctive:

    Test that the required authorization is performed within your authorization layer (controller, channel, etc.)

    line 2396
  63. Distinctive:

    Test that the required scoping has been applied.

    line 2397
  64. Distinctive:

    Consider an RSpec example:

    line 2403
  65. Very specific:

    Add the following to your rails_helper.rb (or spec_helper.rb):

    line 2436
  66. Distinctive:

    If test failed the exception message includes the result and failure reasons (if any):

    line 2499
  67. Distinctive:

    If you have debugging utils installed the message also includes the *annotated* source code of the policy rule:

    line 2509
  68. Very specific:

    If you omit .with(PostPolicy) then the inferred policy for the target (post) would be used.

    line 2676
  69. Very specific:

    When specifying contexts in the authorization, you can use the with_context modifier to ensure the context match.

    line 2678
  70. Very specific:

    When you test views that call policies methods as allowed_to?, your may have Missing policy authorization context: user error.

    line 2814
  71. Very specific:

    When you lint your RSpec spec files with rubocop-rspec, it will fail to detect RSpec aliases that Action Policy defines.

    line 2839
  72. Very specific:

    Make sure to use rubocop-rspec 2.0 or newer and add the following to your .rubocop.yml:

    line 2840
  73. Very specific:

    If you don't want to include Action Policy in your controllers at all, you can disable the integration by setting …

    line 2892
  74. Very specific:

    When an unauthorized action is encountered, the ActionPolicy::UnauthorizedAction error is raised.

    line 2924
  75. Very specific:

    When an unscoped action is encountered, the ActionPolicy::UnscopedAction error is raised.

    line 2955
  76. Very specific:

    Instead of calling rules directly, it is better to call the apply method (which wraps rule method with some useful functionality, such as …

    line 3094

≈ “Common” is an estimate: at least 1 in 5 of the 3,592 llms.txt files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
llms-full.txt
---
url: /guide.md
---
[![Gem Version](https://badge.fury.io/rb/action_policy.svg)](https://badge.fury.io/rb/action_policy)

# Action Policy

## What is it?

*Authorization* is an act of giving **someone** official
permission to **do something** (to not be confused with [*authentication*](https://en.wikipedia.org/wiki/Authentication)).

Action Policy provides flexible tools to build an *authorization layer* for your application.

**NOTE:** Action Policy does not force you to use a specific authorization model (i.e., roles, permissions, etc.) and does not provide one. It only answers a single question: **How to verify access?**

## Where to go from here?

* [Quick start](./quick_start.md)
* [Using with Rails](./rails.md)
* [Using with other Ruby frameworks](./non_rails.md)
* [Using with GraphQL Ruby](./graphql.md)

## Project State

The project is being used in production since mid 2018. Major features have been implemented, API has been stabilized. Check out our [development board](https://github.com/palkan/action_policy/projects/1) to see what's coming next.

## History

Action Policy gem is an *extraction*-kind of a library. Most of the code has been used in production for several years in different [Evil Martians][] projects.

We have decided to collect all our authorization techniques and pack them into a standalone gem–and that is how Action Policy was born!

## What about the existing solutions?

Why did we decide to build our own authorization gem instead of using the existing solutions, such as [Pundit][] and [CanCanCan][]?

**TL;DR they didn't solve all of our problems.**

[Pundit][] has been our framework of choice for a long time. Being too *dead-simple*, it required a lot of hacking to fulfill business logic requirements.

These *hacks* later became Action Policy (initially, we even called it "Pundit, re-visited").

We also took a few ideas from [CanCanCan][]—such as [default rules and rule aliases](./aliases.md).

It is also worth noting that Action Policy (despite having a *Railsy* name) is designed to be **Rails-free**. On the other hand, it contains some Rails-specific extensions and seamlessly integrates into the framework.

So, what are the main reasons to consider Action Policy as your authorization tool?

* **Performance**: multiple [caching strategies](./caching.md) out-of-the-box make authorization overhead as small as possible–especially useful when your rules involve DB queries; you can also monitor the performance and detect the bottlenecks using the built-in [instrumentation](./instrumentation.md) features.

* **Composition & Customization**: use [only the features you need](./custom_policy.md) or easily extend the functionality–it's just Ruby classes and modules, (almost) zero magic! And you can add authorization [anywhere in your code](./non_rails.md), not only in controllers.

* **Code Organization**: use [namespaces](./namespaces.md) to organize your policies (for example, when you have multiple authorization strategies); add [pre-checks](./pre_checks.md) to make rules more readable and better express your business-logic.

* **...and more**: [testability](./testing.md), [i18n](./i18n.md) integrations, [actionable errors](./reasons.md).

Learn more about the motivation behind the Action Policy and its features by watching this [RailsConf talk](https://www.youtube.com/watch?v=NVwx0DARDis).

## Resources

* RubyRussia, 2019 "Welcome, or access denied?" talk ([video](https://www.youtube.com/watch?v=y15a2g7v8i0) \[RU], [slides](https://speakerdeck.com/palkan/rubyrussia-2019-welcome-or-access-denied))

* [Exposing permissions in GraphQL APIs with Action Policy](https://evilmartians.com/chronicles/exposing-permissions-in-graphql-apis-with-action-policy)

* Seattle.rb, 2019 "A Denial!" talk \[[slides](https://speakerdeck.com/palkan/seattle-dot-rb-2019-a-denial)]

* RailsConf, 2018 "Access Denied" talk \[[video](https://www.youtube.com/watch?v=NVwx0DARDis), [slides](https://speakerdeck.com/palkan/railsconf-2018-access-denied-the-missing-guide-to-authorization-in-rails)]

[CanCanCan]: https://github.com/CanCanCommunity/cancancan

[Pundit]: https://github.com/varvet/pundit

[Evil Martians]: https://evilmartians.com

---

---
url: /guide/behaviour.md
---
# Action Policy Behaviour

Action Policy provides a mixin called `ActionPolicy::Behaviour` which adds authorization methods to your classes.

## Usage

Let's make our custom *service* object aware of authorization:

```ruby
class PostUpdateAction
  # First, we should include the behaviour
  include ActionPolicy::Behaviour

  # Secondly, provide authorization subject (performer)
  authorize :user

  attr_reader :user

  def initialize(user)
    @user = user
  end

  def call(post, params)
    # Now we can use authorization methods
    authorize! post, to: :update?

    post.update!(params)
  end
end
```

`ActionPolicy::Behaviour` provides `authorize` class-level method to configure [authorization context](authorization_context.md) and the instance-level methods: `authorize!`, `allowed_to?`, `allowance_to`, and `authorized`:

### `authorize!`

This is a *guard-method* which raises an `ActionPolicy::Unauthorized` exception
if authorization failed (i.e. policy rule returns false):

```ruby
# `to` is a name of the policy rule to apply
authorize! post, to: :update?
```

### `allowed_to?`

This is a *predicate* version of `authorize!`: it returns true if authorization succeed and false otherwise:

```ruby
# the first argument is the rule to apply
# the second one is the target
if allowed_to?(:edit?, post)
  # ...
end
```

### `allowance_to`

This method is similar to `allowed_to?` but returns an authorization result instead. It's especially useful for APIs when you want to
return not only true or false but also, for example, [failure reasons](./reasons.md):

```ruby
result = allowance_to(:edit?, post)

{value: result.value, fullMessages: result.reasons.full_messages, details: result.reasons.details}.to_json
```

### `authorized`

See [scoping](./scoping.md) docs.

## Policy lookup

All three instance methods (`authorize!`, `allowed_to?`, `authorized`) uses the same
`policy_for` to lookup a policy class for authorization target. So, you can provide additional options to control the policy lookup process:

* Explicitly specify policy class using `with` option:

```ruby
allowed_to?(:edit?, post, with: SpecialPostPolicy)
```

* Provide a [namespace](./namespaces.md):

```ruby
# Would try to lookup Admin::PostPolicy first
authorize! post, to: :destroy?, namespace: Admin
```

* Provide a [strict\_namespace lookup option](./lookup_chain.md):

```ruby
# Would not fallback lookup PostPolicy if Admin::PostPolicy doesn't exist
authorize! post, to: :destroy?, namespace: Admin, strict_namespace: true

# or by overriding a specific behavior method
def authorization_strict_namespace
  true
end
```

* Define a default policy to use in case lookup finds nothing:

```ruby
# either explicitly
authorize! post, to: :destroy?, default: GuestPolicy

# or by overriding a specific behavior method
def default_authorization_policy_class
  logged_in? ? DefaultUserPolicy : GuestPolicy
end
```

## Implicit authorization target

You can omit the authorization target for all the methods by defining an *implicit authorization target*:

```ruby
class PostActions
  include ActionPolicy::Behaviour

  authorize :user

  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update(params)
    # post is used here implicitly as a target
    authorize! to: :update

    post.update!(params)
  end

  def destroy
    # post is used here implicitly as a target
    authorize! to: :destroy

    post.destroy!
  end

  def implicit_authorization_target
    post
  end
end
```

---

---
url: /guide/authorization_context.md
---
# Authorization Context

*Authorization context* contains all contextual information required to apply a policy rule.

In most cases, it only contains a *user*. However, if needed, Action Policy allows extending a policy's authorization context to include any additional information required to contextualize the authorization of a system or a particular resource.

You must configure authorization context in **two places**: in the policy itself and in the place where you perform the authorization (e.g., controllers).

By default, `ActionPolicy::Base` includes `user` as authorization context. If you don't need it, you have to [build your own base policy](custom_policy.md).

To specify additional contexts, you should use the `authorize` method:

```ruby
class ApplicationPolicy < ActionPolicy::Base
  authorize :account
end
```

Now you must provide `account` during policy initialization. When authorization key is missing or equals to `nil`, `ActionPolicy::AuthorizationContextMissing` error is raised.

If you want to allow passing `nil` as `account` value, you must add `allow_nil: true` option to `authorize`.
If you want to be able not to pass `account` at all, you must add `optional: true`:

```ruby
class GuestPolicy < ApplicationPolicy
  # With allow_nil: true, the `user` key is still required to be present
  # in the authorization context
  authorize :user, allow_nil: true
end

class ProjectPolicy < ApplicationPolicy
  # With optional: true, authorization context may not include the `team` key at all
  authorize :team, optional: true
end

GuestPolicy.new(user: nil) #=> OK
GuestPolicy.new #=> raises ActionPolicy::AuthorizationContextMissing

ProjectPolicy.new(user: user) #=> OK
```

To do that automatically in your `authorize!` and `allowed_to?` calls, you must also configure authorization context. For example, in your controller:

```ruby
class ApplicationController < ActionController::Base
  # First argument should be the same as in the policy.
  # `through` specifies the method name to be called to
  # get the required context object
  # (equals to the context name itself by default, i.e. `account`)
  authorize :account, through: :current_account

  # `through` can also be passed a proc that will be executed against self:
  authorize :user, through: -> { @user || Current.user }
end
```

**NOTE:** To un-register a context (e.g., if you want to remove `:user` from the Base policy class), you can manipulate the contexts map directly: `authorization_targets.delete(:user)`.

## Nested Policies vs Contexts

See also: [action\_policy#36](https://github.com/palkan/action_policy/issues/36) and [action\_policy#37](https://github.com/palkan/action_policy/pull/37)

When you call another policy from the policy object (e.g. via `allowed_to?` method),
the context of the current policy is passed to the *nested* policy.

That means that if the nested policy has a different authorization context, we won't be able
to build it (even if you configure all the required keys in the controller).

For example:

```ruby
class UserPolicy < ActionPolicy::Base
  authorize :user

  def show?
    allowed_to?(:show?, record.profile)
  end
end

class ProfilePolicy < ActionPolicy::Base
  authorize :user, :account
end

class ApplicationController < ActionController::Base
  authorize :user, through: :current_user
  authorize :account, through: :current_account
end

class UsersController < ApplicationController
  def show
    user = User.find(params[:id])

    authorize! user #=> raises "Missing policy authorization context: account"
  end
end
```

That means that **all the policies that could be used together MUST share the same set of authorization contexts** (or at least the *parent* policies' contexts must be supersets of the nested policies' contexts).

## Explicit context

You can override the *implicit* authorization context (generated with `authorize` method) in-place
by passing the `context` option:

```ruby
def show
  user = User.find(params[:id])

  authorize! user, context: {account: user.account}
end
```

**NOTE:** the explicitly provided context is merged with the implicit one (i.e. you can specify
only the keys you want to override).

---

---
url: /guide/caching.md
---
# Caching

Action Policy aims to be as performant as possible. One of the ways to accomplish that is to include a comprehensive caching system.

There are several cache layers available: rule-level memoization, local (instance-level) memoization, and *external* cache (through cache stores).

## Policy memoization

### Per-instance

There could be a situation when you need to apply the same policy to the same record multiple times during the action (e.g., request). For example:

```ruby
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
    authorize! @post
    render :show
  end
end
```

```erb
# app/views/posts/show.html.erb
<h1><%= @post.title %>

<% if allowed_to?(:edit?, @post) %>
  <%= link_to "Edit", @post %>
<% end %>

<% if allowed_to?(:destroy?, @post) %>
  <%= link_to "Delete", @post, method: :delete %>
<% end %>
```

In the above example, we need to use the same policy three times. Action Policy re-uses the policy instance to avoid unnecessary object allocation.

We rely on the following assumptions:

* parent object (e.g., a controller instance) is *ephemeral*, i.e., it is a short-lived object
* all authorizations use the same [authorization context](authorization_context.md).

We use `record.policy_cache_key` with fallback to `record.cache_key` or `record.object_id` as a part of policy identifier in the local store.

**NOTE**: policies memoization is an extension for `ActionPolicy::Behaviour` and could be included with `ActionPolicy::Behaviours::Memoized`.

**NOTE**: memoization is automatically included into Rails controllers integration, but not included into channels integration, since channels are long-lived objects.

### Per-thread

Consider a more complex situation:

```ruby
# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
  def index
    # all comments for all posts
    @comments = Comment.all
  end
end
```

```erb
# app/views/comments/index.html.erb
<% @comments.each do |comment| %>
  <li><%= comment.text %>
    <% if allowed_to?(:edit?, comment) %>
      <%= link_to comment, "Edit" %>
    <% end %>
  </li>
<% end %>
```

```ruby
# app/policies/comment_policy.rb
class CommentPolicy < ApplicationPolicy
  def edit?
    user.admin? || (user.id == record.id) ||
      allowed_to?(:manage?, record.post)
  end
end
```

In some cases, we have to initialize **two** policies for each comment: one for the comment itself and one for the comment's post (in the `allowed_to?` call).

That is an example of a *N+1 authorization* problem, which in its turn could easily cause a *N+1 query* problem (if `PostPolicy#manage?` makes database queries). Sounds terrible, doesn't it?

It is likely that many comments belong to the same post. If so, we can move our memoization one level up and use local thread store.

Action Policy provides `ActionPolicy::Behaviours::ThreadMemoized` module with this functionality (included into Rails controllers integration by default).

If you want to add this behavior to your custom authorization-aware class, you should care about cleaning up the thread store manually (by calling `ActionPolicy::PerThreadCache.clear_all`).

**NOTE:** per-thread cache is disabled by default in test environment (when either `RACK_ENV` or `RAILS_ENV` environment variable is equal to "test").
You can turn it on (or off) by setting:

```ruby
ActionPolicy::PerThreadCache.enabled = true # or false to disable
```

## Rule cache

### Per-instance

There could be a situation when the same rule is called multiple times for the same policy instance (for example, when using [aliases](aliases.md)).

In that case, Action Policy invokes the rule method only once, remembers the result, and returns it immediately for the subsequent calls.

**NOTE**: rule results memoization is available only if you inherit from `ActionPolicy::Base` or include `ActionPolicy::Policy::CachedApply` into your `ApplicationPolicy`.

### Using the cache store

Some policy rules might be *performance-heavy*, e.g., make complex database queries.

In that case, it makes sense to cache the rule application result for a long time (not just for the duration of a request).

Action Policy provides a way to use *cache stores* for that. You have to explicitly define which rules you want to cache in your policy class. For example:

```ruby
class StagePolicy < ApplicationPolicy
  # mark show? rule to be cached
  cache :show?
  # you can also provide store-specific options
  # cache :show?, expires_in: 1.hour

  def show?
    full_access? ||
      user.stage_permissions.where(
        stage_id: record.id
      ).exists?
  end

  private

  def full_access?
    !record.funnel.is_private? ||
      user.permissions
        .where(
          funnel_id: record.funnel_id,
          full_access: true
        ).exists?
  end
end
```

You must configure a cache store to use this feature:

```ruby
ActionPolicy.cache_store = MyCacheStore.new
```

Or, in Rails:

```ruby
# config/application.rb (or config/environments/<environment>.rb)
Rails.application.configure do |config|
  config.action_policy.cache_store = :redis_cache_store
end
```

Cache store must provide at least a `#read(key)` and `#write(key, value, **options)` methods.

**NOTE:** cache store also should take care of serialiation/deserialization since the `value` is `ExecutionResult` instance (which contains also some additional information, e.g. failure reasons). Rails cache store supports serialization/deserialization out-of-the-box.

By default, Action Policy builds a cache key using the following scheme (defined in `#rule_cache_key(rule)` method):

```ruby
"#{cache_namespace}/#{context_cache_key}" \
"/#{record.policy_cache_key}/#{policy.class.name}/#{rule}"
```

Where `cache_namespace` is equal to `"acp:#{MAJOR_GEM_VERSION}.#{MINOR_GEM_VERSION}"`, and `context_cache_key` is a concatenation of all authorization contexts cache keys (in the same order as they are defined in the policy class).

If any object does not respond to `#policy_cache_key`, we fallback to `#cache_key` (or `#cache_key_with_version` for modern Rails versions). If `#cache_key` is not defined, an `ArgumentError` is raised.

**NOTE:** if your `#cache_key` method is performance-heavy (e.g. like the `ActiveRecord::Relation`'s one), we recommend to explicitly define the `#policy_cache_key` method on the corresponding class to avoid unnecessary load. See also [action\_policy#55](https://github.com/palkan/action_policy/issues/55).

You can define your own `rule_cache_key` / `cache_namespace` / `context_cache_key` methods for policy class to override this logic.

You can also use the `#cache` instance method to cache arbitrary values in you policies:

```ruby
class ApplicationPolicy < ActionPolicy::Base
  # Suppose that a user has many roles each having an array of permissions
  def permissions
    cache(user) { user.roles.pluck(:permissions).flatten.uniq }
  end

  # You can pass multiple cache key "parts"
  def account_permissions(account)
    cache(user, account) { user.account_roles.where(account: account).pluck(:permissions).flatten.uniq }
  end
end
```

**NOTE:** `#cache` method uses the same cache key generation logic as rules caching (described above).

#### Invalidation

There no one-size-fits-all solution for invalidation. It highly depends on your business logic.

**Case #1**: no invalidation required.

First of all, you should try to avoid manual invalidation at all. That could be achieved by using elaborate cache keys.

Let's consider an example.

Suppose that your users have *roles* (i.e. `User.belongs_to :role`) and you give access to resources through the `Access` model (i.e. `Resource.has_many :accesses`).

Then you can do the following:

* Keep tracking the last `Access` added/updated/deleted for resource (e.g. `Access.belongs_to :accessessable, touch: :access_updated_at`)
* Use the following cache keys:

```ruby
class User
  def policy_cache_key
    "user::#{id}::#{role_id}"
  end
end

class Resource
  def policy_cache_key
    "#{resource.class.name}::#{id}::#{access_updated_at}"
  end
end
```

**Case #2**: discarding all cache at once.

That's pretty easy: just override `cache_namespace` method in your `ApplicationPolicy` with the new value:

```ruby
class ApplicationPolicy < ActionPolicy::Base
  # It's a good idea to store the changing part in the constant
  CACHE_VERSION = "v2".freeze

  # or even from the env variable
  # CACHE_VERSION = ENV.fetch("POLICY_CACHE_VERSION", "v2").freeze

  def cache_namespace
    "action_policy::#{CACHE_VERSION}"
  end
end
```

**Case #3**: discarding some keys.

That is an alternative approach to *crafting* cache keys.

If you have a limited number of places in your application where you update access control,
you can invalidate policies cache manually. If your cache store supports `delete_matched` command (deleting keys using a wildcard), you can try the following:

```ruby
class ApplicationPolicy < ActionPolicy::Base
  # Define custom cache key generator
  def cache_key(rule)
    "policy_cache/#{user.id}/#{self.class.name}/#{record.id}/#{rule}"
  end
end

class Access < ApplicationRecord
  belongs_to :resource
  belongs_to :user

  after_commit :cleanup_policy_cache, on: [:create, :destroy]

  def cleanup_policy_cache
    # Clear cache for the corresponding user-record pair
    ActionPolicy.cache_store.delete_matched(
      "policy_cache/#{user_id}/#{ResourcePolicy.name}/#{resource_id}/*"
    )
  end
end

class User < ApplicationRecord
  belongs_to :role

  after_commit :cleanup_policy_cache, on: [:update], if: :role_id_changed?

  def cleanup_policy_cache
    # Clear all policies cache for user
    ActionPolicy.cache_store.delete_matched(
      "policy_cache/#{user_id}/*"
    )
  end
end
```

---

---
url: /guide/custom_policy.md
---
# Custom Base Policy

`ActionPolicy::Base` is a combination of all available policy extensions with the default configuration.

It looks like this:

```ruby
class ActionPolicy::Base
  include ActionPolicy::Policy::Core
  include ActionPolicy::Policy::Authorization
  include ActionPolicy::Policy::PreCheck
  include ActionPolicy::Policy::Reasons
  include ActionPolicy::Policy::Aliases
  include ActionPolicy::Policy::Scoping
  include ActionPolicy::Policy::Cache
  include ActionPolicy::Policy::CachedApply
  include ActionPolicy::Policy::Defaults

  # Rails-specific scoping extensions
  extend ActionPolicy::ScopeMatchers::ActiveRecord

  scope_matcher :active_record_relation, ActiveRecord::Relation

  extend ActionPolicy::ScopeMatchers::ActionControllerParams

  scope_matcher :action_controller_params, ActionController::Parameters

  # Active Support notifications
  prepend ActionPolicy::Policy::Rails::Instrumentation

  # ActionPolicy::Policy::Defaults module adds the following

  authorize :user

  default_rule :manage?
  alias_rule :new?, to: :create?

  def index?
    false
  end

  def create?
    false
  end

  def manage?
    false
  end
end
```

You can write your `ApplicationPolicy` from scratch instead of inheriting from `ActionPolicy::Base`
if the defaults above do not fit your needs. The only required component is `ActionPolicy::Policy::Core`:

```ruby
# minimal ApplicationPolicy
class ApplicationPolicy
  include ActionPolicy::Policy::Core
end
```

The `Core` module provides `apply` and `allowed_to?` methods.

**NOTE:** When using with Rails, custom [scope matchers](./scoping.md) are also registered within the `Base` class.
If you plan to use them, you need to copy setup code from the [corresponding files](https://github.com/palkan/action_policy/tree/master/lib/action_policy/rails/scope_matchers).

---

---
url: /guide/custom_lookup_chain.md
---
# Custom Lookup Chain

Action Policy's lookup chain is just an array of *probes* (lambdas with a specific interface).

The lookup process itself is pretty simple:

* Call the first probe;
* Return the result if it is not `nil`;
* Go to the next probe.

You can override the default chain with your own. For example:

```ruby
ActionPolicy::LookupChain.chain = [
  # Probe accepts record as the first argument
  # and arbitrary options (passed to `authorize!` / `allowed_to?` call)
  lambda do |record, **options|
    # your custom lookup logic
  end
]
```

## NullPolicy example

Let's consider a simple example of extending the existing lookup chain with one more probe.

Suppose that we want to have a fallback policy (policy used when none found for the resource) instead of raising an `ActionPolicy::NotFound` error.

Let's call this policy a `NullPolicy`:

```ruby
class NullPolicy < ActionPolicy::Base
  default_rule :any?

  def any?
    false
  end
end
```

Here we use the [default rule](aliases.md#default-rule) to handle any rule applied.

Now we need to add a simple probe to the end of our lookup chain:

```ruby
ActionPolicy::LookupChain.chain << ->(_, _) { NullPolicy }
```

That's it!

---

---
url: /guide/decorators.md
---
# Dealing with Decorators

Ref: [action\_policy#7](https://github.com/palkan/action_policy/issues/7).

Since Action Policy [lookup mechanism](./lookup_chain.md) relies on the target
record's class properties (names, methods) it could break when using with *decorators*.

To make `authorize!` and other [behaviour](./behaviour.md) methods work seamlessly with decorated
objects, you might want to *enhance* the `policy_for` method.

For example, when using the [Draper](https://github.com/drapergem/draper) gem:

```ruby
module ActionPolicy
  module Draper
    def policy_for(record:, **opts)
      # From https://github.com/GoodMeasuresLLC/draper-cancancan/blob/master/lib/draper/cancancan.rb
      record = record.model while record.is_a?(::Draper::Decorator)
      super
    end
  end
end

class ApplicationController < ActionController::Base
  prepend ActionPolicy::Draper
end
```

---

---
url: /guide/debugging.md
---
# Debug Helpers

**NOTE:** this functionality requires two additional gems to be available in the app:

* [prism](https://github.com/ruby/prism) (comes with Ruby 3.3+)
* [method\_source](https://github.com/banister/method_source).

We usually describe policy rules using *boolean expressions* (e.g. `A or (B and C)` where each of `A`, `B` and `C` is a simple boolean expression or predicate method).

When dealing with complex policies, it could be hard to figure out which predicate/check made policy to fail.

The `Policy#pp(rule)` method aims to help debug such situations.

Consider a (synthetic) example:

```ruby
def feed?
  (admin? || allowed_to?(:access_feed?)) &&
    (user.name == "Jack" || user.name == "Kate")
end

```

Suppose that you want to debug this rule ("Why does it return false?").
You can drop a [`binding.pry`](https://github.com/deivid-rodriguez/pry-byebug) (or `binding.irb`) right at the beginning of the method:

```ruby
def feed?
  binding.pry # rubocop:disable Lint/Debugger
  #...
end
```

Now, run your code and trigger the breakpoint (i.e., run the method):

```
# now you can preview the execution of the rule using the `pp` method (defined on the policy instance)
pry> pp :feed?
MyPolicy#feed?
↳ (
    admin? #=> false
    OR
    allowed_to?(:access_feed?) #=> true
  )
  AND
  (
    user.name == "Jack" #=> false
    OR
    user.name == "Kate" #=> true
  )

# you can also check other rules or methods as well
pry> pp :admin?
MyPolicy#admin?
↳ user.admin? #=> false
```

## Compatibility with debugging tools

Since we partially evaluate code within a policy rule, pretty printing could get stuck if you have debugging breakpoints.
By default, we ignore `binding.pry` and `binding.irb` expressions (you will see a `<skipped>` comment in the output). If you're using an alternative debugging technique, you can add a regexp to ignore the corresponding expression:

```ruby
ActionPolicy::PrettyPrint.ignore_expressions << /\bdebug\b/
```

---

---
url: /guide/reasons.md
---
# Failure Reasons

When you have complex policy rules, it could be helpful to have an ability to define an exact reason for why a specific authorization was rejected.

It is especially helpful when you compose policies (i.e., use one policy within another) or want
to expose permissions to client applications (see [GraphQL](./graphql.md)).

Action Policy allows you to track failed `allowed_to?` checks in your rules.

Consider an example:

```ruby
class ApplicantPolicy < ApplicationPolicy
  def show?
    user.has_permission?(:view_applicants) &&
      allowed_to?(:show?, record.stage)
  end
end
```

When `ApplicantPolicy#show?` check fails, the exception has the `result` object, which in its turn contains additional information about the failure (`reasons`):

```ruby
class ApplicationController < ActionController::Base
  rescue_from ActionPolicy::Unauthorized do |ex|
    p ex.result.reasons.to_h #=> { stage: [:show?] }

    # or with i18n support
    p ex.result.reasons.full_messages #=> ["You do not have access to the stage"]
  end
end
```

The reason key is the corresponding policy [identifier](writing_policies.md#identifiers).

You can also wrap *local* rules into `allowed_to?` to populate reasons:

```ruby
class ApplicantPolicy < ApplicationPolicy
  def show?
    allowed_to?(:view_applicants?) &&
      allowed_to?(:show?, record.stage)
  end

  def view_applicants?
    user.has_permission?(:view_applicants)
  end
end

# then the reasons object could be
p ex.result.reasons.to_h #=> { applicant: [:view_applicants?] }

# or
p ex.result.reasons.to_h #=> { stage: [:show?] }
```

Reason could also be specified for `deny!` calls:

```ruby
class TeamPolicy < ApplicationPolicy
  def show?
    deny!(:no_user) if user.anonymous?

    user.has_permission?(:view_teams)
  end
end

p ex.result.reasons.to_h #=> { applicant: [:no_user] }
```

In some cases it might be useful to propagate reasons from the nested policy calls instead of adding a top-level reason.
You can achieve this by adding the `inline_reasons: true` option:

```ruby
class ApplicantPolicy < ApplicationPolicy
  def show?
    allowed_to?(:show?, record.stage, inline_reasons: true)
  end
end

class StagePolicy < ApplicationPolicy
  def show?
    deny!(:archived) if record.archived?
  end
end

# When applying ApplicationPolicy and the stage is archived
p ex.result.reasons.details #=> { stage: [:archived] }
# Without inline_reasons we would get { stage: [:show?] } instead
```

See also [#186](https://github.com/palkan/action_policy/issues/186) for discussion.

## Detailed Reasons

You can provide additional details to your failure reasons by using a `details: { ... }` option:

```ruby
class ApplicantPolicy < ApplicationPolicy
  def show?
    allowed_to?(:show?, record.stage)
  end
end

class StagePolicy < ApplicationPolicy
  def show?
    # Add stage title to the failure reason (if any)
    # (could be used by client to show more descriptive message)
    details[:title] = record.title

    # then perform the checks
    user.stages.where(id: record.id).exists?
  end
end

# when accessing the reasons
p ex.result.reasons.to_h #=> { stage: [{show?: {title: "Onboarding"}] }
```

**NOTE**: when using detailed reasons, the `details` array contains as the last element
a hash with ALL details reasons for the policy (in a form of `<rule> => <details>`).

The additional details are especially helpful when combined with localization, 'cause you can you them as interpolation data source for your translations. For example, for the above policy:

```yml
en:
  action_policy:
    policy:
      stage:
        show?: "The %{title} stage is not accessible"
```

And then when you call `full_messages`:

```ruby
p ex.result.reasons.full_messages #=> The Onboarding stage is not accessible
```

### `result.all_details`

Sometimes details could be useful not only to provide more context to the user, but to handle failures differently.

In this cases, digging through the `ex.result.reasons.details` could be cumbersome (see [this PR](https://github.com/palkan/action_policy/pull/130) for discussion). We provide a helper method, `result.all_details`, which could be used to get all details merged into a single Hash:

```ruby
class PostPolicy < ApplicationPolicy
  def edit?
    check?(:published?)
  end

  def published?
    details[:not_found] = true
    record.published?
  end
end
```

```ruby
p ex.result.all_details #=> {not_found: true}
```

```ruby
rescue_from ActionPolicy::Unauthorized do |ex|
  if ex.result.all_details[:not_found]
    head :not_found
  else
    head :unauthorized
  end
end
```

***

**P.S. What is the point of failure reasons?**

Failure reasons helps you to write *actionable* error messages, i.e. to provide a user with helpful feedback.

For example, in the above scenario, when the reason is `ApplicantPolicy#view_applicants?`, you could show the following message:

```
You don't have enough permissions to view applicants.
Please, ask your manager to update your role.
```

And when the reason is `StagePolicy#show?`:

```
You don't have access to the stage XYZ.
Please, ask your manager to grant access to this stage.
```

Much more useful than just showing "You are not authorized to perform this action," isn't it?

---

---
url: /guide/graphql.md
---
# GraphQL integration

You can use Action Policy as an authorization library for your [GraphQL Ruby](https://graphql-ruby.org/) application via the [`action_policy-graphql` gem](https://github.com/palkan/action_policy-graphql).

This integration provides the following features:

* Fields & mutations authorization
* List and connections scoping
* [**Exposing permissions/authorization rules in the API**](https://evilmartians.com/chronicles/exposing-permissions-in-graphql-apis-with-action-policy).

## Getting Started

First, add the `action_policy-graphql` gem to your Gemfile (see [installation instructions](https://github.com/palkan/action_policy-graphql#installation)).

Then, include `ActionPolicy::GraphQL::Behaviour` to your base type (or any other type/mutation where you want to use authorization features):

```ruby
# For fields authorization, lists scoping and rules exposing
class Types::BaseObject < GraphQL::Schema::Object
  include ActionPolicy::GraphQL::Behaviour
end

# For using authorization helpers in mutations
class Types::BaseMutation < GraphQL::Schema::Mutation
  include ActionPolicy::GraphQL::Behaviour
end

# For using authorization helpers in resolvers
class Types::BaseResolver < GraphQL::Schema::Resolver
  include ActionPolicy::GraphQL::Behaviour
end
```

## Authorization Context

By default, Action Policy uses `context[:current_user]` as the `user` [authorization context](./authorization_context.md).

**NOTE:** see below for more information on what's included into `ActionPolicy::GraphQL::Behaviour`.

## Authorizing Fields

You can add `authorize: true` option to any field (=underlying object) to protect the access (it's equal to calling `authorize! object, to: :show?`):

```ruby
# authorization could be useful for find-like methods,
# where the object is resolved from the provided params (e.g., ID)
field :home, Home, null: false, authorize: true do
  argument :id, ID, required: true
end

def home(id:)
  Home.find(id)
end

# Without `authorize: true` the code would look like this
def home(id:)
  Home.find(id).tap { |home| authorize! home, to: :show? }
end
```

You can use authorization options to customize the behaviour, e.g. `authorize: {to: :preview?, with: CustomPolicy}`.

By default, if a user is not authorized to access the field, an `ActionPolicy::Unauthorized` exception is raised.

If you want to return a `nil` instead, you should add `raise: false` to the options:

```ruby
# NOTE: don't forget to mark your field as nullable
field :home, Home, null: true, authorize: {raise: false}
```

You can make non-raising behaviour a default by setting a configuration option:

```ruby
ActionPolicy::GraphQL.authorize_raise_exception = false
```

You can also change the default `show?` rule globally:

```ruby
ActionPolicy::GraphQL.default_authorize_rule = :show_graphql_field?
```

If you want to perform authorization before resolving the field value, you can use `preauthorize: *` option:

```ruby
field :homes, [Home], null: false, preauthorize: {with: HomePolicy}

def homes
  Home.all
end
```

The code above is equal to:

```ruby
field :homes, [Home], null: false

def homes
  authorize! "homes", to: :index?, with: HomePolicy
  Home.all
end
```

You can specify the default *raising* behaviour for `preauthorize:` by setting a configuration option:

```ruby
# By default, it fallbacks to .authorize_raise_exception
ActionPolicy::GraphQL.preauthorize_raise_exception = false
```

**NOTE:** we pass the field's name as the `record` to the policy rule. We assume that pre-authorization rules do not depend on
the record itself and pass the field's name for debugging purposes only.

You can customize the authorization options, e.g. `authorize: {to: :preview?, with: CustomPolicy}`.

**NOTE:** unlike `authorize: *` you MUST specify the `with: SomePolicy` option.
The default authorization rule depends on the type of the field:

* for lists we use `index?` (configured by `ActionPolicy::GraphQL.default_preauthorize_list_rule` parameter)
* for *singleton* fields we use `show?` (configured by `ActionPolicy::GraphQL.default_preauthorize_node_rule` parameter)

### Class-level authorization

You can use Action Policy in the class-level [authorization hooks](https://graphql-ruby.org/authorization/authorization.html) (`self.authorized?`) like this:

```ruby
class Types::Friendship < Types::BaseObject
  def self.authorized?(object, context)
    super &&
      allowed_to?(
        :show?,
        object,
        # NOTE: you must provide context explicitly
        context: {user: context[:current_user]}
      )
  end
end
```

## Authorizing Mutations

A mutation is just a Ruby class with a single API method. There is nothing specific in authorizing mutations: from the Action Policy point of view, they are just [*behaviours*](./behaviour.md).

If you want to authorize the mutation, you call `authorize!` method. For example:

```ruby
class Mutations::DestroyUser < Types::BaseMutation
  argument :id, ID, required: true

  def resolve(id:)
    user = User.find(id)

    # Raise an exception if the user has not enough permissions
    authorize! user, to: :destroy?
    # Or check without raising and do what you want
    #
    #     if allowed_to?(:destroy?, user)

    user.destroy!

    {deleted_id: user.id}
  end
end
```

Check out this issue on how you can implement a `verify_authorized` callback for your mutations: [#28](https://github.com/palkan/action_policy-graphql/issues/28).

### Using `preauthorize: *` with mutations

Since mutation is also a GraphQL field, we can also use our custom `authorize: *` and `preauthorize: *` options. However, using `authorize: *` for mutations is deprecated and will raise an error in the future versions: it doesn't make any sense because it's called after the field has been resolved (i.e., mutation has been executed).

It is possible to override the default *raising* behaviour for mutation only via the following configuration option:

```ruby
# By default, it fallbacks to .preauthorize_raise_exception
ActionPolicy::GraphQL.preauthorize_mutation_raise_exception = true
```

## Handling exceptions

The query would fail with `ActionPolicy::Unauthorized` exception when using `authorize: true` (in raising mode) or calling `authorize!` explicitly.

That could be useful to handle this exception and send a more detailed error message to the client, for example:

Please make sure you have added error handling to your schema with `use GraphQL::Execution::Errors`.

```ruby
# in your schema file
rescue_from(ActionPolicy::Unauthorized) do |exp|
  raise GraphQL::ExecutionError.new(
    # use result.message (backed by i18n) as an error message
    exp.result.message,
    # use GraphQL error extensions to provide more context
    extensions: {
      code: :unauthorized,
      fullMessages: exp.result.reasons.full_messages,
      details: exp.result.reasons.details
    }
  )
end
```

## Scoping Data

You can add `authorized_scope: true` option to a field (list or [*connection*](https://graphql-ruby.org/pagination/connection_concepts.html)) to apply the corresponding policy rules to the data:

```ruby
class CityType < ::Common::Graphql::Type
  # It would automatically apply the relation scope from the EventPolicy to
  # the relation (city.events)
  field :events, EventType.connection_type,
    null: false,
    authorized_scope: true

  # you can specify the policy explicitly
  field :events, EventType.connection_type,
    null: false,
    authorized_scope: {with: CustomEventPolicy}

  # without the option you would write the following code
  def events
    authorized_scope object.events
    # or if `with` option specified
    authorized_scope object.events, with: CustomEventPolicy
  end
end
```

**NOTE:** you cannot use `authorize: *` and `authorized_scope: *` at the same time but you can combine `preauthorize: *` with `authorized_scope: *`.

See the documentation on [scoping](./scoping.md).

## Exposing Authorization Rules

With `action_policy-graphql` gem, you can easily expose your authorization logic to the client in a standardized way.

For example, if you want to "tell" the client which actions could be performed against the object you
can use the `expose_authorization_rules` macro to add authorization-related fields to your type:

```ruby
class ProfileType < Types::BaseType
  # Adds can_edit, can_destroy fields with
  # AuthorizationResult type.

  # NOTE: prefix "can_" is used by default, no need to specify it explicitly
  expose_authorization_rules :edit?, :destroy?, prefix: "can_"
end
```

**NOTE:** you can use [aliases](./aliases.md) here as well as defined rules.

**NOTE:** This feature relies the [*failure reasons*](./reasons.md) and
the [i18n integration](./i18n.md) extensions. If your policies don't include any of these,
you won't be able to use it.

Then the client could perform the following query:

```gql
{
  post(id: $id) {
    canEdit {
      # (bool) true|false; not null
      value
      # top-level decline message ("Not authorized" by default); null if value is true
      message
      # detailed information about the decline reasons; null if value is true or you don't have "failure reasons" extension enabled
      reasons {
        details # JSON-encoded hash of the form { "event" => [:privacy_off?] }
        fullMessages # Array of human-readable reasons
      }
    }

    canDestroy {
      # ...
    }
  }
}
```

You can override a custom authorization field prefix (`can_`):

```ruby
ActionPolicy::GraphQL.default_authorization_field_prefix = "allowed_to_"
```

You can specify a custom field name as well (only for a single rule):

```ruby
class ProfileType < ::Common::Graphql::Type
  # Adds can_create_post field.

  expose_authorization_rules :create?, with: PostPolicy, field_name: "can_create_post"
end
```

## Custom Behaviour

Including the default `ActionPolicy::GraphQL::Behaviour` is equal to adding the following to your base class:

```ruby
class Types::BaseObject < GraphQL::Schema::Object
  # include Action Policy behaviour and its extensions
  include ActionPolicy::Behaviour
  include ActionPolicy::Behaviours::ThreadMemoized
  include ActionPolicy::Behaviours::Memoized
  include ActionPolicy::Behaviours::Namespaced

  # define authorization context
  authorize :user, through: :current_user

  # add a method helper to get the current_user from the context
  def current_user
    context[:current_user]
  end

  # extend the field class to add `authorize` and `authorized_scope` options
  field_class.prepend(ActionPolicy::GraphQL::AuthorizedField)

  # add `expose_authorization_rules` macro
  include ActionPolicy::GraphQL::Fields
end
```

Feel free to create your own behaviour by adding only the functionality you need.

---

---
url: /guide/i18n.md
---
# I18n Support

`ActionPolicy` integrates with [`i18n`][] to support localizable `full_messages` for [reasons](./reasons.md) and the execution result's `message`:

```ruby
class ApplicationController < ActionController::Base
  rescue_from ActionPolicy::Unauthorized do |ex|
    p ex.result.message #=> "You do not have access to the stage"
    p ex.result.reasons.full_messages #=> ["You do not have access to the stage"]
  end
end
```

The message contains a string for the *rule* that was called, while `full_messages` contains the list of reasons, why `ActionPolicy::Unauthorized` has been raised. You can find more information about tracking failure reasons [here](./reasons.md).

## Configuration

`ActionPolicy` doesn't provide any localization out-of-the-box and uses "You are not authorized to perform this action" as the default message.

You can add your app-level default fallback by providing the `action_policy.unauthorized` key value.

When using **Rails**, all you need is to add translations to any file under the `config/locales` folder (or create a new file, e.g. `config/locales/policies.yml`).

Non-Rails projects should configure [`i18n`][] gem manually:

```ruby
I18n.load_path << Dir[File.expand_path("config/locales") + "/*.yml"]
```

## Translations lookup

`ActionPolicy` uses the `action_policy` scope. Specific policies translations must be stored inside the `policy` sub-scope.

The following algorithm is used to find out the translation for a policy with a class `klass` and rule `rule`:

1. Translation for `"#{klass.identifier}.#{rule}"` key, when `self.identifier =` is not specified then underscored class name without the *Policy* suffix would be used (e.g. `GuestUserPolicy` turns into `guest_user:` scope)
2. Repeat step 1 for each ancestor which looks like a policy (`.respond_to?(:identifier)?`) up to `ActionPolicy::Base`
3. Use `#{rule}` key
4. Use `en.action_policy.unauthorized` key
5. Use a default message provided by the gem

For example, given a `GuestUserPolicy` class which is inherited from `DefaultUserPolicy` and a rule `feed?`, the following list of possible translation keys would be used: `[:"action_policy.policy.guest_user.feed?", :"action_policy.policy.default_user.feed?", :"action_policy.policy.feed?", :"action_policy.unauthorized"]`

[`i18n`]: https://github.com/svenfuchs/i18n

---

---
url: /guide/instrumentation.md
---
# Instrumentation

Action Policy integrates with [Rails instrumentation system](https://guides.rubyonrails.org/active_support_instrumentation.html), `ActiveSupport::Notifications`.

## Events

### `action_policy.apply_rule`

This event is triggered every time a policy rule is applied:

* when `authorize!` is called
* when `allowed_to?` is called within the policy or the [behaviour](behaviour.md)
* when `apply_rule` is called explicitly (i.e. `SomePolicy.new(record, context).apply_rule(record)`).

The event contains the following information:

* `:policy` – policy class name
* `:rule` – applied rule (String)
* `:result` — the authorization result object.

The following fields are deprecated and will be removed in v1.0:

* `:value` – the result of the rule application (true of false) (use `event[:result].value` instead)
* `:cached` – whether we hit the [cache](caching.md)\* (use `even[:result].cached?` instead).

\* This parameter tracks only the cache store usage, not memoization.

You can use this event to track your policy cache usage and also detect *slow* checks.

Here is an example code for sending policy stats to [Librato](https://librato.com/)
using [`librato-rack`](https://github.com/librato/librato-rack):

```ruby
ActiveSupport::Notifications.subscribe("action_policy.apply_rule") do |event, started, finished, _, data|
  # Track hit and miss events separately (to display two measurements)
  measurement = "#{event}.#{data[:cached] ? "hit" : "miss"}"
  # show ms times
  timing = ((finished - started) * 1000).to_i
  Librato.tracker.check_worker
  Librato.timing measurement, timing, percentile: [95, 99]
end
```

### `action_policy.authorize`

This event is identical to `action_policy.apply_rule` with the one difference:
**it's only triggered when `authorize!` method is called**.

The motivation behind having a separate event for this method is to monitor the number of failed
authorizations: the high number of failed authorizations usually means that we do not take
into account authorization rules in the application UI (e.g., we show a "Delete" button to the user not
permitted to do that).

The `action_policy.apply_rule` might have a large number of failures, 'cause it also tracks the usage of non-raising applications (i.e. `allowed_to?`).

### `action_policy.init`

This event is triggered every time a new policy object is initialized.

The event contains the following information:

* `:policy` – policy class name.

This event is useful if you want to track the number of initialized policies per *action* (for example, when you want to ensure that
the [memoization](caching.md) works as expected).

## Turn off instrumentation

Instrumentation is enabled by default. To turn it off add to your configuration:

```ruby
config.action_policy.instrumentation_enabled = false
```

**NOTE:** changing this setting after the application has been initialized doesn't take any effect.

## Non-Rails usage

If you don't use Rails itself but have `ActiveSupport::Notifications` available in your application,
you can use the instrumentation feature with some additional configuration:

```ruby
# Enable `apply_rule` event by extending the base policy class
require "action_policy/rails/policy/instrumentation"
ActionPolicy::Base.include ActionPolicy::Policy::Rails::Instrumentation

# Enabled `authorize` event by extending the authorizer class
require "action_policy/rails/authorizer"
ActionPolicy::Authorizer.singleton_class.prepend ActionPolicy::Rails::Authorizer
```

---

---
url: /guide/pundit_migration.md
---
# Migrate from Pundit to Action Policy

Migration from Pundit to Action Policy could be done in a progressive way: first, we make Pundit polices and authorization helpers use Action Policy under the hood, then you can rewrite policies in the Action Policy way.

## Phase 1. Quacking like a Pundit

### Step 1. Prepare controllers

* Remove `include Pundit` from ApplicationController

* Add `authorize` method:

```ruby
def authorize(record, rule = nil)
  options = {}
  options[:to] = rule unless rule.nil?

  authorize! record, **options
end
```

* Configure [authorization context](authorization_context.md) if necessary, e.g. add `authorize :current_user, as: :user` to `ApplicationController` (**NOTE:** added automatically in Rails apps)

* Add `policy` and `policy_scope` helpers:

```ruby
helper_method :policy
helper_method :policy_scope

def policy(record)
  policy_for(record: record)
end

def policy_scope(scope)
  authorized scope
end

```

**NOTE**: `policy` defined above is not equal to `allowed_to?` since it doesn't take into account pre-checks.

### Step 2. Prepare policies

We assume that you have a base class for all your policies, e.g. `ApplicationPolicy`.

Then do the following:

* Add `include ActionPolicy::Policy::Core` to `ApplicationPolicy`

* Update `ApplicationPolicy#initialize`:

```ruby
def initialize(target, user:)
  # ...
end
```

* [Rewrite scopes](scoping.md).

Unfortunately, there is no easy way to migrate Pundit class-based scope to Action Policies scopes.

### Step 3. Replace RSpec helper

We provide a Pundit-compatible syntax for RSpec tests:

```
# Remove DSL
# require "pundit/rspec"
#
# Add Action Policy Pundit DSL
require "action_policy/rspec/pundit_syntax"
```

## Phase 2. No more Pundit

When everything is green, it's time to fully migrate to ActionPolicy:

* make ApplicationPolicy inherit from `ActionPolicy::Base`
* migrate view helpers (from `policy(..)` to `allowed_to?`, from `policy_scope` to `authorized`)
* re-write specs using simple non-DSL syntax (or [Action Policy RSpec syntax](testing.md#rspec-dsl))
* add [authorization tests](testing.md#testing-authorization) (add `require 'action_policy/rspec'`)
* use [Reasons](reasons.md), [I18n integration](i18n.md), [cache](caching.md) and other Action Policy features!

---

---
url: /guide/namespaces.md
---
# Namespaces

Action Policy can lookup policies with respect to the current execution *namespace* (i.e., authorization class module).

Consider an example:

```ruby
module Admin
  class UsersController < ApplicationController
    def index
      # uses Admin::UserPolicy if any, otherwise fallbacks to UserPolicy
      authorize!
    end
  end
end
```

Module nesting is also supported:

```ruby
module Admin
  module Client
    class UsersController < ApplicationController
      def index
        # lookup for Admin::Client::UserPolicy -> Admin::UserPolicy -> UserPolicy
        authorize!
      end
    end
  end
end
```

**NOTE**: to support namespaced lookup for non-inferrable resources,
you should specify `policy_name` at a class level (instead of `policy_class`, which doesn't take namespaces into account):

```ruby
class Guest < User
  def self.policy_name
    "UserPolicy"
  end
end
```

**NOTE**: by default, we use class's name as a policy name; so, for namespaced resources, the namespace part is also included:

```ruby
class Admin
  class User
  end
end

# search for Admin::UserPolicy, but not for UserPolicy
authorize! Admin::User.new
```

You can access the current authorization namespace through `authorization_namespace` method.

You can also define your own namespacing logic by overriding `authorization_namespace`:

```ruby
def authorization_namespace
  return ::Admin if current_user.admin?
  return ::Staff if current_user.staff?
  # fallback to current namespace
  super
end
```

**NOTE**: namespace support is an extension for `ActionPolicy::Behaviour` and could be included with `ActionPolicy::Behaviours::Namespaced` (included into Rails controllers and channel integrations by default).

## Namespace resolution cache

We cache namespaced policy resolution for better performance (it could affect performance when we look up a policy from a deeply nested module context, see the [benchmark](https://github.com/palkan/action_policy/blob/master/benchmarks/namespaced_lookup_cache.rb)).

It could be disabled by setting `ActionPolicy::LookupChain.namespace_cache_enabled = false`. It's enabled by default unless `RACK_ENV` env var is specified and is not equal to `"production"` (e.g. when `RACK_ENV=test` the cache is disabled).

When using Rails it's enabled only in production mode but could be configured through setting the `config.action_policy.namespace_cache_enabled` parameter.

---

---
url: /guide/lookup_chain.md
---
# Policy Lookup

Action Policy tries to automatically infer policy class from the target using the following *probes*:

1. If the target is a `Symbol`:

   a) Try `"#{target.to_s.camelize}Policy"` as a `policy_name` (see below);

   b) If `String#classify` is available, e.g. when using Rails' ActiveSupport, try `"#{target.to_s.classify}Policy"`;

2. If the target responds to `policy_class`, then use it;

3. If the target's class responds to `policy_class`, then use it;

4. If the target or the target's class responds to `policy_name`, then use it (the `policy_name` should end with `Policy` as it's not appended automatically);

5. Otherwise, use `#{target.class.name}Policy` or `#{target.name}Policy` if target is a Module or Class.

> \* [Namespaces](namespaces.md) could be also be considered when `namespace` option is set. You can also set the `strict_namespace` option to disable constant cascade lookup.

You can call `ActionPolicy.lookup(record, options)` to infer policy class for the record.

When no policy class is found, an `ActionPolicy::NotFound` error is raised.

You can [customize lookup](custom_lookup_chain.md) logic if necessary.

---

---
url: /guide/pre_checks.md
---
# Pre-Checks

Consider a typical situation when you start most—or even all—of your rules with the same predicates.

For example, when you have a super-user role in the application:

```ruby
class PostPolicy < ApplicationPolicy
  def show?
    user.super_admin? || record.published
  end

  def update?
    user.super_admin? || (user.id == record.user_id)
  end

  # more rules
end
```

Action Policy allows you to extract the common parts from rules into *pre-checks*:

```ruby
class PostPolicy < ApplicationPolicy
  pre_check :allow_admins

  def show?
    record.published
  end

  def update?
    user.id == record.user_id
  end

  private

  def allow_admins
    allow! if user.super_admin?
  end
end
```

Pre-checks act like *callbacks*: you can add multiple pre-checks, specify `except` and `only` options, and skip already defined pre-checks if necessary:

```ruby
class UserPolicy < ApplicationPolicy
  skip_pre_check :allow_admins, only: :destroy?

  def destroy?
    user.admin? && !record.admin?
  end
end
```

To halt the authorization process within a pre-check, you must return either `allow!` or `deny!` call value. When any other value is returned, the pre-check is ignored, and the rule is called (or next pre-check).

**NOTE**: pre-checks are available only if you inherit from `ActionPolicy::Base` or include `ActionPolicy::Policy::PreCheck` into your `ApplicationPolicy`.

---

---
url: /guide/quick_start.md
---
# Quick Start

## Installation

Install Action Policy with RubyGems:

```ruby
gem install action_policy
```

Or add `action_policy` to your application's `Gemfile`:

```ruby
gem "action_policy"
```

And then execute:

```sh
bundle install
```

## Basic usage

The core component of Action Policy is a *policy class*. Policy class describes how you control access to resources.

We suggest having a separate policy class for each resource and encourage you to follow these conventions:

* put policies into the `app/policies` folder (when using with Rails);
* name policies using the corresponding singular resource name (model name) with a `Policy` suffix, e.g. `Post -> PostPolicy`;
* name rules using a predicate form of the corresponding activity (typically, a controller's action), e.g. `PostsController#update -> PostPolicy#update?`.

We also recommend to use an application-specific `ApplicationPolicy` with a global configuration to inherit from:

```ruby
class ApplicationPolicy < ActionPolicy::Base
end
```

You could use the following command to generate it when using Rails:

```sh
rails generate action_policy:install
```

**NOTE:** it is not necessary to inherit from `ActionPolicy::Base`; instead, you can [construct basic policy](custom_policy.md) choosing only the components you need.

Rules must be public methods on the class. Using private methods as rules will raise an error.

Consider a simple example:

```ruby
class PostPolicy < ApplicationPolicy
  # everyone can see any post
  def show?
    true
  end

  def update?
    # `user` is a performing subject,
    # `record` is a target object (post we want to update)
    user.admin? || (user.id == record.user_id)
  end
end
```

Now you can easily add authorization to your Rails\* controller:

```ruby
class PostsController < ApplicationController
  def update
    @post = Post.find(params[:id])
    authorize! @post

    if @post.update(post_params)
      redirect_to @post
    else
      render :edit
    end
  end
end
```

\* See [Non-Rails Usage](non_rails.md) on how to add `authorize!` to any Ruby project.

In the above case, Action Policy automatically infers a policy class and a rule to verify access: `@post -> Post -> PostPolicy`, rule is inferred from the action name (`update -> update?`), and `current_user` is used as `user` within the policy by default (read more about [authorization context](authorization_context.md)).

When authorization is successful (i.e., the corresponding rule returns `true`), nothing happens, but in case of an authorization failure `ActionPolicy::Unauthorized` error is raised:

```ruby
rescue_from ActionPolicy::Unauthorized do |ex|
  # Exception object contains the following information
  ex.policy #=> policy class, e.g. UserPolicy
  ex.rule #=> applied rule, e.g. :show?
end
```

There is also an `allowed_to?` method which returns `true` or `false` and could be used, for example, in views:

```erb
<% @posts.each do |post| %>
  <li><%= post.title %>
    <% if allowed_to?(:edit?, post) %>
      <%= link_to "Edit", post %>
    <% end %>
  </li>
<% end %>
```

Although Action Policy tries to [infer the corresponding policy class](lookup_chain.md) and rule itself, there could be a situation when you want to specify those values explicitly:

```ruby
# specify the rule to verify access
authorize! @post, to: :update?

# specify policy class
authorize! @post, with: CustomPostPolicy

# or
allowed_to? :edit?, @post, with: CustomPostPolicy
```

---

---
url: /guide/aliases.md
---
# Rule Aliases

Action Policy allows you to add rule aliases. It is useful when you rely on *implicit* rules in controllers. For example:

```ruby
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:

```ruby
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](caching.md) 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):

```ruby
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](custom_policy.md) (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.

3. If there is a concrete rule method on the superclass, then this is called, else
4. If there is a default rule defined, then this is called, else
5. If the default rule is unaltered, then the `manage?` rule is called
6. If the default rule is set to `nil`, `ActionPolicy::UnknownRule` is raised.

Here's an example with the expected results:

```ruby
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`

---

---
url: /guide/scoping.md
---
# Scoping

By *scoping* we mean an ability to use policies to *scope data* (or *filter/modify/transform/choose-your-verb*).

The most common situation is when you want to *scope* ActiveRecord relations depending
on the current user permissions. Without policies it could look like this:

```ruby
class PostsController < ApplicationController
  def index
    @posts =
      if current_user.admin?
        Post.all
      else
        Post.where(user: current_user)
      end
  end
end
```

That's a very simplified example. In practice scoping rules might be more complex, and it's likely that we would use them in multiple places.

Action Policy allows you to define scoping rules within a policy class and use them with the help of `authorized_scope` method (`authorized` alias is also available):

```ruby
class PostsController < ApplicationController
  def index
    @posts = authorized_scope(Post.all)
  end
end

class PostPolicy < ApplicationPolicy
  relation_scope do |relation|
    next relation if user.admin?
    relation.where(user: user)
  end
end
```

You can also use a callable object to encapsulate the scoping logic:

```ruby
class PostsController < ApplicationController
  def index
    @posts = authorized_scope(Post.all)
  end
end

class PostPolicy < ApplicationPolicy
  relation_scope AuthorizedPosts
end

class AuthorizedPosts
  class << self
    def call(policy, relation, **_scope_options)
      user = policy.user
      user.admin? ? relation : relation.where(user: user)
    end
  end
end
```

## Define scopes

To define scope you should use either `scope_for` or `smth_scope` methods in your policy:

```ruby
class PostPolicy < ApplicationPolicy
  # define a scope of a `relation` type
  scope_for :relation do |relation|
    relation.where(user: user)
  end

  # define a scope of `data` type,
  # which acts on hashes
  scope_for :data do |data|
    next data if user.admin?
    data.delete_if { |k, _| SENSITIVE_KEYS.include?(k) }
  end
end
```

Scopes have *types*: different types of scopes are meant to be applied to different data types.

You can specify multiple scopes (*named scopes*) for the same type providing a scope name:

```ruby
class EventPolicy < ApplictionPolicy
  scope_for :relation, :own do |relation|
    relation.where(owner: user)
  end
end
```

When the second argument is not specified, the `:default` is implied as the scope name.

Also, there are cases where it might be easier to add options to existing scope than create a new one.

For example, if you use soft-deletion and your logic inside a scope depends on if deleted records are included, you can add `with_deleted` option:

```ruby
class PostPolicy < ApplicationPolicy
  scope_for :relation do |relation, with_deleted: false|
    rel = some_logic(relation)
    with_deleted ? rel.with_deleted : rel
  end
end
```

You can add as many options as you want:

```ruby
class PostPolicy < ApplicationPolicy
  scope_for :relation do |relation, some_required_option:, with_deleted: false, magic_number: 42|
    # Your code
  end
end
```

### Scopes inheritance and chaining

Even though we use blocks to declare scopes, under the hood they are transformed into class instance methods. Hence, inheritance works out-of-the-box as with pure Ruby classes.

It is also possible to *chain* scopes by using the `super` keyword. For example:

```ruby
class ApplicationPolicy < ActionPolicy::Base
  authorize :user, :account

  scope_for :relation do |relation|
    relation.where(account_id: account.id)
  end
end

class PostPolicy < ApplicationPolicy
  scope_for :relation do |relation|
    super(relation).published
  end
end
```

**NOTE:** Using `super` with implicit arguments is not supported, since we use `define_method` internally.

## Apply scopes

Action Policy behaviour (`ActionPolicy::Behaviour`) provides an `authorized` method which allows you to use scoping:

```ruby
class PostsController < ApplicationController
  def index
    # The first argument is the target,
    # which is passed to the scope block
    #
    # The second argument is the scope type
    @posts = authorized_scope(Post, type: :relation)
    #
    # For named scopes provide `as` option
    @events = authorized_scope(Event, type: :relation, as: :own)
    #
    # If you want to specify scope options provide `scope_options` option
    @events = authorized_scope(Event, type: :relation, scope_options: {with_deleted: true})
  end
end
```

You can also specify additional options for policy class inference (see [behaviour docs](behaviour.md)). For example, to explicitly specify the policy class use:

```ruby
@posts = authorized_scope(Post, with: CustomPostPolicy)
```

## Using scopes within policy

You can also use scopes within policy classes using the same `authorized_scope` method.
For example:

```ruby
relation_scope(:edit) do |scope|
  teachers = authorized_scope(Teacher.all, as: :edit)
  scope
    .joins(:teachers)
    .where(teacher_id: teachers)
end
```

## Using scopes explicitly

To use scopes without including Action Policy [behaviour](behaviour.md)
do the following:

```ruby
# initialize policy
policy = ApplicantPolicy.new(user: user)
# apply scope
policy.apply_scope(User.all, type: :relation)
```

## Scope type inference

Action Policy could look up a scope type if it's not specified and if *scope matchers* were configured.

Scope matcher is an object that implements `#===` (*case equality*) or a Proc. You can define it within a policy class:

```ruby
class ApplicationPolicy < ActionPolicy::Base
  scope_matcher :relation, ActiveRecord::Relation

  # use Proc to handle AR models classes
  scope_matcher :relation, ->(target) { target < ActiveRecord::Base }

  scope_matcher :custom, MyCustomClass
end
```

Adding a scope matcher also adds a DSL to define scope rules (just a syntax sugar):

```ruby
class ApplicationPolicy < ActionPolicy::Base
  scope_matcher :relation, ActiveRecord::Relation

  # now you can define scope rules like this
  relation_scope { |relation| relation }
end
```

When `authorized_scope` is called without the explicit scope type, Action Policy uses matchers (in the order they're defined) to infer the type.

## Rails integration

Action Policy provides a couple of *scope matchers* out-of-the-box for Active Record relations and Action Controller parameters.

### Active Record scopes

Scope type `:relation` is automatically applied to the object of `ActiveRecord::Relation` type.

To define Active Record scopes you can use `relation_scope` macro (which is just an alias for `scope :relation`) in your policy:

```ruby
class PostPolicy < ApplicationPolicy
  # Equals `scope_for :active_record_relation do ...`
  relation_scope do |scope|
    if super_user? || admin?
      scope
    else
      scope.joins(:accesses).where(accesses: {user_id: user.id})
    end
  end

  # define named scope
  relation_scope(:own) do |scope|
    next scope.none if user.guest?
    scope.where(user: user)
  end
end
```

**NOTE:** the `:active_record_relation` scoping is used if and only if an `ActiveRecord::Relation` is passed to `authorized`:

```ruby
def index
  # BAD: Post is not a relation; raises an exception
  @posts = authorized_scope(Post)

  # GOOD:
  @posts = authorized_scope(Post.all)
end
```

### Action Controller parameters

Use scopes of type `:params` if your strong parameters filtering depends on the current user:

```ruby
class UserPolicy < ApplicationPolicy
  # Equals to `scope_for :action_controller_params do ...`
  params_filter do |params|
    if user.admin?
      params.permit(:name, :email, :role)
    else
      params.permit(:name)
    end
  end

  params_filter(:update) do |params|
    params.permit(:name)
  end
end

class UsersController < ApplicationController
  def create
    # Call `authorized_scope` on `params` object
    @user = User.create!(authorized_scope(params.require(:user)))
    # Or you can use `authorized` alias which fits this case better
    @user = User.create!(authorized(params.require(:user)))
    head :ok
  end

  def update
    @user.update!(authorized_scope(params.require(:user), as: :update))
    head :ok
  end
end
```

---

---
url: /guide/testing.md
---
# Testing

Authorization is one of the crucial parts of your application. Hence, it should be thoroughly tested (that is the place where 100% coverage makes sense).

When you use policies for authorization, it is possible to split testing into the following parts:

* Test the policy class itself
* Test that **the required authorization is performed** within your authorization layer (controller, channel, etc.)
* Test that **the required scoping has been applied**.

## Testing policies

You can test policies as plain-old Ruby classes, no special tooling is required.

Consider an RSpec example:

```ruby
describe PostPolicy do
  let(:user) { build_stubbed(:user) }
  let(:post) { build_stubbed(:post) }

  let(:policy) { described_class.new(post, user: user) }

  describe "#update?" do
    subject { policy.apply(:update?) }

    it "returns false when the user is not admin nor author" do
      is_expected.to eq false
    end

    context "when the user is admin" do
      let(:user) { build_stubbed(:user, :admin) }

      it { is_expected.to eq true }
    end

    context "when the user is an author" do
      let(:post) { build_stubbed(:post, user: user) }

      it { is_expected.to eq true }
    end
  end
end
```

And we provide a `#be_an_alias_of` RSpec matcher for testing authorization rule aliases:

Add the following to your `rails_helper.rb` (or `spec_helper.rb`):

```ruby
require "action_policy/rspec"
```

Now you can use `be_an_alias_of` matcher:

```ruby
describe PostPolicy do
  let(:user) { build_stubbed(:user) }
  let(:post) { build_stubbed(:post) }

  let(:policy) { described_class.new(post, user: user) }

  describe "#show?" do
    it "is an alias of :index? authorization rule" do
      expect(:show?).to be_an_alias_of(policy, :index?)
    end
  end
end
```

### RSpec DSL

We also provide a simple RSpec DSL which aims to reduce the boilerplate when writing
policies specs.

Example:

```ruby
# Add this to your spec_helper.rb / rails_helper.rb
require "action_policy/rspec/dsl"

describe PostPolicy do
  let(:user) { build_stubbed :user }
  # `record` must be defined – it is the authorization target
  let(:record) { build_stubbed :post, draft: false }

  # `context` is the authorization context
  let(:context) { {user: user} }

  # `describe_rule` is a combination of
  # `describe` and `subject { ... }` (returns the result of
  # applying the rule to the record)
  describe_rule :show? do
    # `succeed` is `context` + `specify`, which checks
    # that the result of application is successful
    succeed "when post is published"

    # `failed` is `context` + `specify`, which checks
    # that the result of application wasn't successful
    failed "when post is draft" do
      before { post.draft = false }

      succeed "when user is a manager" do
        before { user.role = "manager" }
      end
    end
  end
end
```

If test failed the exception message includes the result and [failure reasons](reasons.md) (if any):

```sh
1) PostPolicy#show? when post is draft
Failure/Error:  ...

Expected to fail but succeed:
<PostPolicy#show?: true (reasons: ...)>
```

If you have [debugging utils](debugging.md) installed the message also includes the *annotated*
source code of the policy rule:

```sh
1) UserPolicy#manage? when post is draft
Failure/Error:  ...

Expected to fail but succeed:
<PostPolicy#show?: true (reasons: ...)>
↳ user.admin? #=> true
OR
!record.draft? #=> false
```

**NOTE:** DSL for focusing or skipping examples and groups is also available (e.g. `xdescribe_rule`, `fsucceed`, etc.).

**NOTE:** the DSL is included only to example with the tag `type: :policy` or in the `spec/policies` folder. If you want to add this DSL to other examples, add `include ActionPolicy::RSpec::PolicyExampleGroup`.

### Testing scopes

#### Active Record relation example

There is no single rule on how to test scopes, 'cause it depends on the *nature* of the scope.

Here's an example of RSpec tests for Active Record scoping rules:

```ruby
describe PostPolicy do
  describe "relation scope" do
    let(:user) { build_stubbed :user }
    let(:context) { {user: user} }

    # Feel free to replace with `before_all` from `test-prof`:
    # https://test-prof.evilmartians.io/#/before_all
    before do
      create(:post, name: "A")
      create(:post, name: "B", draft: true)
    end

    let(:target) do
      # We want to make sure that only the records created
      # for this test are affected, and they have a deterministic order
      Post.where(name: %w[A B]).order(name: :asc)
    end

    subject { policy.apply_scope(target, type: :active_record_relation).pluck(:name) }

    context "as user" do
      it { is_expected.to eq(%w[A]) }
    end

    context "as manager" do
      before { user.role = :manager }

      it { is_expected.to eq(%w[A B]) }
    end

    context "as banned user" do
      before { user.banned = true }

      it { is_expected.to be_empty }
    end
  end
end
```

#### Action Controller params example

Here's an example of RSpec tests for Action Controller parameters scoping rules:

```ruby
describe PostPolicy do
  describe "params scope" do
    let(:user) { build_stubbed :user }
    let(:context) { {user: user} }

    let(:params) { {name: "a", password: "b"} }
    let(:target) { ActionController::Parameters.new(params) }

    # it's easier to asses the hash representation, not the AC::Params object
    subject { policy.apply_scope(target, type: :action_controller_params).to_h }

    context "as user" do
      it { is_expected.to eq({name: "a"}) }
    end

    context "as manager" do
      before { user.update!(role: :manager) }

      it { is_expected.to eq({name: "a", password: "b"}) }
    end
  end
end
```

## Testing authorization

To test the act of authorization you have to make sure that the `authorize!` method is called with the appropriate arguments.

Action Policy provides tools for such kind of testing for Minitest and RSpec.

### Minitest

Include `ActionPolicy::TestHelper` to your test class and you'll be able to use
`assert_authorized_to` assertion:

```ruby
# in your controller
class PostsController < ApplicationController
  def update
    @post = Post.find(params[:id])
    authorize! @post, context: {favorite: true}
    if @post.update(post_params)
      redirect_to @post
    else
      render :edit
    end
  end
end

# in your test
require "action_policy/test_helper"

class PostsControllerTest < ActionDispatch::IntegrationTest
  include ActionPolicy::TestHelper

  test "update is authorized" do
    sign_in users(:john)

    post = posts(:example)

    assert_authorized_to(:update?, post, with: PostPolicy, context: {favorite: true}) do
      patch :update, id: post.id, name: "Bob"
    end
  end
end
```

You can omit the policy (then it would be inferred from the target):

```ruby
assert_authorized_to(:update?, post) do
  patch :update, id: post.id, name: "Bob"
end
```

### RSpec

Add the following to your `rails_helper.rb` (or `spec_helper.rb`):

```ruby
require "action_policy/rspec"
```

Now you can use `be_authorized_to` matcher:

```ruby
describe PostsController do
  subject { patch :update, id: post.id, params: params }

  it "is authorized" do
    expect { subject }.to be_authorized_to(:update?, post)
      .with(PostPolicy)
  end
end
```

If you omit `.with(PostPolicy)` then the inferred policy for the target (`post`) would be used.

When specifying contexts in the authorization, you can use the `with_context` modifier to ensure the context match.

```ruby
class PostController
  def post
    authorize! post, context: {favorite: true}
  end
end

describe PostsController do
  subject { patch :update, id: post.id, params: params }

  it "is authorized" do
    expect { subject }.to be_authorized_to(:update?, post)
      .with_context(favorite: true)
  end
end
```

RSpec composed matchers are available as target:

```ruby
expect { subject }.to be_authorized_to(:show?, an_instance_of(Post))
```

## Testing scoping

Action Policy provides a way to test that a correct scoping has been applied during the code execution.

For example, you can test that in your `#index` action the correct scoping is used:

```ruby
class UsersController < ApplicationController
  def index
    @user = authorized(User.all)
  end

  def for_user
    user = User.find(params[:id])
    authorized_scope(User.all, context: {user:})
  end
end
```

### Minitest

Include `ActionPolicy::TestHelper` to your test class and you'll be able to use
`assert_have_authorized_scope` assertion:

```ruby
# in your test
require "action_policy/test_helper"

class UsersControllerTest < ActionDispatch::IntegrationTest
  include ActionPolicy::TestHelper

  test "index has authorized scope" do
    sign_in users(:john)

    assert_have_authorized_scope(type: :active_record_relation, with: UserPolicy) do
      get :index
    end
  end
end
```

You can also specify `as` and `scope_options` options.

**NOTE:** both `type` and `with` params are required.

It's not possible to test that a scoped has been applied to a particular *target* but we provide
a way to perform additional assertions against the matching target (if the assertion didn't fail):

```ruby
test "index has authorized scope" do
  sign_in users(:john)

  assert_have_authorized_scope(type: :active_record_relation, with: UserPolicy) do
    get :index
  end.with_target do |target|
    # target is a object passed to `authorized` call
    assert_equal User.all, target
  end
end
```

### RSpec

Add the following to your `rails_helper.rb` (or `spec_helper.rb`):

```ruby
require "action_policy/rspec"
```

Now you can use `have_authorized_scope` matcher:

```ruby
describe UsersController do
  subject { get :index }

  it "has authorized scope" do
    expect { subject }.to have_authorized_scope(:active_record_relation)
      .with(PostPolicy)
  end
end
```

You can also add `.as(:named_scope)` and `with_scope_options(options_hash)` options.

RSpec composed matchers are available as scope options:

```ruby
expect { subject }.to have_authorized_scope(:scope)
  .with_scope_options(matching(with_deleted: a_falsey_value))
```

You can use the `with_target` modifier to run additional expectations against the matching target (if the matcher didn't fail):

```ruby
expect { subject }.to have_authorized_scope(:scope)
  .with_scope_options(matching(with_deleted: a_falsey_value))
  .with_target { |target|
    expect(target).to eq(User.all)
  }
```

Also can use the `with_context` options:

```ruby
expect { get :for_user, params: {id: user.id} }.to have_authorized_scope(:scope)
  .with_scope_options(matching(with_deleted: a_falsey_value))
  .with_context(a_hash_including(user:))
```

## Testing views

When you test views that call policies methods as `allowed_to?`, your may have `Missing policy authorization context: user` error.
You may need to stub `current_user` to resolve the issue.

Consider an RSpec example:

```ruby
describe "users/index.html.slim" do
  let(:user) { build_stubbed :user }
  let(:users) { create_list(:user, 2) }

  before do
    allow(controller).to receive(:current_user).and_return(user)

    assign :users, users
    render
  end

  describe "displays user#index correctly" do
    it { expect(rendered).to have_link(users.first.email, href: edit_user_path(users.first)) }
  end
end
```

## Linting with RuboCop RSpec

When you lint your RSpec spec files with `rubocop-rspec`, it will fail to detect RSpec aliases that Action Policy defines.
Make sure to use `rubocop-rspec` 2.0 or newer and add the following to your `.rubocop.yml`:

```yaml
inherit_gem:
  action_policy: config/rubocop-rspec.yml
```

---

---
url: /guide/rails.md
---
# Using with Rails

Action Policy seamlessly integrates with Ruby on Rails applications.

In most cases, you do not have to do anything except writing policy files and adding `authorize!` calls.

**NOTE:** both controllers and channels extensions are built on top of the Action Policy [behaviour](./behaviour.md) mixin.

## Generators

Action Policy provides a couple of useful Rails generators:

* `rails g action_policy:install` — adds `app/policies/application_policy.rb` file
* `rails g action_policy:policy MODEL_NAME` — adds a policy file and a policy test file for a given model (also creates an `application_policy.rb` if it's missing)
* `rails g action_policy:policy MODEL_NAME --parent=base_policy` — adds a policy file that inherits from `BasePolicy`, and a policy test file for a given model (also creates an `application_policy.rb` if it's missing)

## Controllers integration

Action Policy assumes that you have a `current_user` method which specifies the current authenticated subject (`user`).

You can turn off this behaviour by setting `config.action_policy.controller_authorize_current_user = false` in `application.rb`, or override it:

```ruby
class ApplicationController < ActionController::Base
  authorize :user, through: :my_current_user
end
```

You can also pass a proc that will be executed in action context `self` to `:through`. This is useful if you're using `ActiveSupport::CurrentAttributes` or want to refer to instance variables:

```ruby
class ApplicationController < ActionController::Base
  authorize :user, through: -> { @user || Current.user }
end
```

**NOTE:** The `controller_authorize_current_user` setting only affects the way authorization context is built in controllers but does not affect policy classes configuration. If you inherit from `ActionPolicy::Base`, you will still have the `user` required as an authorization context. Add `authorize :user, optional: true` to your base policy class to make it optional or use a [custom base class](custom_policy.md).

> Read more about [authorization context](authorization_context.md).

If you don't want to include Action Policy in your controllers at all,
you can disable the integration by setting `config.action_policy.auto_inject_into_controller = false` in `application.rb`.

### `verify_authorized` hooks

Usually, you need all of your actions to be authorized. Action Policy provides a controller hook which ensures that an `authorize!` call has been made during the action:

```ruby
class ApplicationController < ActionController::Base
  # adds an after_action callback to verify
  # that `authorize!` has been called.
  verify_authorized

  # you can also pass additional options,
  # like with a usual callback
  verify_authorized except: :index
end
```

You can skip this check when necessary:

```ruby
class PostsController < ApplicationController
  skip_verify_authorized only: :show

  def index
    # or dynamically within an action
    skip_verify_authorized! if some_condition
  end
end
```

When an unauthorized action is encountered, the `ActionPolicy::UnauthorizedAction` error is raised.

### `verify_authorized_scoped` hooks

Similarly, you might want to ensure that `authorized_scope` has been called during certain actions. Action Policy provides a `verify_authorized_scoped` hook for this:

```ruby
class ApplicationController < ActionController::Base
  # adds an after_action callback to verify
  # that `authorized_scope` has been called.
  verify_authorized_scoped

  # you can also pass additional options,
  # like with a usual callback
  verify_authorized_scoped only: :index
end
```

You can skip this check when necessary:

```ruby
class PostsController < ApplicationController
  skip_verify_authorized_scoped only: :show

  def index
    # or dynamically within an action
    skip_verify_authorized_scoped! if some_condition
  end
end
```

When an unscoped action is encountered, the `ActionPolicy::UnscopedAction` error is raised.

### Resource-less `authorize!`

You can also call `authorize!` without a resource specified.
In that case, Action Policy tries to infer the resource class from the controller name:

```ruby
class PostsController < ApplicationController
  def index
    # Uses Post class as a resource implicitly.
    # NOTE: it just calls `controller_name.classify.safe_constantize`,
    # you can override this by defining `implicit_authorization_target` method.
    authorize!
  end
end
```

### Usage with `API` and `Metal` controllers

Action Policy is only included into `ActionController::Base`. If you want to use it with other base Rails controllers, you have to include it manually:

```ruby
class ApiController < ActionController::API
  include ActionPolicy::Controller

  # NOTE: you have to provide authorization context manually as well
  authorize :user, through: :current_user
end
```

## Channels integration

Action Policy also integrates with Action Cable to help you authorize your channels actions:

```ruby
class ChatChannel < ApplicationCable::Channel
  def follow(data)
    chat = Chat.find(data["chat_id"])

    # Verify against ChatPolicy#show? rule
    authorize! chat, to: :show?
    stream_from chat
  end
end
```

Action Policy assumes that you have `current_user` as a connection identifier.

You can turn off this behaviour by setting `config.action_policy.channel_authorize_current_user = false` in `application.rb`, or override it:

```ruby
module ApplicationCable
  class Channel < ActionCable::Channel::Base
    # assuming that identifier is called `user`
    authorize :user
  end
end
```

> Read more about [authorization context](authorization_context.md).

In case you do not want to include Action Policy to channels at all,
you can disable the integration by setting `config.action_policy.auto_inject_into_channel = false` in `application.rb`.

---

---
url: /guide/non_rails.md
---
# Using with Ruby applications

Action Policy is designed to be independent of any framework and does not have specific dependencies on Ruby on Rails.

You can [write your policies](writing_policies.md) for non-Rails applications the same way as you would do for Rails applications.

In order to have `authorize!` / `allowed_to?` / `authorized` methods, you will have to include [`ActionPolicy::Behaviour`](./behaviour.md) into your class (where you want to perform authorization):

```ruby
class PostUpdateAction
  include ActionPolicy::Behaviour

  # provide authorization subject (performer)
  authorize :user

  attr_reader :user

  def initialize(user)
    @user = user
  end

  def call(post, params)
    authorize! post, to: :update?

    post.update!(params)
  end
end
```

---

---
url: /guide/writing_policies.md
---
# 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](authorization_context.md) (by default equals to `user`):

```ruby
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](testing.md)). Use [`authorize!` / `allowed_to?` methods](./behaviour.md#authorize) instead.

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

```ruby
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](caching.md), [pre-checks](pre_checks.md), and [failure reasons tracking](reasons.md)):

```ruby
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`:

```ruby
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:

```ruby
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](./pre_checks.md)) to allow or deny a particular action. There are two methods—`allow!` and `deny!`:

```ruby
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:

```ruby
class CommentPolicy < ApplicationPolicy
end

CommentPolicy.identifier #=> :comment
```

For namespaced policies it has a form of:

```ruby
module ActiveAdmin
  class UserPolicy < ApplicationPolicy
  end
end

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

You can specify your own identifier:

```ruby
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](reasons.md) and [i18n](i18n.md).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.