DEV Community

Cover image for Do you really understand authorization?
Jonathan Frias
Jonathan Frias

Posted on

Do you really understand authorization?

Pretty much every web application handles authorization, and there are so many different libraries. How do you pick the right one? You're about to find out.

Fundamentally, authorization is a matching game to determine if some actor is allowed access to some resource. There's many ways to skin the cat, but they all follow the rules. Here are my rules:

  • Authentication presumes authorization. Null Object pattern is your friend here. You can absolutely make a Guest or Anonymous user with no access to anything. There isn't any reason to treat unauthorized users differently besides native language support for nil.

  • Authorization systems are written from either perspective: the user, or the resource.

  • Any arbitrarily complex authorization code can be constructed by the basis of the following 5 concepts:

  1. Users (You, customer, external system)
  2. Groups (This is a collection of users)
  3. Permissions (CRUD+, standard CRUD plus whatever)
  4. Roles (This is a collection of Permissions)
  5. Resource (Anything! A record, a method, object etc)

In this mental framework Groups are just a collection of users and can be recursively defined. A group can be a tree of groups. Same with Roles.
A Role being a collection of permissions was a bit jarring at first, but think about it for a bit, and you'll realize it's true. There's quite a few of libraries that use role when instead of permission so be careful.

Now you have a solid mental framework to quickly review authorization options based on the trade offs that are being made by any particular library. I have yet to see any authorization library that supports all of them at the same time.

Let's review a few popular ruby gems and we'll see the trade-offs using this framework

  1. CanCanCan
  2. Pundit
  3. Apartment
  4. acts_as_tenant

CanCanCan

Let's look at cancan/cancancan, a popular gem originally created by the infamous Ryan Bates.

Straight from the README:

class Ability
  include CanCan::Ability

  def initialize(user)
    can :read, Post, public: true

    return unless user.present?  # additional permissions for logged in users (they can read their own posts)
    can :read, Post, user: user

    return unless user.admin?  # additional permissions for administrators
    can :read, Post
  end
end
Enter fullscreen mode Exit fullscreen mode

You can immediately see the tradeoffs in the short example.

users groups permissions roles resource auditable complexity
low

CanCanCan supports users, permissions, and resources, limited support for auditing support, shallow learning curve, fail closed access via load_and_authorize_resource

CanCanCan works from the "perspective" of the user.

Supporting groups of users, or roles is left to the developer to implement.


Pundit

users groups permissions roles resource auditable complexity
medium

Pundit works from the perspective of the resource via policies.

Via policies, Pundit supports resources, users, auditing, a bit complex, permissions, fail closed via after_action :verify_authorized

Supporting groups of users, or roles is left to the developer to implement.


Apartment

users groups permissions roles resource auditable complexity
high

Advertised as "Multitenancy for Rails and ActiveRecord" means it supports groups. It does so via database replication/sharding/schema_search_path.

This design means that it supports users, groups, and resources, and is trivially auditable. fail open/close depends on how you handle your database connections/search path, and has a steep learning curve and maintenance burden.

It does not support permissions, roles.


acts_as_tenant

users groups permissions roles resource auditable complexity
low

This gem uses the ActiveRecord's default_scope to filter out records based on the group. It is quite an elegant solution, even though it doesn't solve permissions, nor roles.


There are various other role based authorization gems that have similar trade-offs. (Spoiler: They never support groups)

If only there was a gem that supported it all:

  1. Trivial to use
  2. Groups
  3. Users
  4. Roles
  5. Permissions
  6. Trivial to Audit
  7. Fail-closed design
  8. Can work from the perspective of a user or resource

That gem doesn't exist.. yet

Top comments (0)