Security is an important aspect of application development. Two main components of security are Authentication (Who are you?) and Authorization (are you supposed to be here?). Authentication verifies the user’s identity while authorization verifies whether that user has access rights on certain resources to perform actions on them.

Two popular gems for authorization in the rails world are CanCanCan and Pundit, we at Red Panthers prefers pundit over CanCanCan we get to write Pure Ruby Objects and keep the logic for each part separate.

The gem CanCanCan isolates (encourages) all authorization logic into a single class. It is a drawback when the complexity of application increases. Pundit gem provides object oriented design patterns to build our own authorization system that meets project’s requirements. It enables us to keep the models and controllers free from authorization code and allows to keep the resource logic separately. This flexibility and simplicity of Pundit gem help to use it with ease.

To start with Pundit

Add Pundit gem to your gem file and run bundle install.

Integrate Pundit to Rails application by adding the following line to ApplicationController.

If you run the Pundit’s generator as below, it will generate app/policies folder which contains application_policy.rb by default.

Then restart the Rails server.

Base class policy looks like

Create policies

Policy classes are the core of Pundit. In the app/policies folder, we can write our own policies. Each policy is a Ruby class. Each policy class should be named after a model they belong to, followed by the word Policy. For example, use CollectionPolicy for Collection model. Pundit can also be used without an associated model.

Pundit uses the current_user method to get the first argument in initialize method in ApplicationPolicy. But if current_user is not the method that should be invoked by Pundit, simply define a method in your controller.

Logically, Pundit can be used outside controllers, for example, in custom services or in views.

Consider the following example.

In User model,

In Collection model,

A collection should be able to be deleted only by the user who created it.

So, let’s start by creating a new file collection_policy.rb in app/policies  that will store our policies that are specific to collections.

In this file, we define a class that inherits from the ApplicationPolicy class and we will integrate delete method for managing permissions for the delete action .

In our CollectionPolicy class we are overriding the delete? method originally declared in the ApplicationPolicy. There it simply returns false. We can override the methods in ApplicationPolicy class with our unique requirements since it’s intended to give a structure only.

This states that, the only user that should be able to delete a collection is the user that created it. We can refactor this into their own method since it’s a better approach if other authorized users are needed to be added in future so that changes can be made easily in a single method instead of having to make the same changes in multiple places.

This is very explicit, clearly describing the intent of the program flow.

Definitely, we won’t be wondering, from where these record and user attributes are coming from. In ApplicationPolicy class we can see that they are set as read only attributes representing the object that we are adding authorization to, such as collection in our app and then the user. This is an instance of Pundit providing easy access to the items that we want to add authorization. If we add another policy class like CollectionPolicy then also we will be able to use very similar code like we are working with collections.

Now let’s move to CollectionsController . By using Pundit policies, a proper permission structure can be integrated to the delete action, which earlier had no protection from unauthorized HTTP requests.

The authorize method automatically assumes that Collection will have a corresponding CollectionPolicy class, and instantiates this class. It should call destroy? method on this instance of the policy. Passing a second argument to authorize method is optional here. It infers from the action name that it should call destroy? method on this instance of the policy. But second argument should be passed if it doesn’t match the action name.

Policy without a corresponding model

We can create ‘headless’ policies that are not tied to any specific model. Such policies can be retrieved by passing a symbol.

Here a model or class named Website doesn’t exist. So WebsitePolicy is retrieved by passing a symbol. This is a headless policy.

Pundit scopes

In application_policy.rb, there is a scope class defined. It implements a method called resolve for filtering. We can inherit it from a base class and implement our own resolve method. In this example a scope is setup to allow users to view websites only if they have a link through collections.

Let’s consider three models: User, Website, and Collection

Collection belongs to User.

In WebsitePolicy,

In the websites_controller,

Now we see only the websites where we have a collection.

Exception handling

By default, Pundit raises an exception when users attempt to access that which they are not authorized to. This situation can be handled in ApplicationController.

We need to rescue the Pundit::NotAuthorizedError exception with a suitable method that tells how to handle it.

Why Pundit?

  • Well suited to the service oriented architecture that’s popular for large Rails applications
  • Keep controllers skinny
  • Pundit policy objects are lightweight, adding authorization logic without as much overhead as CanCanCan
  • Emphasizes object-oriented design with discrete Ruby objects providing specialized services

Therefore, as an application grows in complexity it’s always better to prefer Pundit for authorization.

References