Using authorize!

Now that we have a policy, let’s use it in our controller. The authorize! method is the primary way to enforce authorization.

How authorize! Works

When you call authorize!, Action Policy:

  1. Finds the policy class - Product -> ProductPolicy
  2. Infers the rule - show action -> show? rule
  3. Gets the user - Uses current_user by default
  4. Checks the rule - Calls ProductPolicy#show?
  5. Raises on failure - Throws ActionPolicy::Unauthorized if the rule returns false

Add Authorization to the Controller

Open and add authorize! calls:

class ProductsController < ApplicationController
before_action :set_product, only: %i[ show edit update destroy ]
def index
@products = Product.all
authorize! @products
end
def show
authorize! @product
end
# ... rest of the controller
end

Handling Authorization Failures

When authorization fails, Action Policy raises ActionPolicy::Unauthorized. Let’s add a global handler in ApplicationController:

Open and add:

class ApplicationController < ActionController::Base
rescue_from ActionPolicy::Unauthorized do |exception|
redirect_to root_path, alert: "You are not authorized to perform this action."
end
end

Test the Authorization

Adding More Rules

Now let’s add rules for the other actions. Update :

# frozen_string_literal: true
class ProductPolicy < ApplicationPolicy
def index?
true
end
def show?
true
end
def new?
# For now, allow everyone
true
end
def create?
true
end
def update?
true
end
def destroy?
true
end
end

Update the Controller

Add authorize! to all actions:

class ProductsController < ApplicationController
before_action :set_product, only: %i[ show edit update destroy ]
def index
@products = Product.all
authorize! @products
end
def show
authorize! @product
end
def new
@product = Product.new
authorize! @product
end
def create
@product = Product.new(product_params)
authorize! @product
if @product.save
redirect_to @product
else
render :new, status: :unprocessable_entity
end
end
def edit
authorize! @product
end
def update
authorize! @product
if @product.update(product_params)
redirect_to @product
else
render :edit, status: :unprocessable_entity
end
end
def destroy
authorize! @product
@product.destroy
redirect_to products_path
end
private
def set_product
@product = Product.find(params[:id])
end
def product_params
params.expect(product: [ :name ])
end
end

Explicit Rule Specification

Sometimes you want to use a different rule than the action name:

# Use update? rule instead of edit?
authorize! @product, to: :update?
# Use a specific policy class
authorize! @product, with: SpecialProductPolicy

Great! Now our controller is protected by authorization. Next, let’s learn how to check permissions in views.

Powered by WebContainers
Files
Preparing Environment
  • Preparing Ruby runtime
  • Prepare development database
  • Starting Rails server