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