Authorizing your Rails app with Authority

Share this article

Imagine you’re writing a Rails app to organize conferences. As soon as you know what the app can do, you have to start deciding who can do what. Who is allowed to:

  • Decide who will speak at the conference?
  • Edit the presenter schedule?
  • Upload presentation slides?
  • Comment on those slides?
  • Create playlists of music?
  • Make a personal schedule of which talks to see?
All these questions are about authorization: “what is this user authorized to do?” Obviously, it’s important to get this logic right: it needs to be correct, and it needs to be consistent. And it would be nice if that logic was grouped together, rather than scattered all over your app. I’ve just released a gem to solve this problem cleanly. It’s called Authority (see Github and Rubygems).

Protecting Your Models

Before I show you how Authority works, let’s talk about some of the general ideas. Authority’s notion of permissions in your Rails app is focused on your models; in the example above, these would be presenters, comments, playlists, etc. In some cases, the question is very simple: a conference attendee cannot make any changes to any presenter, period. You can think of that as a “class-level” rule: the Presenter model is read-only for anyone who isn’t a conference organizer. If an attendee tries to visit the page for editing a presenter’s bio, we don’t have to ask any questions about this particular presenter to know that the action is not allowed. In other cases, the question is more nuanced: attendees can edit their own personal schedule, but not anyone else’s. You can think of that as an “instance-level” rule: to know whether a conference attendee can edit a schedule, you have to look at that schedule instance and see who it belongs to. Using Authority, you’d use class methods, like def self.updatable_by?(user) to set class-level rules, and instance methods, like def deletable_by?(user) , to set instance-level rules. But where should those methods be written?

Keeping Your Permissions DRY

Obviously, different models have different rules, so you might think that authorization methods should go on the models themselves. But it’s likely that some of your models share rules: anyone who can edit a Presenter can also edit the PresenterSchedule. If that’s true, it would be nice to keep that DRY: let the Presenter model and the PresenterSchedule model use the same authorization logic. Authority accomplishes this by having the model delegate any questions of authorization to a specified Authorizer class. Models with the same rules can point to the same Authorizer.

An Example

To take a simple example from the gem’s README, suppose you two have categories of resources in your app: some for regular users and some for administrators. Using Authority, you’d have two authorizer classes. You might call them BasicAuthorizer
and AdminAuthorizer. You could group your models like this: [gist id=’d38d297e3ed9a3411c3a’] In this example, the Comment model’s authorization rules come from BasicAuthorizer, but Article and Edition get theirs from AdminAuthorizer. You’d call self.authorizer_name = on each model to set this up. The AdminAuthorizer might have a method like this: [gist id=’0f9d6cb90948117b47fe’] Anytime a user tries to create an Article or an Edition, this method will be called. If the user isn’t an admin, it will return false and the action will be denied. Any method that isn’t defined on an authorizer will be inherited from Authority::Authorizer, which will consult a configurable default_strategy proc. The built-in default strategy simply returns false. This is a whitelisting approach: any action you don’t explicitly allow will be forbidden. But you can supply your own default strategy to do something more nuanced. So the full lookup chain looks like this: [gist id=’e577d3ef3b988aa2a1a0′]

Standard Ruby Classes And Methods

The nice thing about this structure is that it’s just regular object-oriented programming. Your authorizers are just classes, so you can modify them any way you like: include modules, change the parent class, metaprogram, etc. In addition, the authorizer’s methods are just plain Ruby methods: there’s no new syntax to learn. Authority makes no assumptions about what logic you’ll need. You can consult a database or a file or a web service to make your decisions; if you use a database, you can use whatever ORM you like. All the logic is up to you; Authority just helps you keep it organized.

Syntactic Sugar for Users

So far we’ve seen our authorization methods in the passive voice: is this resource creatable by this user? But it’s nice to be able to say the same thing in an active voice: can this user create this resource? Like several other popular authorization gems, Authority gives you this syntactic sugar. current_user.can_edit?(@article) is simply a pass-through to @article.editable_by?(current_user), which in turn would ask the AdminAuthorizer. Since can_edit? and similar methods are defined on the user object, you can use them anywhere that object is available. A common case would be to show links only to users who should see them: [gist id=’322a41a42087a7c091d1′]

