Authorization is a critical component of any web application, ensuring that users can only access resources they are permitted to. Pundit is a popular authorization library for Ruby on Rails that allows developers to define fine-grained access rules. This article will guide you through using the Pundit gem for authorization in a Rails application, complete with an example.
Step-by-Step Guide
1. Adding Pundit to Your Rails Application
First, add Pundit to your Gemfile and run bundle install:
gem 'pundit'
bundle install
Next, generate the Pundit installation files:
rails generate pundit:install
This will create an application_policy.rb file in the app/policies directory, which serves as the default policy for all models.
2. Defining Policies
Policies in Pundit are Plain Old Ruby Objects (POROs) that encapsulate the authorization logic. Each policy corresponds to a model in your application. Let’s consider a simple example where we have a Post model and we want to define authorization rules for it.
Create a policy for the Post model:
rails generate pundit:policy post
This generates a post_policy.rb file in the app/policies directory.
3. Implementing Authorization Logic
Open the post_policy.rb file and define the authorization rules:
class PostPolicy < ApplicationPolicy def index? true end def show? true end def create? user.present? end def update? user.present? && user == record.user end def destroy? user.present? && user == record.user end end
Here, we define methods corresponding to each action (index?, show?, create?, update?, and destroy?). These methods return true or false based on the user and the record being accessed.
4. Using Policies in Controllers
In your controllers, you can use Pundit to authorize actions. First, include the Pundit module in the ApplicationController:
class ApplicationController < ActionController::Base
include Pundit
end
Then, use the authorize method to check authorization in your PostsController:
class PostsController < ApplicationController before_action :authenticate_user! before_action :set_post, only: [:show, :edit, :update, :destroy] def index @posts = Post.all authorize @posts end def show end def new @post = Post.new authorize @post end def create @post = current_user.posts.build(post_params) authorize @post if @post.save redirect_to @post, notice: 'Post was successfully created.' else render :new end end def edit authorize @post end def update authorize @post if @post.update(post_params) redirect_to @post, notice: 'Post was successfully updated.' else render :edit end end def destroy authorize @post @post.destroy redirect_to posts_url, notice: 'Post was successfully destroyed.' end private def set_post @post = Post.find(params[:id]) end def post_params params.require(:post).permit(:title, :body) end end
In this controller, we use authorize to check permissions before performing any actions.
5. Handling Unauthorized Access
Pundit raises a Pundit::NotAuthorizedError if a user is not authorized to perform an action. You can handle this error globally in the ApplicationController:
class ApplicationController < ActionController::Base include Pundit rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized private def user_not_authorized flash[:alert] = "You are not authorized to perform this action." redirect_to(request.referrer || root_path) end end
This way, if a user tries to perform an unauthorized action, they will be redirected with an error message.
Conclusion
Using Pundit for authorization in Rails is a powerful and flexible way to control access to resources in your application. By defining policies and using them in your controllers, you can ensure that users can only perform actions they are authorized for. This covered the basics, but Pundit also supports more complex scenarios, including scopes and custom policy generators.
For more detailed information, check out the Pundit GitHub repository