Table of Contents
Ruby on Rails is a popular web development framework that emphasizes convention over configuration. One of its critical features is the ability to implement robust authorization systems to control user access. While gems like Pundit and CanCanCan provide ready-made solutions, sometimes you need to build custom authorization rules tailored to your application’s unique requirements. This tutorial guides you through creating such rules step-by-step.
Understanding Authorization in Rails
Authorization determines what actions a user can perform within your application. Unlike authentication, which verifies identity, authorization enforces permissions. In Rails, authorization logic can be integrated at various levels, including controllers, models, or through dedicated policies.
Setting Up Your Rails Environment
Before diving into custom rules, ensure your Rails project is set up correctly. Use Rails 6 or later for optimal features. Initialize a new project with:
rails new authorization_tutorial
Navigate into your project directory and set up a basic User model:
rails generate model User name:string role:string
Run migrations with:
rails db:migrate
Creating Custom Authorization Logic
To implement custom rules, you can define methods within your User model or create a separate service object. For simplicity, we’ll add methods to the User model.
Edit app/models/user.rb to include:
class User < ApplicationRecord
def can_edit?(resource)
# Example: only admins can edit
role == 'admin'
end
def can_delete?(resource)
# Example: admins and moderators can delete
['admin', 'moderator'].include?(role)
end
end
Implementing Authorization in Controllers
Use these methods inside your controllers to enforce permissions. For example, in app/controllers/articles_controller.rb:
class ArticlesController < ApplicationController
before_action :authorize_edit, only: [:edit, :update]
before_action :authorize_delete, only: [:destroy]
def edit
# editing logic
end
def update
# updating logic
end
def destroy
# deletion logic
end
private
def current_user
# Mock current user; replace with actual authentication logic
User.find(session[:user_id])
end
def authorize_edit
unless current_user.can_edit?(Article.find(params[:id]))
redirect_to root_path, alert: 'You are not authorized to edit this article.'
end
end
def authorize_delete
unless current_user.can_delete?(Article.find(params[:id]))
redirect_to root_path, alert: 'You are not authorized to delete this article.'
end
end
end
Testing Your Custom Rules
To verify your authorization logic, create seed data with users of different roles:
- Admin user with role ‘admin’
- Moderator with role ‘moderator’
- Regular user with role ‘user’
Log in as each user and attempt to perform actions like editing or deleting articles. Confirm that permissions are enforced according to your rules.
Extending and Maintaining Custom Rules
As your application grows, consider organizing your authorization logic into service objects or policy classes. This approach keeps controllers clean and makes testing easier. For example, create a UserPolicy class to encapsulate permissions.
Additionally, always keep your authorization logic updated with your application’s evolving requirements. Regularly review and refactor rules for clarity and security.
Conclusion
Building custom authorization rules in Ruby on Rails provides flexibility to tailor access control to your application’s needs. By defining permission methods within models and enforcing them in controllers, you can create a secure and maintainable permission system. Remember to test thoroughly and consider organizing complex rules into dedicated policy classes for better scalability.