Securing Ruby on Rails Applications: Part 1 (Use Strong Authentication and Authorization Mechanisms)

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.