A Little Magic for Controllers

If you’re using the method shown above to hide links from unauthorized users, most people won’t try anything they shouldn’t be doing. But what if someone manually types the URL to edit a resource that’s forbidden for them? At that point, your controller must intervene. Authority gives you a couple of methods for this: authorize_actions_for(ModelName), which checks class-level permissions and will stop the controller method from ever running, and authorize_action_for(@model_instance), which checks instance-level permissions from inside a controller method. In either case, forbidden actions are handled by a controller method that you specify; the default one logs the user’s action and displays a warning.

Check it out

You can get more detail on everything discussed here by looking at the README on Github. I’d also encourage you to read the source code; Authority isn’t a large gem, and the source is well-commented. You may learn something, and of course, reading the code is the first step towards contributing. Happy hacking!

Frequently Asked Questions on Authorizing Your Rails App with Authority

How does Authority compare to other Ruby gems for authentication and authorization?

Authority is a flexible, ORM-neutral authorization system for Ruby on Rails applications. Unlike other gems like Devise or CanCanCan, Authority focuses solely on authorization, not authentication. This means it’s designed to control what resources a user can access, not to manage user sessions or passwords. Authority is also unique in its use of ‘authorizers’, which are Ruby classes that encapsulate the rules for a specific model or set of models. This makes it easy to keep your authorization logic organized and DRY.

Can I use Authority with other authentication gems?

Yes, Authority can be used in conjunction with any authentication gem. It’s designed to work seamlessly with gems like Devise or Authlogic, which handle user sessions and passwords. Once a user is authenticated, Authority can then control what resources they can access based on your defined rules.

How do I set up Authority in my Rails app?

Setting up Authority in your Rails app involves a few steps. First, you’ll need to add the gem to your Gemfile and run bundle install. Then, you’ll need to generate an initializer file with rails generate authority:install. This file will contain some default configuration options, which you can customize to suit your needs. Finally, you’ll need to create ‘authorizer’ classes for each of your models, which define the authorization rules for those models.

What are ‘authorizers’ in Authority?

Authorizers’ in Authority are Ruby classes that encapsulate the authorization rules for a specific model or set of models. Each authorizer class includes methods for each action you want to control, like ‘readable_by?’ or ‘updatable_by?’. These methods return a boolean value indicating whether the given user is allowed to perform the action.

How does Authority handle role-based authorization?

Authority handles role-based authorization through its ‘authorizer’ classes. You can define different rules for different user roles within these classes. For example, you might have a ‘PostAuthorizer’ class with a ‘readable_by?’ method that returns true for admins and false for regular users. This would mean that only admins can read posts.

Can I use Authority with non-ActiveRecord models?

Yes, Authority is ORM-neutral, meaning it can be used with any object-relational mapping system, not just ActiveRecord. You can define authorizer classes for any Ruby class, whether it’s an ActiveRecord model, a plain old Ruby object, or a class from another ORM like Sequel or Mongoid.

How does Authority handle nested resources?

Authority handles nested resources through its ‘authorizer’ classes. You can define rules for parent-child relationships within these classes. For example, you might have a ‘CommentAuthorizer’ class with a ‘creatable_by?’ method that checks whether the given user is the author of the parent post.

Can I use Authority to control access to controller actions?

Yes, Authority can be used to control access to controller actions. You can use the ‘authorize_actions_for’ method in your controllers to specify which authorizer class should be used for each action. This allows you to centralize your authorization logic in your authorizer classes, rather than scattering it throughout your controllers.

How does Authority handle exceptions?

Authority raises a ‘SecurityViolation’ exception whenever a user tries to perform an action they’re not authorized for. You can rescue this exception in your controllers and handle it however you like, such as by redirecting the user to a ‘403 Forbidden’ page.

Can I customize the error messages in Authority?

Yes, Authority allows you to customize the error messages it displays when a user tries to perform an unauthorized action. You can do this by overriding the ‘message’ method in your authorizer classes. This method should return a string that will be displayed to the user.

Nathan LongNathan Long
View Author

Nathan is a former journalist stumbled into programming in 2007. He lives in Charlotte, NC with his lovely wife. He has also been a part-time singer/songwriter: see nathanlongmusic.com.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week