### Execute Bundle Install
Source: https://actionpolicy.evilmartians.io/guide/quick_start
Run this command after updating your Gemfile to install the gem.
```sh
bundle install
```
--------------------------------
### Install Action Policy with RubyGems
Source: https://actionpolicy.evilmartians.io/guide/quick_start
Use this command to install the Action Policy gem directly.
```bash
gem install action_policy
```
--------------------------------
### Basic Controller and View Setup
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Standard Rails controller to fetch records and an ERB view to iterate and display them, conditionally rendering edit links based on authorization.
```ruby
class CommentsController < ApplicationController
def index
# all comments for all posts
@comments = Comment.all
end
end
```
```erb
<% @comments.each do |comment| %>
<%= comment.text %>
<% if allowed_to?(:edit?, comment) %>
<%= link_to comment, "Edit" %>
<% end %>
<% end %>
```
--------------------------------
### Populate Reasons with Local Rules
Source: https://actionpolicy.evilmartians.io/guide/reasons
Wrap local rules within `allowed_to?` to populate the reasons object. This example shows how to check for viewing applicants and then the stage.
```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
```
```ruby
# then the reasons object could be
p ex.result.reasons.to_h #=> { applicant: [:view_applicants?] }
```
```ruby
# or
p ex.result.reasons.to_h #=> { stage: [:show?] }
```
--------------------------------
### Example of nested policy context mismatch
Source: https://actionpolicy.evilmartians.io/guide/authorization_context
Demonstrates a scenario where nested policies require different context keys, leading to authorization errors.
```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
```
--------------------------------
### Subscribe to `action_policy.apply_rule` Event
Source: https://actionpolicy.evilmartians.io/guide/instrumentation
Subscribe to the `action_policy.apply_rule` event to track policy cache hits/misses and timing. This example sends data to Librato.
```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
```
--------------------------------
### Define a PostPolicy with Rules
Source: https://actionpolicy.evilmartians.io/guide/quick_start
Example of a policy class for a 'Post' resource, defining 'show?' and 'update?' rules. The 'user' is the performing subject and 'record' is the target object.
```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
```
--------------------------------
### Example Actionable Error Message (Stage Access)
Source: https://actionpolicy.evilmartians.io/guide/reasons
Provide a user-friendly error message when the failure reason is related to lacking access to a specific stage, suggesting a specific action.
```text
You don't have access to the stage XYZ.
Please, ask your manager to grant access to this stage.
```
--------------------------------
### Controller with Implicit Rules
Source: https://actionpolicy.evilmartians.io/guide/aliases
Example of a controller using `before_action` to load a resource and then authorizing it. The `authorize!` call implicitly checks for rules like `edit?`, `update?`, or `destroy?` based on the action.
```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
```
--------------------------------
### Specify Reason for Deny Call
Source: https://actionpolicy.evilmartians.io/guide/reasons
Use `deny!` with a specific reason identifier when a condition is not met. This example denies access for anonymous users.
```ruby
class TeamPolicy < ApplicationPolicy
def show?
deny!(:no_user) if user.anonymous?
user.has_permission?(:view_teams)
end
end
```
```ruby
p ex.result.reasons.to_h #=> { applicant: [:no_user] }
```
--------------------------------
### GraphQL Query for Authorization Rules
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Example GraphQL query to check authorization status and retrieve decline messages and reasons for specific actions.
```graphql
{
post(id: $id) {
canEdit {
value
message
reasons {
details
fullMessages
}
}
canDestroy {
# ...
}
}
}
```
--------------------------------
### Query authorization rules from client
Source: https://actionpolicy.evilmartians.io/guide/graphql
Example GraphQL query structure for accessing authorization results including values, messages, and failure reasons.
```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 {
# ...
}
}
}
```
--------------------------------
### Example Actionable Error Message (Manager Role)
Source: https://actionpolicy.evilmartians.io/guide/reasons
Provide a user-friendly error message when the failure reason is related to insufficient permissions for viewing applicants, suggesting a specific action.
```text
You don't have enough permissions to view applicants.
Please, ask your manager to update your role.
```
--------------------------------
### Custom Lookup Chain Probe
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Extends the Action Policy lookup chain by adding a custom probe. This example demonstrates how to add a probe that returns a NullPolicy as a fallback when no other policy is found.
```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
]
```
```ruby
ActionPolicy::LookupChain.chain << ->(_, _) { NullPolicy }
```
--------------------------------
### Controller Show Action with Authorization
Source: https://actionpolicy.evilmartians.io/guide/caching
Example of a Rails controller action using Action Policy for authorization checks on a record. Ensure the policy is included in your controller.
```ruby
class PostsController < ApplicationController
def show
@post = Post.find(params[:id])
authorize! @post
render :show
end
end
```
--------------------------------
### Test PostPolicy with RSpec DSL
Source: https://actionpolicy.evilmartians.io/guide/testing
Example of using the Action Policy RSpec DSL to test policy rules. It simplifies writing specs for authorization rules and their outcomes (succeed/failed).
```ruby
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
```
--------------------------------
### Customizing Authorization Namespace Logic
Source: https://actionpolicy.evilmartians.io/guide/namespaces
Provides an example of overriding the `authorization_namespace` method to define custom logic for determining the current authorization namespace based on user roles.
```ruby
def authorization_namespace
return ::Admin if current_user.admin?
return ::Staff if current_user.staff?
# fallback to current namespace
super
end
```
--------------------------------
### Policy with Aliases and Default Rule (Subclass Example)
Source: https://actionpolicy.evilmartians.io/guide/aliases
Illustrates rule resolution order in subclasses. `SubPolicy` defines its own aliases and sets a `nil` default rule, affecting how authorization checks are resolved compared to its superclass.
```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
```
--------------------------------
### Controller Index Action with Nested Authorization
Source: https://actionpolicy.evilmartians.io/guide/caching
Example of a Rails controller action fetching multiple records and iterating through them, potentially leading to N+1 authorization issues if not handled by thread memoization.
```ruby
class CommentsController < ApplicationController
def index
# all comments for all posts
@comments = Comment.all
end
end
```
--------------------------------
### View Iterating Over Records with Authorization Checks
Source: https://actionpolicy.evilmartians.io/guide/caching
Example of a Rails view iterating over a collection of records and performing authorization checks for each item, highlighting the potential for N+1 authorization problems.
```erb
<% @comments.each do |comment| %>
<%= comment.text %>
<% if allowed_to?(:edit?, comment) %>
<%= link_to comment, "Edit" %>
<% end %>
<% end %>
```
--------------------------------
### Policy with Nested Authorization Check
Source: https://actionpolicy.evilmartians.io/guide/caching
Example of a policy defining an `edit?` rule that includes a nested authorization check using `allowed_to?`, demonstrating a scenario where thread memoization is beneficial.
```ruby
class CommentPolicy < ApplicationPolicy
def edit?
user.admin? || (user.id == record.id) ||
allowed_to?(:manage?, record.post)
end
end
```
--------------------------------
### Namespaced Resource Policy Lookup
Source: https://actionpolicy.evilmartians.io/guide/namespaces
Explains that by default, Action Policy includes the namespace in the policy name search. This example shows how `authorize!` will look for `Admin::UserPolicy` for an `Admin::User` object.
```ruby
class Admin
class User
end
end
# search for Admin::UserPolicy, but not for UserPolicy
authorize! Admin::User.new
```
--------------------------------
### Define a Policy Rule
Source: https://actionpolicy.evilmartians.io/guide/debugging
Example of a boolean expression used for a policy rule, combining multiple conditions.
```ruby
def feed?
(admin? || allowed_to?(:access_feed?)) &&
(user.name == "Jack" || user.name == "Kate")
end
```
--------------------------------
### Initialize Policy with Nil User
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Demonstrates initializing `GuestPolicy` with `user: nil`, which is allowed due to `allow_nil: true`.
```ruby
GuestPolicy.new(user: nil) #=> OK
GuestPolicy.new #=> raises ActionPolicy::AuthorizationContextMissing
```
--------------------------------
### Initialize and Apply Policy Rules
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Manually initializes a policy object with a record and context, then calls a rule method directly or uses the `apply` method for enhanced functionality like caching and failure reason tracking.
```ruby
policy = PostPolicy.new(post, user: user)
# simply call rule method
policy.update?
policy.apply(:update?)
```
--------------------------------
### Initialize and Apply Scope Explicitly
Source: https://actionpolicy.evilmartians.io/guide/scoping
Use this when you need to apply a scope without including the full Action Policy behavior. Initialize the policy with the user and then apply the desired scope.
```ruby
policy = ApplicantPolicy.new(user: user)
policy.apply_scope(User.all, type: :relation)
```
--------------------------------
### Initialize Policy Object
Source: https://actionpolicy.evilmartians.io/guide/writing_policies
Initialize a policy object by specifying the target record and context. The context defaults to the user if not explicitly provided.
```ruby
policy = PostPolicy.new(post, user: user)
# simply call rule method
policy.update?
```
--------------------------------
### RSpec Failure Exception Message Example
Source: https://actionpolicy.evilmartians.io/llms-full.txt
When a test fails, the exception message includes the expected outcome and reasons for failure. This helps in debugging authorization logic.
```sh
1) PostPolicy#show? when post is draft
Failure/Error: ...
Expected to fail but succeed:
```
--------------------------------
### Customizing Field Authorization Options
Source: https://actionpolicy.evilmartians.io/guide/graphql
Customize field authorization behavior using options like `to:` for the rule and `with:` for a specific policy. For example, `authorize: {to: :preview?, with: CustomPolicy}`.
```ruby
field :home, Home, null: false, authorize: {to: :preview?, with: CustomPolicy}
```
--------------------------------
### Configure optional or nullable authorization context
Source: https://actionpolicy.evilmartians.io/guide/authorization_context
Use allow_nil or optional options to control how missing or nil context values are handled during policy initialization.
```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
```
--------------------------------
### Implement Fail-Fast with `allow!`
Source: https://actionpolicy.evilmartians.io/guide/writing_policies
Use `allow!` to immediately permit an action if a condition is met. Subsequent checks will still be performed unless the policy execution stops.
```ruby
class PostPolicy < ApplicationPolicy
def show?
allow! if user.admin?
check?(:publicly_visible?)
end
# ...
end
```
--------------------------------
### Pretty Print a Policy Rule
Source: https://actionpolicy.evilmartians.io/guide/debugging
Use the `pp` method within a Pry session to evaluate and display the breakdown of a policy rule's execution.
```ruby
pp :feed?
```
--------------------------------
### RSpec Debugging Output with Annotated Source
Source: https://actionpolicy.evilmartians.io/llms-full.txt
If debugging utilities are installed, the failure message includes annotated source code of the policy rule, showing the evaluated conditions and their results.
```sh
1) UserPolicy#manage? when post is draft
Failure/Error: ...
Expected to fail but succeed:
↳ user.admin? #=> true
OR
!record.draft? #=> false
```
--------------------------------
### Pretty Print a Predicate Method
Source: https://actionpolicy.evilmartians.io/guide/debugging
Use the `pp` method to inspect the evaluation of individual predicate methods within a policy.
```ruby
pp :admin?
```
--------------------------------
### Test PostPolicy with RSpec
Source: https://actionpolicy.evilmartians.io/guide/testing
Example of testing a PostPolicy class using RSpec. It covers basic authorization checks for update actions based on user roles (admin, author).
```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
```
--------------------------------
### Apply Policy Rules with Caching and Pre-checks
Source: https://actionpolicy.evilmartians.io/guide/writing_policies
Use the `apply` method to execute a rule, which includes caching, pre-checks, and failure reason tracking. This is preferred over calling rule methods directly.
```ruby
policy.apply(:update?)
```
--------------------------------
### Implement Fail-Fast with `deny!`
Source: https://actionpolicy.evilmartians.io/guide/writing_policies
Use `deny!` to immediately deny an action if a condition is met. This can be used for early exit based on specific criteria.
```ruby
class PostPolicy < ApplicationPolicy
# ...
def destroy?
deny! if record.subscribers.any?
# some general logic
end
end
```
--------------------------------
### Enable `apply_rule` event instrumentation
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Include ActionPolicy::Policy::Rails::Instrumentation to enable the `apply_rule` event.
```ruby
require "action_policy/rails/policy/instrumentation"
ActionPolicy::Base.include ActionPolicy::Policy::Rails::Instrumentation
```
--------------------------------
### Enable `authorize` event instrumentation
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Prepend ActionPolicy::Rails::Authorizer to the authorizer's singleton class to enable the `authorize` event.
```ruby
require "action_policy/rails/authorizer"
ActionPolicy::Authorizer.singleton_class.prepend ActionPolicy::Rails::Authorizer
```
--------------------------------
### Policy with Default Rule
Source: https://actionpolicy.evilmartians.io/guide/aliases
Shows how to define a default rule (`manage?`) that will be applied if no specific rule is found for an authorization check. This acts as a wildcard alias. For `ApplicationPolicy`, this makes `:manage?` match anything not explicitly defined.
```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
```
--------------------------------
### Include ActionPolicy::Policy::Core in ApplicationPolicy
Source: https://actionpolicy.evilmartians.io/guide/pundit_migration
Add `include ActionPolicy::Policy::Core` to your `ApplicationPolicy` base class to enable Action Policy's core functionalities. This is a key step in preparing policies for migration.
```ruby
include ActionPolicy::Policy::Core
```
--------------------------------
### View with Conditional Links Based on Authorization
Source: https://actionpolicy.evilmartians.io/guide/caching
Example of a Rails view that conditionally renders links based on authorization rules defined in Action Policy. Uses `allowed_to?` helper.
```erb
<%= @post.title %>
<% if allowed_to?(:edit?, @post) %>
<%= link_to "Edit", @post %>
<% end %>
<% if allowed_to?(:destroy?, @post) %>
<%= link_to "Delete", @post, method: :delete %>
<% end %>
```
--------------------------------
### Preauthorize Fields with `preauthorize: *`
Source: https://actionpolicy.evilmartians.io/guide/graphql
Use the `preauthorize: *` option to perform authorization before the field's resolver is executed. Specify `with:` to use a custom policy, e.g., `preauthorize: {with: HomePolicy}`.
```ruby
field :homes, [Home], null: false, preauthorize: {with: HomePolicy}
```
--------------------------------
### Handling Failures with all_details
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Use result.all_details to merge all failure metadata into a single hash for easier error handling.
```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
```
--------------------------------
### Configuring GraphQL Behavior
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Include ActionPolicy::GraphQL::Behaviour in your base GraphQL classes to enable 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
```
--------------------------------
### Include ActionPolicy::Behaviour in a Ruby Class
Source: https://actionpolicy.evilmartians.io/guide/non_rails
Include `ActionPolicy::Behaviour` to gain authorization methods. Define the authorization subject using `authorize :user`. This example shows how to perform authorization before updating a post.
```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
```
--------------------------------
### Configure policy lookup options
Source: https://actionpolicy.evilmartians.io/guide/behaviour
Control how policies are resolved using explicit classes, namespaces, or default fallbacks.
```ruby
allowed_to?(:edit?, post, with: SpecialPostPolicy)
```
```ruby
# Would try to lookup Admin::PostPolicy first
authorize! post, to: :destroy?, namespace: Admin
```
```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
```
```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
```
--------------------------------
### Handle Specific Failure Details
Source: https://actionpolicy.evilmartians.io/guide/reasons
Implement custom error handling based on specific details found in `ex.result.all_details`. This allows for more nuanced responses to authorization failures.
```ruby
rescue_from ActionPolicy::Unauthorized do |ex|
if ex.result.all_details[:not_found]
head :not_found
else
head :unauthorized
end
end
```
--------------------------------
### Test Active Record Scoping with RSpec
Source: https://actionpolicy.evilmartians.io/guide/testing
Example of testing Active Record scoping rules within a policy using RSpec. It verifies that `apply_scope` correctly filters and orders records based on user context.
```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
```
--------------------------------
### Allow Nil User Context
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Configure a context to allow `nil` values using `allow_nil: true`. The context key must still be present, but its value can be `nil`.
```ruby
class GuestPolicy < ApplicationPolicy
authorize :user, allow_nil: true
end
```
--------------------------------
### Policy with Rule Aliases
Source: https://actionpolicy.evilmartians.io/guide/aliases
Demonstrates how to define aliases in a policy to map multiple rule names (`edit?`, `destroy?`) to a single existing rule (`update?`). This requires inheriting from `ActionPolicy::Base` or including `ActionPolicy::Policy::Aliases`.
```ruby
class PostPolicy < ApplicationPolicy
alias_rule :edit?, :destroy?, to: :update?
end
```
--------------------------------
### Track reasons with local rules
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Wrap local rules in allowed_to? to populate failure 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
```
--------------------------------
### Skip Pre-Checks in Policy
Source: https://actionpolicy.evilmartians.io/guide/pre_checks
Demonstrates how to conditionally skip a pre-check for specific actions using the skip_pre_check macro.
```ruby
class UserPolicy < ApplicationPolicy
skip_pre_check :allow_admins, only: :destroy?
def destroy?
user.admin? && !record.admin?
end
end
```
--------------------------------
### Configure I18n Load Path for Non-Rails Projects
Source: https://actionpolicy.evilmartians.io/guide/i18n
Manually add locale files to the I18n load path for projects not using Rails.
```ruby
I18n.load_path << Dir[File.expand_path("config/locales") + "/*.yml"]
```
--------------------------------
### Specify reasons with deny!
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Explicitly deny authorization with a specific reason key.
```ruby
class TeamPolicy < ApplicationPolicy
def show?
deny!(:no_user) if user.anonymous?
user.has_permission?(:view_teams)
end
end
```
--------------------------------
### Configure RuboCop for Action Policy
Source: https://actionpolicy.evilmartians.io/guide/testing
Inherit the Action Policy RuboCop configuration in your project's .rubocop.yml.
```yaml
inherit_gem:
action_policy: config/rubocop-rspec.yml
```
--------------------------------
### Configure Controller Context with `through`
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Configure authorization context in a controller using `authorize` and specifying the method to retrieve the context object with `through`.
```ruby
class ApplicationController < ActionController::Base
authorize :account, through: :current_account
authorize :user, through: -> { @user || Current.user }
end
```
--------------------------------
### Define Scope Rules with DSL
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Use the DSL provided by scope matchers to define scope rules. This is syntactic sugar over defining matchers, allowing for a more concise way to set up scope rules.
```ruby
class ApplicationPolicy < ActionPolicy::Base
scope_matcher :relation, ActiveRecord::Relation
# now you can define scope rules like this
relation_scope { |relation| relation }
end
```
--------------------------------
### Custom Policy Name for Non-Inferrable Resources
Source: https://actionpolicy.evilmartians.io/guide/namespaces
Shows how to define a custom policy name using `policy_name` when the resource's class name doesn't directly map to the desired policy name, especially for namespaced resources.
```ruby
class Guest < User
def self.policy_name
"UserPolicy"
end
end
```
--------------------------------
### Update ApplicationPolicy initializer
Source: https://actionpolicy.evilmartians.io/guide/pundit_migration
Modify the `initialize` method of `ApplicationPolicy` to accept the `user` keyword argument, which is standard for Action Policy.
```ruby
def initialize(target, user:)
# ...
end
```
--------------------------------
### Apply Scope Options and Matchers
Source: https://actionpolicy.evilmartians.io/guide/testing
Use composed matchers to validate scope options.
```ruby
expect { subject }.to have_authorized_scope(:scope)
.with_scope_options(matching(with_deleted: a_falsey_value))
```
--------------------------------
### Test Authorization with Context
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Use with_context to verify authorization when specific context attributes are required.
```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
```
--------------------------------
### ActionPolicy::Base Implementation
Source: https://actionpolicy.evilmartians.io/guide/custom_policy
This is the default base policy provided by ActionPolicy, including all available extensions and default configurations. It sets up authorization, pre-checks, reasons, aliases, scoping, caching, and instrumentation.
```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
```
--------------------------------
### Configure Pre-authorization Raising Behavior
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Set the default raising behavior for pre-authorization checks.
```ruby
ActionPolicy::GraphQL.preauthorize_raise_exception = false
```
--------------------------------
### Global Action Policy GraphQL Configuration
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Configure global defaults for authorization behavior and rules within the GraphQL integration.
```ruby
ActionPolicy::GraphQL.authorize_raise_exception = false
```
```ruby
ActionPolicy::GraphQL.default_authorize_rule = :show_graphql_field?
```
--------------------------------
### Pre-authorization of GraphQL Fields
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Use preauthorize to check permissions before resolving the field value, compared against manual authorization.
```ruby
field :homes, [Home], null: false, preauthorize: {with: HomePolicy}
def homes
Home.all
end
```
```ruby
field :homes, [Home], null: false
def homes
authorize! "homes", to: :index?, with: HomePolicy
Home.all
end
```
--------------------------------
### Apply Authorization Context
Source: https://actionpolicy.evilmartians.io/guide/testing
Pass specific context to the scope authorization matcher.
```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:))
```
--------------------------------
### Define custom authorization behavior
Source: https://actionpolicy.evilmartians.io/guide/graphql
Configure a base GraphQL object class with necessary ActionPolicy behaviors and context helpers.
```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
```
--------------------------------
### Generate ApplicationPolicy with Rails
Source: https://actionpolicy.evilmartians.io/guide/quick_start
Use the Rails generator to create the ApplicationPolicy file.
```sh
rails generate action_policy:install
```
--------------------------------
### Localizing Detailed Failure Reasons
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Use the details hash as an interpolation source for translation strings.
```yml
en:
action_policy:
policy:
stage:
show?: "The %{title} stage is not accessible"
```
--------------------------------
### Default Rule Cache Key Generation
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Understand the default scheme Action Policy uses to build cache keys for rules, which includes namespace, context, record, policy class, and rule name.
```ruby
"#{cache_namespace}/#{context_cache_key}"
/ /#{record.policy_cache_key}/#{policy.class.name}/#{rule}"
```
--------------------------------
### Add Detailed Information to Failure Reasons
Source: https://actionpolicy.evilmartians.io/guide/reasons
Provide additional details to failure reasons using the `details: { ... }` option. This allows for richer context, such as including the record's title in the failure reason.
```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
```
```ruby
# when accessing the reasons
p ex.result.reasons.to_h #=> { stage: [{show?: {title: "Onboarding"}] }
```
```ruby
p ex.result.reasons.full_messages #=> The Onboarding stage is not accessible
```
--------------------------------
### Access All Merged Details
Source: https://actionpolicy.evilmartians.io/guide/reasons
Use `ex.result.all_details` to retrieve all aggregated details from the policy execution. This is useful for inspecting the reasons for an authorization failure.
```ruby
p ex.result.all_details #=> {not_found: true}
```
--------------------------------
### Define ApplicationPolicy
Source: https://actionpolicy.evilmartians.io/guide/quick_start
Create a base policy class that other policies can inherit from, allowing for global configuration.
```ruby
class ApplicationPolicy < ActionPolicy::Base
end
```
--------------------------------
### Add Action Policy to Gemfile
Source: https://actionpolicy.evilmartians.io/guide/quick_start
Include Action Policy in your application's Gemfile for dependency management.
```ruby
gem "action_policy"
```
--------------------------------
### Configuring Namespace Cache in Rails
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Shows how to configure the namespace cache setting within a Rails application's configuration.
```ruby
config.action_policy.namespace_cache_enabled = true
```
--------------------------------
### Controller Context with Proc
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Use a proc with `through` to dynamically determine the context object based on the controller instance.
```ruby
authorize :user, through: -> { @user || Current.user }
```
--------------------------------
### Minimal ApplicationPolicy
Source: https://actionpolicy.evilmartians.io/guide/custom_policy
A minimal custom ApplicationPolicy that includes only the required ActionPolicy::Policy::Core module. This provides the essential `apply` and `allowed_to?` methods.
```ruby
# minimal ApplicationPolicy
class ApplicationPolicy
include ActionPolicy::Policy::Core
end
```
--------------------------------
### Use Fail-Fast and Pass-Fast API in Policies
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Provides an alternative API for allowing or denying actions within policies using `allow!` and `deny!` methods. These methods facilitate a fail-fast or pass-fast approach to authorization logic.
```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
```
--------------------------------
### Configure Redis Cache Store
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Set the cache store to Redis in your Rails application configuration. Ensure your cache store provides `#read` and `#write` methods.
```ruby
Rails.application.configure do |config|
config.action_policy.cache_store = :redis_cache_store
end
```
--------------------------------
### Define additional authorization context in a policy
Source: https://actionpolicy.evilmartians.io/guide/authorization_context
Use the authorize method within a policy class to declare required context keys.
```ruby
class ApplicationPolicy < ActionPolicy::Base
authorize :account
end
```
--------------------------------
### Manual Authorization in GraphQL Resolver
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Perform authorization manually within a resolver method using the authorize! helper.
```ruby
def home(id:)
Home.find(id).tap { |home| authorize! home, to: :show? }
end
```
--------------------------------
### Authorizing GraphQL Fields
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Add the authorize: true option to fields to automatically protect access via the show? policy method.
```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
```
--------------------------------
### Enable Instrumentation for Non-Rails Usage
Source: https://actionpolicy.evilmartians.io/guide/instrumentation
For non-Rails applications with ActiveSupport::Notifications, enable `apply_rule` event instrumentation by including `ActionPolicy::Policy::Rails::Instrumentation`. Enable `authorize` event by prepending `ActionPolicy::Rails::Authorizer` to the authorizer's singleton class.
```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
```
--------------------------------
### Preauthorize Fields Equivalent Code
Source: https://actionpolicy.evilmartians.io/guide/graphql
The `preauthorize: {with: HomePolicy}` option is equivalent to explicitly calling `authorize!` before resolving the field. The `record` passed to the policy is the field's name.
```ruby
field :homes, [Home], null: false
def homes
authorize! "homes", to: :index?, with: HomePolicy
Home.all
end
```
--------------------------------
### Default ActionPolicy GraphQL Behaviour
Source: https://actionpolicy.evilmartians.io/llms-full.txt
Includes ActionPolicy behaviour, thread-memoized and memoized extensions, and sets up the authorization context and field extensions.
```ruby
class Types::BaseObject < GraphQL::Schema::Object
include ActionPolicy::Behaviour
include ActionPolicy::Behaviours::ThreadMemoized
include ActionPolicy::Behaviours::Memoized
include ActionPolicy::Behaviours::Namespaced
authorize :user, through: :current_user
def current_user
context[:current_user]
end
field_class.prepend(ActionPolicy::GraphQL::AuthorizedField)
include ActionPolicy::GraphQL::Fields
end
```
--------------------------------
### Define Policy Rules with Inline Predicates
Source: https://actionpolicy.evilmartians.io/guide/pre_checks
Standard approach where common authorization logic is repeated within each rule method.
```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
```
--------------------------------
### Deeply Nested Namespaced Policy Lookup
Source: https://actionpolicy.evilmartians.io/guide/namespaces
Illustrates Action Policy's ability to resolve policies through multiple levels of module nesting. It searches for the most specific policy first, then falls back to less specific ones.
```ruby
module Admin
module Client
class UsersController < ApplicationController
def index
# lookup for Admin::Client::UserPolicy -> Admin::UserPolicy -> UserPolicy
authorize!
end
end
end
end
```
--------------------------------
### Authorize Chat Channel Actions with Action Policy
Source: https://actionpolicy.evilmartians.io/guide/rails
Authorize a chat action within a channel using `authorize!` against a specific policy rule. Assumes `current_user` is available.
```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
```