Pre-Checks
Pre-checks allow you to run common authorization logic before every rule. The most common use case: allowing admins to bypass all authorization.
The Problem
Without pre-checks, you’d repeat admin logic in every rule:
class ProductPolicy < ApplicationPolicy def update? user.admin? || user.id == record.user_id # Admin check repeated end
def destroy? user.admin? || user.id == record.user_id # Same check again! end
def archive? user.admin? || some_other_condition # And again... endendUsing pre_check
Pre-checks run before every policy rule:
class ApplicationPolicy < ActionPolicy::Base pre_check :allow_admins
private
def allow_admins allow! if user&.admin? endendNow every policy that inherits from ApplicationPolicy will automatically allow admins!
How Pre-Checks Work
A pre-check can:
- Allow - Call
allow!to immediately authorize - Deny - Call
deny!to immediately reject - Continue - Return anything else to proceed to the actual rule
def allow_admins # If admin, stop here and allow allow! if user&.admin?
# If not admin, this pre-check does nothing, # and the actual rule will runendUpdate ApplicationPolicy
Open and add the admin pre-check:
class ApplicationPolicy < ActionPolicy::Base pre_check :allow_admins
private
def allow_admins allow! if user&.admin? endendUpdate User Model
We need an admin? method on User. Open :
class User < ApplicationRecord has_secure_password has_many :sessions, dependent: :destroy
normalizes :email_address, with: ->(e) { e.strip.downcase }
def admin? email_address.include?("admin") endendFor this tutorial, we consider any user with “admin” in their email as an admin.
Test Admin Access
Log in with admin@example.com / secret123 and try editing/deleting products:
Now log in with user@example.com / secret123:
Selective Pre-Checks
You can limit pre-checks to specific rules:
class ProductPolicy < ApplicationPolicy # Only apply to update and destroy pre_check :allow_admins, only: [:update?, :destroy?]
# Apply to everything except index pre_check :require_verified_email, except: [:index?]endSkip Pre-Checks
Child policies can skip inherited pre-checks:
class SensitiveDataPolicy < ApplicationPolicy # Even admins shouldn't bypass this! skip_pre_check :allow_admins, only: [:destroy?]
def destroy? # Only super-admins can destroy sensitive data user.super_admin? endendMultiple Pre-Checks
You can have multiple pre-checks that run in order:
class ApplicationPolicy < ActionPolicy::Base pre_check :allow_admins pre_check :deny_banned_users pre_check :require_verified_email
private
def allow_admins allow! if user&.admin? end
def deny_banned_users deny! if user&.banned? end
def require_verified_email deny! unless user&.email_verified? endendThe first pre-check to call allow! or deny! wins. If none do, the actual rule runs.
Next, let’s learn about scoping to filter records based on user permissions!
- Preparing Ruby runtime
- Prepare development database
- Starting Rails server