ruby 45 lines · 9 steps

Policy-based authorization in a Rails controller

Every write action loads the article, checks a policy, and only then mutates state.

Explained by highlit
1class ArticlesController < ApplicationController
2 before_action :set_article, only: %i[show edit update destroy publish]
3 
4 def edit
5 authorize! ArticlePolicy.new(current_user, @article), to: :edit?
6 end
7 
8 def update
9 authorize! ArticlePolicy.new(current_user, @article), to: :update?
10 
11 if @article.update(article_params)
12 redirect_to @article, notice: "Article updated."
13 else
14 render :edit, status: :unprocessable_entity
15 end
16 end
17 
18 def publish
19 authorize! ArticlePolicy.new(current_user, @article), to: :publish?
20 
21 @article.publish!
22 redirect_to @article, notice: "Article published."
23 end
24 
25 def destroy
26 authorize! ArticlePolicy.new(current_user, @article), to: :destroy?
27 
28 @article.destroy
29 redirect_to articles_path, notice: "Article deleted."
30 end
31 
32 private
33 
34 def authorize!(policy, to:)
35 raise ActiveRecord::RecordNotFound unless policy.public_send(to)
36 end
37 
38 def set_article
39 @article = Article.find(params[:id])
40 end
41 
42 def article_params
43 params.require(:article).permit(:title, :body, :category_id)
44 end
45end
01 / 01
STEP 01

Walkthrough

Space play ←→ step click any line
Three takeaways
  1. 1Centralizing the authorization check in one private method keeps each action's intent focused on its policy question.
  2. 2Raising RecordNotFound on a failed check hides a resource's existence instead of leaking a 403 to unauthorized users.
  3. 3A shared before_action for record loading removes repetitive find calls across related actions.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Policy-based authorization in a Rails controller — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code