Skip to content
How-to guides and worked examplesSee the guides →

Authorization API ​

Per-option reference for authorization: the initializer options, the resource option, every policy method Avo calls, and the client contract. For task-oriented documentation and worked examples, see the Authorization guide.

Configuration options go in config/initializers/avo.rb:

ruby
Avo.configure do |config|
  config.authorization_client = :pundit
  config.explicit_authorization = true
end

Policy methods go in the policy class for the resource's model, for example app/policies/post_policy.rb.

Configuration ​

authorization_client

The client Avo uses to answer authorization checks.

ruby
config.authorization_client = "Avo::ActionPolicyAuthorizationClient"
ValueBehavior
:punditUses the built-in Pundit client. Requires gem "pundit" in the Gemfile.
nilUses the built-in nil client, which applies no policies.
StringThe class name of a custom client that implements the client contract.
  • Type: Symbol, String, or nil
  • Default: :pundit (the generated initializer sets it to nil)
  • Without the add-on on your license: a client set in the initializer refuses every resource, and no policy runs. Left unset or nil, policies are skipped. See When the license lapses.

explicit_authorization

How a missing policy class or policy method is treated.

ruby
config.explicit_authorization = true
ValueBehavior
trueA missing policy class or method denies the action.
falseA missing policy class or method allows the action.
ProcEvaluated per request; a truthy result behaves like true, a falsy one like false.
ruby
config.explicit_authorization = -> {
  current_user.access_to_admin_panel? && !current_user.admin?
}

INFO

Doesn't affect field lists. A policy that declares neither whitelisted_fields nor blacklisted_fields restricts no fields, whatever this option says.

raise_error_on_missing_policy

Raises Avo::NoPolicyError when a resource has no policy class, instead of applying explicit_authorization. It doesn't apply to a missing policy method.

ruby
config.raise_error_on_missing_policy = true
  • Type: Boolean
  • Default: false

authorization_methods

Maps the actions Avo authorizes to the policy methods it calls. Keys not in the Hash fall back to the action name.

ruby
config.authorization_methods = {
  index: "avo_index?",
  show: "avo_show?",
  edit: "avo_edit?",
  new: "avo_new?",
  update: "avo_update?",
  create: "avo_create?",
  destroy: "avo_destroy?",
  search: "avo_search?"
}
  • Type: Hash of action Symbol to method name String
  • Default: {index: "index?", show: "show?", edit: "edit?", new: "new?", update: "update?", create: "create?", destroy: "destroy?"}

Resource options ​

authorization_policy

The policy class for the resource. When unset, the policy is inferred from the resource's model.

ruby
class Avo::Resources::PhotoComment < Avo::BaseResource
  self.model_class = "Comment"
  self.authorization_policy = PhotoCommentPolicy
end
  • Type: Class
  • Default: nil

Resource policy methods ​

Called with the current user and the record (or the model class when there is no record yet). Return truthy to allow.

index?

Controls the resource's sidebar entry in the auto-generated menu and access to the Index view. A summarizable column's distribution chart asks it too, or the parent's view_{association}? for a column on an association panel. Since avo >= v4.3.8

  • Type: Boolean

INFO

Not used by the menu editor. Items there are hidden through their visible block.

show?

Controls the view button on the resource row and access to the Show view.

  • Type: Boolean
An Avo Posts index table with the View (eye) icon in a row's action controls highlighted, the control the show? policy governs.

new?

Controls the Create new {model} button on the Index view and on association panels, and access to the /new page. The /new page check doesn't raise on denial.

  • Type: Boolean
The Posts Index view with the “Create new post” button highlighted in the top-right of the header, illustrating the resource the Pundit new? policy controls.

create?

Controls whether a new record can be saved, from the /new page and from association create actions. Checked only on save, so record holds the submitted form values.

  • Type: Boolean
The Avo post create form, trimmed to the Name, Body and Status fields, with the Save button in the top-right action bar highlighted.

edit?

Controls the edit button on the resource row and access to the Edit view.

  • Type: Boolean
