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