How to Use Pundit for Authorization in Ruby on Rails
Pundit in Ruby on Rails by creating policy classes that define authorization rules for your models, then call authorize in your controllers to enforce these rules. Include Pundit in your application controller and use policy methods like show? or update? to control access.Syntax
Pundit uses policy classes named after your models to define authorization rules. Each policy has methods ending with ? that return true or false to allow or deny actions. In controllers, you call authorize @record to check permissions.
- Policy class: Defines who can do what.
- authorize method: Checks permission in controller.
- current_user: Passed automatically to policies.
class PostPolicy attr_reader :user, :post def initialize(user, post) @user = user @post = post end def show? true end def update? user.admin? || post.user_id == user.id end end # In controller class PostsController < ApplicationController include Pundit def show @post = Post.find(params[:id]) authorize @post end end
Example
This example shows a PostPolicy that allows anyone to view a post but only the post owner or an admin to update it. The controller uses authorize to enforce these rules before showing or updating a post.
class PostPolicy attr_reader :user, :post def initialize(user, post) @user = user @post = post end def show? true end def update? user.admin? || post.user_id == user.id end end class PostsController < ApplicationController include Pundit def show @post = Post.find(params[:id]) authorize @post render plain: "Post content: #{@post.content}" end def update @post = Post.find(params[:id]) authorize @post if @post.update(post_params) render plain: "Post updated" else render plain: "Update failed", status: :unprocessable_entity end end private def post_params params.require(:post).permit(:content) end end
Common Pitfalls
Common mistakes include forgetting to include Pundit in your controller, not calling authorize for actions, or not defining policy methods correctly. Also, ensure current_user is available and policies handle nil users if needed.
Another pitfall is not rescuing Pundit::NotAuthorizedError to handle unauthorized access gracefully.
class PostsController < ApplicationController include Pundit rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized def show @post = Post.find(params[:id]) authorize @post # Must call authorize or no check happens end private def user_not_authorized render plain: "You are not allowed to do this", status: :forbidden end end
Quick Reference
- Include Pundit:
include Punditin ApplicationController. - Create Policy:
rails g pundit:policy ModelNameto generate. - Define Methods: Use
def action?returning true/false. - Authorize: Call
authorize @recordin controller actions. - Handle Errors: Rescue
Pundit::NotAuthorizedErrorfor user feedback.