Software development is one of those disciplines where there always tend to be a thousand ways to solve a particular problem. Of those thousand, 990 are usually pretty bad, and any of the remaining ten will serve you pretty well.
Authorization (access to features) in an application, whether it’s a web, desktop, or mobile app, is one of those problems that clearly has one (yes, one) best solution – tackling the problem any other way represents a compromise that you’ll regret somewhere down the road. Use it and you’ll be fine. Don’t use it, and you (or those who have to maintain your code after you move on) will be forever frustrated.
The solution? Generically, ACL. Access Control Language. A concentrated place where you tell your application which users (or classes of users, commonly called roles) have access to which features – where a feature could be a button, a web service, or a REST resource – or anything else in your app.
The important flip side of this coin is perhaps the more important: never (not once!) place a line, not a fragment, of code in the working bits of your application that interrogates a user’s role.
Let’s look at an example. I’m coding up a menu system for a web site, and I want to include an Manage Users menu if the user is an administrator. So it’s straightforward, right? In Ruby’s ERB, I’d write something like this:
<% if current_user.is_administrator %>
<%= link_to ... %>
<% end %>
Perfect, right?
Wrong!!! You just tested the user’s role in code!!!
Why is this so bad? Well, on its own, it’s only mildly offensive. But consider a growing application, where code like this is used to conditionally grant access to links, buttons, resources – to users in five different roles. As the caretaker of the application, things are humming along, when your boss pops his head in your cube and asks “Hey, can you give me a report that shows what types of users have access to features?” (Don’t think that’ll happen? If your business is subject to Sarbanes-Oxley, it will eventually happen. Even if it’s not, someone will eventually want to know.)
Congratulations. You now get to spend the next few hours of your life grep-ing your code to find all the various places where current_user was referenced, and hand-produce a report that will be obsolete tomorrow.
Or, consider the inevitable tiered administrator scenario, where the simple “administrator” role bifurcates into “superadmin” and “admin”, and then, in your software-as-a-service app, you suddenly decide you need “client-admin”. Now you get to churn through your code again, finding all the places you need to add “or” logic to grant features to users in various roles.
Instead, when you’re adding that role-restricted “Manage Users” menu, try something like this:
<% if current_user.can?(:manage_users) %>
<%= link_to ... %>
<% end %>
This cleverly-placed bit of indirection allows you to locate the code that grants access to the feature someplace else. That someplace? Your ACL, where it can live alongside all of its peers in one tidy file (which, by the way, will likely be so readable that you can just hand it to your boss and say “Here’s that access control report, you pointy-headed moron.” (Well, okay, omit that last part if you want to see your application tomorrow.)
That little “can?” method in the code above, incidentally, comes from the CanCan library for Rails. For those of you wanting a more elaborate example, just go watch the Railscast on CanCan gem (which was authored by Ryan Bates, the very man who brings you Railscasts). Even if you’re not a Rails person, you’ll get the concept. It is, after all, quite simple:
NEVER INQUIRE ABOUT THE CURRENT USER’S ROLE IN APPLICATION CODE. EVER.