Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 14, 2022 09:06 pm GMT

Complete Guide To Managing User Permissions In Rails Apps

This article was originally written by Renata Marques on the Honeybadger Developer Blog.

A common requirement of web applications is the ability to assign specific roles and permissions.

Many types of web applications distinguish between admins and regular users in providing restricted access. This is often performed using a simple boolean that determines whether the user is an admin. However, roles and permissions can become much more complex.

The value of your application lies in restricting access to certain data and actions. It's definitely something you don't want to mess up. In this post, we will explain how to implement roles and permissions in a basic Ruby on Rails application.

Do I need a gem to manage permissions?

No, you don't need a gem, especially if your application is small and you want to avoid adding more dependencies to your code base.
However, if you are looking for alternatives, here are the most popular gems that deal with roles and permissions:

  • Devise
    Devise is a gem for authentication and roles management, and it is a really complex and robust solution.
    With 21.7k stars on GitHub, it is the most popular repo in this post, but it does more than roles management. It is known as an authentication solution, so only apply it to your codebase if you need a very robust library.

  • Pundit:
    Pundit is a gem that uses simple Ruby objects, and it is probably the simplest policy gem we will cover. Is simple to use, has minimal authorization, and is similar to using pure Ruby. With 7.3k stars on GitHub, it is currently the most popular policy gem.

  • CanCan:
    CanCan is an authorization library that restricts the resources a given user is allowed to access. However, CanCan has been abandoned for years and only works with Rails 3 and earlier releases.

  • CanCanCan:
    CanCanCan is another authorization library for Ruby and Ruby on Rails. It is an alternative to CanCan and is currently being maintained. With 4.9k stars on GitHub, it is the least popular, but it works pretty well and is well maintained.

All of these gems are great, but it's not too hard to build permissions yourself in plain Ruby. I will show you how to manage permissions without a gem, using a strategy called policy object pattern.

Policy object pattern

Policy Object is a design pattern used to deal with permissions and roles. You can use it each time you have to check whether something or someone is allowed to perform an action. It encapsulates complex business rules and can easily be replaced by other policy objects with different rules. All the external dependencies are injected into the policy object, encapsulating the permission check logic,
which results in a clean controller and model. Gems like Pundit, Cancan, and Cancancan implement this pattern.

Pure policy object rules

  • The return has to be a boolean value
  • The logic has to be simple
  • Inside the method, we should only call methods on the passed objects

Implementation

Let's start with the naming convention; the filename has the _policy suffix applied and the class and policy at the end.
In this method, names always end with the ? character (e.g.,UsersPolicy#allowed?).

Here is some example code:

class UsersPolicy  def initialize(user)    @user = user  end  def allowed?    admin? || editor?  end  def editor?    @user.where(editor: true)  end  def admin?    @user.where(admin: true)  endend

In which scenarios should I use them?

When your app has more than one type of restricted access and restricted actions. For example, posts can be created with the following:

  • at least one tag,
  • a restriction that only admins and editors can create them, and
  • a requirement that editors need to be verified.

Heres an example controller without a policy object:

class PostsController < ApplicationController  def create    if @post.tag_ids.size > 0    && (current_user.role == admin    || (current_user.role == editor && current_user.verified_email))      # create    end  endend

Because the above condition checks are long, ugly, and unreadable, the policy object pattern should be applied.

Lets begin by creating PostsCreationPolicy.

class PostsCreationPolicy  attr_reader :user, :post  def initialize(user, post)    @user = user    @post = post  end  def self.create?(user, post)    new(user, post).create?  end  def create?    with_tags? && author_is_allowed?  end  private  def with_tags?    post.tag_ids.size > 0  end  def author_is_allowed?    is_admin? || editor_is_verified?  end  def is_admin?    user.role == admin  end  def editor_is_verified?    user.role == editor` && user.verified_email  endend

Our controller with the policy object looks like this:

class PostsController < ApplicationController  def create    if PostsCreationPolicy.create?(current_user, @post)      # create    end  endend

How to use policy objects in Rails

Create a policy directory inside app /policies and place all of your policy classes there. When you need to call the controller, you can do so directly in an action or use a before_action:

class PostsController < ApplicationController  before_action :authorized?, only: [:edit, :create, :update, :destroy]  def authorized?    unless ::PostsCreationPolicy.create?(current_user, @post)      render :file => "public/404.html", :status => :unauthorized    end  endend

How to test policy objects

It's simple to test the behavior in the controller:

require 'rails_helper'RSpec.describe "/posts", type: :request do  describe "when user is not allowed" do    let(:user_not_allowed) { create(:user, admin: false, editor: false) }    let(:tag) { create(:tag) }    let(:valid_attributes) { attributes_for(:post, tag_id: tag.id) }    before do      sign_in user_not_allowed    end    describe "GET /index" do      it "return code 401" do        diet = Post.create! valid_attributes        get edit_post_url(post)        expect(response).to have_http_status(401)      end    end  endend

Testing the policy is simple, too; we have a lots of small methods with only one responsibility.

require 'rails_helper'RSpec.describe PostsCreationPolicy do  describe "when user is not allowed" do    let(:user) { create(:user, editor: false, admin: false) }    let(:user_editor) { create(:user, editor: true, email: verified) }    let(:tag) { create(:tag) }    let(:post) { create(:post, tag_id: tag.id) }    describe ".create?" do      context "when user is allowed" do        it "creates a new post" do          expect(described_class.create?(user_editor, post)).to eq(true)        end      end      context "when user is not allowed" do        it "does not create a new post" do          expected(described_class.create?(user, post)).to eq(false)        end      end    end    # ...more test cases  endend

We test whether the object is allowed to be created in every scenario.

Conclusion

The policy pattern concept is small but produces big results.
Consider applying a policy object each time you have to deal with simple or complex permissions. When it comes to testing with RSpec, you dont need to use database records; your policies
are purely Ruby objects, and your testing will be simple and fast.


Original Link: https://dev.to/honeybadger/complete-guide-to-managing-user-permissions-in-rails-apps-4dm6

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To