Authorization is an essential aspect of web application security that ensures that only authorized users have access to certain resources or functionalities within the application. Ruby on Rails provides several mechanisms for implementing authorization, including role-based and attribute-based authorization. In this article, we will discuss how to implement authorization in Ruby on Rails, with code examples.
Role-based Authorization
Installing CanCanCan
CanCanCan is a Ruby gem that provides a simple and flexible way to define abilities and permissions for each role. To use it, add it to your Gemfile and run bundle install
:
1
gem 'cancancan'
Defining Abilities
Once CanCanCan is installed, we can define abilities for each role in our application. An ability is a set of permissions that a user with a particular role has. Abilities should be defined in the app/models/ability.rb
file.
For example, if we have an admin role and a user role, we would define the abilities for each role like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :read, :all
can :create, Post
can :update, Post, user_id: user.id
can :destroy, Post, user_id: user.id
end
end
end
In the above example, we define an Ability class and define the abilities for the admin and user roles. An admin user has the ability to manage all resources, while a regular user has the ability to read all resources, create new posts, and update and destroy their own posts.
Using Abilities in Controllers
Once we have defined our abilities, we can use them in our controllers to authorize access to resources. To do this, we can use the load_and_authorize_resource
method provided by CanCanCan:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class PostsController < ApplicationController
load_and_authorize_resource
def index
@posts = @posts.order(created_at: :desc)
end
def create
if @post.save
redirect_to @post
else
render :new
end
end
def update
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
def destroy
@post.destroy
redirect_to posts_url
end
end
In the above example, we use the load_and_authorize_resource method to automatically load the resource and authorize access to it. If the user is not authorized to access the resource, CanCanCan will raise a CanCan::AccessDenied
exception.
Using Abilities in Views
We can also use abilities in our views to conditionally display or hide content based on the user’s role. To do this, we can use the can?
method provided by CanCanCan:
1
2
3
<% if can? :update, @post %>
<%= link_to 'Edit', edit_post_path(@post) %>
<% end %>
In the above example, we use the can?
method to check if the user has the ability to update the @post resource. If the user has the ability, we display a link to edit the post.
Attribute-based Authorization
Installing Pundit
Pundit is a Ruby gem that provides a simple and flexible way to define policies for each resource. To use it, add it to your Gemfile and run bundle install
:
1
gem 'pundit'
Defining Policies
Once Pundit is installed, we can define policies for each resource in our application. A policy is a Ruby class that defines the rules for access to a particular resource. Policies should be created in the app/policies
directory and named after the resource they apply to, with the word “Policy” appended.
For example, if we have a Post model, we would create a PostPolicy
class:
1
2
3
4
5
6
7
8
9
10
11
12
class PostPolicy
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def update?
user.admin? || post.user == user
end
end
In the above example, we define a PostPolicy class and define an update?
method that returns true if the user is an admin or the post belongs to the user.
Using Policies in Controllers
Once we have defined our policies, we can use them in our controllers to authorize access to resources. To do this, we can use the authorize
method provided by Pundit:
1
2
3
4
5
6
7
8
9
10
11
12
class PostsController < ApplicationController
def update
@post = Post.find(params[:id])
authorize @post
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
end
In the above example, we authorize access to the @post resource using authorize @post. If the user is not authorized to access the resource, Pundit will raise a Pundit::NotAuthorizedError
exception.
Using Policies in Views
We can also use policies in our views to conditionally display or hide content based on the user’s permissions. To do this, we can use the policy and can?
methods provided by Pundit:
1
2
3
<% if policy(@post).update? %>
<%= link_to 'Edit', edit_post_path(@post) %>
<% end %>
In the above example, we use the policy method to retrieve the policy for the @post
resource and the can?
method to check if the user is authorized to update the resource. If the user is authorized, we display a link to edit the post.