An Avo Posts index table with the Edit (pencil) icon in a row's action controls highlighted, the control the edit? policy governs.

update?

Controls whether changes to a record can be saved. Asked twice on a save Since avo >= v4.3.8: once on the record as loaded from the database, then again once the submitted form values are filled in. Both have to allow the write.

  • Type: Boolean
  • record: the stored record on the first check, the record carrying the submitted values on the second
The Avo resource edit form with the Save button highlighted, illustrating the Pundit update? policy that controls whether a user can save changes.

destroy?

Controls the delete button and whether a record can be deleted. Per-file deletion is controlled by delete_{FIELD_ID}?.

  • Type: Boolean
An Avo Posts index table with the Delete (trash) icon in a row's action controls highlighted, the control the destroy? policy governs.

act_on?

Controls the actions button, and running an action: Avo asks it of the record on the page and of each selected record (the model class when there are none, and for Select all), and refuses the run if any is denied.

  • Type: Boolean
The Posts Index view with the “Actions” button highlighted in the header controls bar, illustrating the control the Pundit act_on? policy governs.

reorder?

Controls the record reordering controls on the Index view. It also authorizes the move itself, from any panel.

  • Type: Boolean
An Avo Course links index table with a row's record reordering controls (drag handle and up, down, to-top, to-bottom arrows) highlighted, the controls the reorder? policy governs.

Controls the resource search input at the top of the Index view.

  • Type: Boolean
The Posts Index view with the resource search input highlighted at the top of the page, illustrating the input the Pundit search? policy controls.

preview?

Controls access to the endpoint the preview field calls.

  • Type: Boolean

INFO

Doesn't hide the preview field itself. Use the field's visible option for that.

Preview field trigger highlighted on a team row in the Index view

Association policy methods ​

Defined on the parent resource's policy. {association} is the association name exactly as declared, including its pluralization: has_many :comments gives attach_comments?, not attach_comment?.

record is either the parent record or the associated row record, depending on the method:

Methodrecord
attach_, create_, act_on_, view_The parent record
detach_, show_, edit_, destroy_, reorder_The associated row record

view_{association}?

Controls whether the association panel is displayed on the parent record. It also answers the requests behind the panel:

  • The association's show route (/resources/:resource/:id/:related_name/:related_id), where a has_one panel loads its frame from. That route also asks the related record's own show?, and looks a has_many row up inside the related resource's policy scope. Since avo >= v4.3.4

  • The requests that list the association: the panel's rows, its search, a summarizable column's chart, and scope counts. Each has to name the resource the field lists with, its use_resource when the field sets one. Since avo >= v4.3.8

  • Type: Boolean

  • record: the parent record

  • Refusal: Avo::NotAuthorizedError. A list requested under a resource the association field does not list with answers ActiveRecord::RecordNotFound (404)

attach_{association}?

Controls the Attach {model} button.

  • Type: Boolean
  • record: the parent record
The Team members association Index view with the “Attach team member” button highlighted — the control the attach_{association}? policy governs.

detach_{association}?

Controls the detach button on each associated row.

  • Type: Boolean
  • record: the associated row record
The Team members association Index view with the Detach (unlink) icon in an associated record row’s action controls highlighted, the control the detach_{association}? policy governs.

create_{association}?

Controls the Create new {model} button on the association panel. It also controls adding new rows to a nested form: the form hides its "Add" button, and a save with a new row is refused.

  • Type: Boolean
  • record: the parent record
The Team members association Index view with the “Create new team member” header button highlighted, the control the create_{association}? policy governs.

show_{association}?

Controls the view button on each associated row.

  • Type: Boolean
  • record: the associated row record

WARNING

Doesn't control access to the associated record's Show view. That's decided by the associated record's own policy.

The Team members association Index view with the View (eye) icon in an associated record row’s action controls highlighted, the control the show_{association}? policy governs.

edit_{association}?

Controls the edit button on each associated row.

  • Type: Boolean
  • record: the associated row record

WARNING

Doesn't control access to the associated record's Edit view. That's decided by the associated record's own policy.

