Authorization in Rails
Not everyone is clear on the distinction between "authentication" and "authorization". Basically authentication manages a list of users and their sessions, handles the login process by checking credentials (e.g. password), and registration. You can then restrict access to areas of your site based on whether a user is logged in or not. For Ruby on Rails applications, you can get authentication with the ActsAsAuthenticated ("AAA") or RestfulAuthentication plug-ins (among others), or build your own.
Authorization adds granularity. In addition to simply asking if you're logged in, different users can have different permissions , or "roles". Thus normal Users may be able to post comments, Moderators may be able to edit/delete comments, and Administrators may have full admin capability such as the ability to modify/remove users.
Here is a survey of a number of authorization add-ons available.
Super Simple Authorization
I recently watched the RailsCast #20 "Restricting Access" and decided to add it to this article. So here it is, at the top of our list.
If you have a User table (using AAA or RA) you can extend the table with an "admin" attribute, and then query current_user.admin? to test for authorization. Simple enough. You could extend this idea by having different booleans for editor, moderator and other roles you may require.
The screencast goes even simpler. If you really only want to restrict access to specific admin functions on an otherwise public site, you may not even need a Users table and registration at all. Instead your login page just asks the user for a password, which you store in the current session. Then you use an admin? method (to the application controller) which compares this against a hardcoded password.
"Authorizing Users with Roles", Recipe 32 in Rails Recipes by Chad Fowler. (Published June 2006).
Builds from an existing User model, as described in Recipe 31 "Authenticating Your Users", or generated with AAA.
This recipe builds two new models for Roles and Rights, such that Users have Roles, Roles have Rights: (HABTM = has_and_belongs_to_many)
- User HABTM roles
- Role HABTM users
- Role HABTM rights
- Right HABTM roles
A right specifies a Controller name and Action name. Add a before_filter in your controllers to check_authorization, which determines whether the current user has rights to the incoming action call. If not he's redirected to an error page.
Simple enough, and bare bones. The implementation is not RESTful but that wouldn't be hard to change. More complexity is left as an exercise to the reader.
The simple_access_control plugin is basically the same as described above by Chad Fowler, offered in a plug-in, if you prefer. Tests are provided.
(Revision 146, February 2006, very low activity, no forum)
Like the aforementioned, the acl_system2 plugin provides simple declarative authorization for protecting controller/actions using roles. It provides a flexible permissions string parser that appears quite convenient. For example, a controller can declare:
access_control [:new, :create, :update, :edit] => '(admin | user | moderator)',
:delete => 'admin'
while allows users, moderators, and admins to create and update items, but only admins to delete items. You can specify callback methods for redirecting or doing other stuff (e.g. setting flash messages) when access is permitted or denied. A helper method, restrict_to, is provided for views, e.g.
<% restrict_to "(admin | moderator) & !blacklist" do %>
<%= link_to "Admin & Moderator only link", :action =>'foo' %>
<% end %>
(Release 0.2.1, December 2006, very low activity)
ActiveACL (as in active control lists) is based on PhpGacl and claims to be highly scalable, having optimized database access and caching. Permissions range from simple (controller/action) to object level, are assigned at runtime, and may be grouped and inherited.
To me, the documentation and implementation seems overly complicated and confusing. ActiveACL creates and manages ten (10) tables in the database. The phpgacl manual is helpful though. Here's how I think it works (mapping their terminology to more common words):
Let's say we group our users ("requesters") into roles ("groups"). Each user may or may not access different objects ("targets") on the site. Targets can be models, individual objects in a model, controllers, and individual actions in a controller.
ActiveACL adds another table, called Section which is simply a way of categorizing access objects for the user interface (e.g. "Rooms", "Floors", "People") but have nothing to do with actual groups in the tree. (This seems to add unnecessary complexity because our models most likely already have attributes for organizing large data sets).
ActiveACL can be used not just for managing user groups/roles, but other application domains like server permissions, or maybe even modeling of engineering constraints.
(Version 1.0, September, 2006, low activity)
The Authorization plug-in provides role based authorization, along with a number of Railsey sugar like dynamic methods and a little grammer parser that potentially enhances the programming experience and readability of your code (a la domain specific languages, DSL). It is compatible with ActsAsAuthenticated plugin out of the box. Documentation is comprehensible, and good test coverage is provided.
User objects just need to implement a has_role? method, using the provided acts_as_authorized_user. And models then use acts_as_authorizable.
Roles can be authorized for the entire application, a model class, or a specific object. The plugin provides a way of checking authorization at the class or instance method level using permit and permit? methods. It also provides english-like dynamic methods like "user.is_manager_of project" (where "user" acts as authorized, "manager" is a role, and "project" is an authorizable model). You can specify how control is redirected if authorization is denied.
Roles are set by has_role and accepts_role methods, with optional scope (such as user.has_role 'site_admin' , or user.has_role 'moderator', group). Models set roles for specific users (such as project.accepts_role 'manager', user). Then, dynamic methods let you say things like user.is_manager_of? project
(Version 0.2.0, April 2007, active)
The Goldberg plugin is an example of a high level generator that includes both authorization and authentication. It's like a framework built on top of the Rails framework, but actually sits beside it because you continue to develop your Rails app as usual, Goldberg does not interfere. You don't have to write any Goldberg-specific code in your application or use its API.
You define Roles (which can be has subgroup hierarchy), and assign permissions to the roles. A permssion is a controller/action that the role may access. Then you assign users to roles.
Goldberg installs as application-wide before-filters (if not exactly, at least conceptually) that examines the routed controller/action against its database of permissions ("credentials") for the current logged in user. Credentials are stored in the current session minimizing the need for database access between page loads.
That's just the beginning. Goldberg also provides a little hierarchical page manager (approaching a CMS) for static content; a menu manager for redirecting to pages or any arbitrary controller/action; and a theme system for dynamically re-skinning your application. It does not impose a specific markup on you, however. Finally there is an API for custom access control within your application.
Defense in Depth -- Model Level Security
(Release 0.0.9 November, 2005, inactive)
Second level of defense. You still use your views to hide links to unauthorized actions, and set before_filter's in controllers to prevent unauthorized action methods from executing. But should someone get through, the model can also provide security.
The ModelSecurity gem is a generator. It provides for example let_read, let_write, and let_access added to model declarations, allow fine grained permissions to a whole model or separate attributes.
Includes a user model and UserSuport mixin. (Seems to been written before AAA so integration with AAA requires investigation).
Includes a ModelSecurityHelper which makes fields conform to the model security settings (eg makes form fields hidden or read-only).
Looks like a well thought-out generalized solution. My concern is the performance overhead this generalization requires, whether it plays well with various other helpers, AJAX, etc. Maybe its too scaffold oriented, and simply just overkill.
If your requirements are pretty simple, just a few roles like Visitor, User, and Admin, and don't need a lot of granularity (just control access to specific controller/actions) then you could use the convenient acl_system2, or just roll your own.
If you're looking for a high level, well designed tool to build fairly conventional sites quickly in Rails, then Goldberg is a good shot. It has a lot of commonly needed features built in. Why rebuild it yourself each time?
On the other hand, if you're building an application with more complex authorization requirements, the Authorization plugin seems a very good bet. For most people it may be overkill. But any kind of groupware or social networking site may benefit from this one.