Welcome to Giant Robots Smashing Into Other Giant Robots — a weblog about development, business, design and technology — written by thoughtbot.

Did you order the code red?!

On Fridays, we enjoy getting together for lunch and reviewing code which needs tender loving care. This week, our liveliest discussion surrounded responsibility for authorization logic.

The problem ~ where does the authorization logic go?

You’ve come across it before ~ you have a User model and a second model for which Users must be authorized to perform certain tasks:

1
2
3
4
5
6
7
8
9
10
  def show
    @event = Event.find params[:id]
    unless (logged_in? &&
            (@event.members.include?(current_user) ||
            @event.organizer.include?(current_user)) || 
            current_user.admin?)
      flash[:failure] = "You are not authorized to view this event."
      redirect_to home_url and return
    end
  end

In discussion, we decided there is too much logic in the controller. We want to move the authorization into the correct place in our domain model. But where?


unless current_user.authorized_for_event?(@event)

That reads nicely in English as “Unless the current user is authorized for the event” but logically, a User should not be responsible for its own authorization. If that were the case in the real world, we’d have societal chaos:

Kaffee: Did you order the code red?!
Col. Jessep: You’re goddamn right I did!
Tom Cruise in a Few Good Men

The solution ~ the non-User model authorizes the User

Whether your domain model has an Event or a CodeRed, the authorization logic should be on those models, not on User. In just about every case I can think of, that will remain true.

Yawkey Way

For example, I have a ticket to a Red Sox playoff game. When I arrive on Yawkey Way, the ticket-taker will authorize me to enter the event. He or she is the Controller in our MVC pattern (and I suppose the View is the world of sight, touch, taste, sound, and smell…). The ticket-taker doesn’t need to know that I paid with a MasterCard on September 20th or that my seat is in right field or any other details. All that matters is: “is this guy authorized?”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  def attend
    @game = RedSoxGame.find_by_id params[:id]
    unless @game.authorized?(current_user)
      flash[:failure] = "You are not authorized for this game. 
         Watch it over a pint of Sam Adams at the Cask n' Flagon."
      redirect_to home_url and return
    end
  end

  class RedSoxGame < ActiveRecord::Base
    
    def authorized?(user)
      return false if user.nil?
      self.doors_open? && self.not_rained_out? 
        && user.ticket?(self) && user.not_drunk?
    end

  end

The authorized method is noteworthy for two reasons. First, it uses the Composed Method:

Divide your program into methods that perform one identifiable task. Keep all of the operations in a method at the same level of abstraction. This will naturally result in programs with many small methods, each a few lines long.

The authorized? method is very easy to read because it focuses on simply authorizing the user for the game. It relies on a number of other methods whose purpose is clear and are similarly of narrow focus.

The second thing to note is that authorized? actually calls methods on the User model. It may seem contradictory to call User after saying that authorization logic does not belongs there. However, it isn’t authorization logic, it’s another level of abstraction.

Most importantly, the ticket-taker will let me into the Sox game and no one gets a code red.


About this entry

 

thoughtbot is hiring

We are hiring web developers and web designers in both Boston and New York, NY.

What are we up to?

We built Shoulda, an eclectic set of additions to Test::Unit; Paperclip to manage uploaded files without hassle; Jester, a REST/ActiveResource client library written in Javascript, and Squirrel, an enhancement for ActiveRecord's find syntax; — amongst some other projects.


Chad (President) and Jon (CTO) co-authored a technical book titled Pro Active Record: Databases with Ruby and Rails, which explores the ins and outs of the ActiveRecord ruby library. You can buy it today at Amazon.com.

About thoughtbot, inc.

We are a small web application development consulting business, with offices in Boston, MA and New York, NY. If you're looking to find a team for your next web development project or your new web application — get in touch.