The Team members association Index view with the Edit (pencil) icon in an associated record row’s action controls highlighted, the control the edit_{association}? policy governs.

destroy_{association}?

Controls the delete button on each associated row.

  • Type: Boolean
  • record: the associated row record
The Team members association Index view with the Delete (trash) icon in an associated record row’s action controls highlighted, the control the destroy_{association}? policy governs.

act_on_{association}?

Controls the Actions dropdown on the association panel. Running an action from the panel also needs act_on? on each selected record's own policy.

  • Type: Boolean
  • record: the parent record
The Team members association Index view with the “Actions” header dropdown button highlighted, the control the act_on_{association}? policy governs.

reorder_{association}?

Controls the record reordering controls on a has_many association panel. The move itself also needs reorder? on the record's policy.

  • Type: Boolean
  • record: the associated row record
A Course links association Index (on the Course Show page) with a row's record-reordering controls (drag handle and up, down, to-top, to-bottom arrows) highlighted, the controls the reorder_{association}? policy governs.

inherit_association_from_policy

Class method that defines the association methods above by delegating to another policy. Available after including Avo::Authorization::Concerns::PolicyHelpers.

ruby
class PostPolicy < ApplicationPolicy
  include Avo::Authorization::Concerns::PolicyHelpers

  inherit_association_from_policy :comments, CommentPolicy
end
Defined methodDelegates to
create_comments?CommentPolicy#create?
edit_comments?CommentPolicy#edit?
update_comments?CommentPolicy#update?
destroy_comments?CommentPolicy#destroy?
show_comments?CommentPolicy#show?
reorder_comments?CommentPolicy#reorder?
act_on_comments?CommentPolicy#act_on?
attach_comments?CommentPolicy#attach?
detach_comments?CommentPolicy#detach?
view_comments?CommentPolicy#index?

Each delegate is called as CommentPolicy.new(user, record). A method defined in the policy after the call overrides the generated one.

  • Arguments: association_name (Symbol), policy_class (Class)

Attachment policy methods ​

{FIELD_ID} is the file field's id. user and record are available. The same methods authorize file fields on actions that run on the resource.

upload_{FIELD_ID}?

Controls whether a file can be uploaded to the field.

  • Type: Boolean

download_{FIELD_ID}?

Controls whether the field's file can be downloaded.

  • Type: Boolean

delete_{FIELD_ID}?

Controls whether the field's file can be deleted.

  • Type: Boolean

Policy scope ​

Scope

Nested policy class whose resolve method filters the records on the Index, Show, and Edit views. scope is the query being run.

ruby
class PostPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      user.admin? ? scope.all : scope.where(published: true)
    end
  end
end
  • Type: Class with a resolve method returning a relation
  • Applies to: the resource's own views, and the rows of has_many and has_and_belongs_to_many panels that list this resource. The association field's scope option runs after it, on the narrowed query.

Field lists ​

Policy methods that name which of the resource's fields a user may reach. A withheld field is neither rendered nor settable on any Avo surface. See Hide fields from a user for what that covers.

Both lists hold field ids, not column names. An id matching no field the resource declares on the current view is ignored. When both are declared, the allowlist resolves first and the denylist subtracts from it. A private declaration counts as declared.

whitelisted_fields

The only field ids this user may reach.

ruby
def whitelisted_fields
  user.admin? ? :all : [:id, :name, :email]
end
  • Type: :all, :none, or Array of field id Symbols
  • Default: :all when undeclared

blacklisted_fields

Field ids this user may not reach.

ruby
def blacklisted_fields
  user.admin? ? [] : [:budget, :internal_notes]
end
  • Type: :all, :none, or Array of field id Symbols
  • Default: :none when undeclared

Avo::Current.interface

The surface the current request came through. Readable in field list methods and anywhere else Avo::Current is, including a field's visible: block.

ValueSet by
:uiThe admin panel
:apiavo-api
:aiavo-ai
:mcpavo-mcp_server
  • Type: Symbol
  • Default: :ui

Field list errors ​

