Building applications can be a lot of fun until you start thinking about authorization. Figuring out what resources a user can access and what actions they can perform on that resource is a critical component. To help with this daunting task, there are quite a few Ruby on Rails libraries for building an authorization system, one of the more popular ones is CanCan. As of this writing, CanCan had well over 5000 followers (stars) on github.com and over 700 forks.
What attracted me to CanCan after reading through their wiki and several blog posts about it was its simple approach to authorization. Rather than having fat controllers and models cluttered with authorization logic, CanCan’s simple approach is to isolate all authorization logic into a single Ability class. Sounds great, in theory. With a complex application your single Ability class can grow to an unmanageable state, more so if your application has multiple user roles all with different abilities. Trading fat models and controllers for a large, unmanageable authorization class seems like a code smell to me. Another drawback is that all abilities for that user’s role needs to be evaluated for each request unnecessarily slowing down the overall response time. Finally, CanCan hasn’t seen any activity in over 8 months which means Rails 4 support is spotty at best.
After spending several frustrating weeks wrestling with CanCan’s complex DSL forcing it to fit our application needs, I started searching for a better solution. I stumbled onto Pundit. Pundit is a very simple and small authorization library which provides a set of helpers to build your own authorization system using plain Ruby classes.
With Pundit, you create a single policy for each domain model. A policy is just a simple Ruby class that defines abilities for that model. Pundit assumes that your policy class has the same name as the model class suffixed with the word ‘Policy’. This allowed me to break down the massive CanCan Ability class into many, much smaller policies which are now easier to maintain. This also eliminates CanCan’s need to evaluate all abilities for each request and only evaluates the ability for the requested resource’s action. This is achieved by implementing a query method on your policy that maps to a controller action, for example create? in your policy would map to the controller’s create action to determine if the user has the ability to create the resource. If you’ve upgraded to Rails 4, or are familiar with Strong Parameters, you can also define the permitted attributes in a policy. This is especially useful when you have user roles with different permissions for which attributes they can update. Just another example of how Pundit helps keep your authorization logic out of controllers and models.
What makes Pundit a winner for me is that there is no complicated DSL. It provides simple helpers and object oriented design patterns that can be leveraged in building your own authorization system that meets your project’s needs. It took a bad experience with CanCan for me to truly appreciate how simple Pundit is and having full control over how our authorization system works.
If you are interested in learning more about Pundit or CanCan, check out their github.com repos.
About the AuthorFollow on Twitter More Content by Mark Malek