A list that can't be resolved raises rather than falling back to no restriction. Every error subclasses Avo::Authorization::FieldResolver::Error:

ErrorRaised when
InvalidDeclarationErrorA method returns something other than :all, :none or an Array. nil counts.
ResolutionFailedErrorA method raised while being read.

Client contract ​

Methods a custom authorization_client implements. The built-in :pundit client follows the same contract.

Every method must accept keyword arguments it doesn't use, usually through **. Avo passes keys such as policy_class:, raise_exception: and resource_class:, and a method that doesn't accept them raises ArgumentError, which a UI check can swallow as a denial.

Raise on denial

authorize must raise on denial. Avo ignores its return value, so a client that returns false authorizes the action. When Avo passes raise_exception: false, it still expects the client to raise, and turns the exception into false itself.

authorize

Checks whether user can perform action, a String such as "index?", on record. Called for sidebar items, buttons, menu visibility, and controller requests.

ruby
def authorize(user, record, action, policy_class: nil, **)
  Pundit.authorize(user, record, action, policy_class: policy_class)
rescue Pundit::NotDefinedError => error
  raise Avo::NoPolicyError, error.message
rescue Pundit::NotAuthorizedError => error
  raise Avo::NotAuthorizedError, error.message
end
  • Arguments: user, record, action, policy_class:, **
  • Raises: Avo::NotAuthorizedError on denial, Avo::NoPolicyError when the policy is missing

policy

Returns the policy instance for a record or model class, or nil when none exists.

ruby
def policy(user, record, **)
  Pundit.policy(user, record)
end
  • Arguments: user, record, **
  • Returns: a policy instance or nil

policy!

Returns the policy instance for a record or model class, raising when none exists.

ruby
def policy!(user, record, **)
  Pundit.policy!(user, record)
rescue Pundit::NotDefinedError => error
  raise Avo::NoPolicyError, error.message
end
  • Arguments: user, record, **
  • Raises: Avo::NoPolicyError when the policy is missing

apply_policy

Scopes a query to the records user may see. Called on the Index, Show, and Edit views.

ruby
def apply_policy(user, model, policy_class: nil, **)
  scope_from_policy_class = scope_for_policy_class(policy_class)

  if scope_from_policy_class.present?
    scope_from_policy_class.new(user, model).resolve
  else
    Pundit.policy_scope!(user, model)
  end
rescue Pundit::NotDefinedError => error
  raise Avo::NoPolicyError, error.message
end
  • Arguments: user, model (a relation or model class), policy_class:, **
  • Returns: the scoped query
  • Raises: Avo::NoPolicyError when the policy is missing

permitted_field_ids

Optional. Returns the subset of declared_field_ids this user may reach, for reads and writes alike. A client that implements neither this nor field_reachable? imposes no field restriction.

ruby
def permitted_field_ids(user, record, declared_field_ids:, policy_class: nil)
  resolved = policy_class ? policy_class.new(user, record) : policy(user, record)
  return declared_field_ids if resolved.nil?

  Avo::Authorization::FieldResolver.new(
    policy: resolved,
    declared_field_ids: declared_field_ids
  ).permitted_field_ids
end
  • Arguments: user, record, declared_field_ids: (Array of Symbols), policy_class: (the resource's authorization_policy, if set)
  • Returns: Array of field id Symbols
  • Raises: on resolution failure. Returning the full list on error falls open.

field_reachable?

Optional. Returns whether one field id survives the policy's lists, for callers that resolve a single field, such as a record title in a search result or an association picker.

ruby
def field_reachable?(user, record, field_id, declared_field_ids: [], policy_class: nil)
  resolved = policy_class ? policy_class.new(user, record) : policy(user, record)
  return true if resolved.nil?

  Avo::Authorization::FieldResolver.new(
    policy: resolved,
    declared_field_ids: declared_field_ids
  ).reaches?(field_id)
end
  • Arguments: user, record, field_id, declared_field_ids: (empty when the resource hasn't detected its fields), policy_class:
  • Returns: Boolean