Skip to content

Instantly share code, notes, and snippets.

@stoffie
Created December 15, 2015 11:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stoffie/7cd6eb4d61dc79b0e842 to your computer and use it in GitHub Desktop.
Save stoffie/7cd6eb4d61dc79b0e842 to your computer and use it in GitHub Desktop.

Authorization in ruby on rails


Web framework

In a web framework, when we handle a request in our application, we always do the same things, every time:

  • Authenticate the user
  • Authorize the request
  • Validate the request
  • Handle business logic
  • Persist the request
  • Handle HTTP overhead
  • Render some response

What is Authorization?

The authorization function determines whether a particular entity is authorized to perform a given activity.

For our function to make the right decision, there are three things to consider:

  • Who is the current_user
  • What controller/method is being authorized
  • What data the request is going to manipulate

Authorization (anti-)patterns


Authorization in the model

class Post < ActiveRecord::Base
  attr_accessor :current_user
  validate :current_user_is_admin
  def current_user_is_admin
    unless current_user.role == 'admin'
      errors.add(:current_user, 'must be an admin')
    end
  end
end
  • You can no longer save Post models outside of a web form. (tests, console, background jobs, etc.)
  • Fat models, no separation of concerns

Authorization using before_action

before_action :redirect_non_admin, only: [:flip_paid]
before_action only: [:show, :edit, :update, :destroy] do
  redirect_non_owner Order.find(params[:id])
end
before_action only: [:show, :edit, :update, :destroy] do
  redirect_different_organization Order.find(params[:id])
end
  • Produce spaghetti code very easily
  • Hard to test
  • Queries the database multiple times

Authorization everywhere

def update
  user = User.find(params[:id])
  unless user.supervisor == current_user
    raise "Trying to edit unauthorized user!"
  end
  if params[:user][:role] == 'admin'
    raise "Trying to set unauthorized value!"
  end
  user.update_attributes(params[:user])
end
  • Authorization logic is spread throughout the application
  • Crazy to test
  • Changing authorization rules means reviewing all code and tests

Authorization as a separate layer

def update
  post = Post.find(params[:id])
  authorize! current_user, post, :update
  post.update_attributes(params[:user])
end
  • Controllers don't know about authorization, but they enforce it
  • Authorization logic is grouped in one single place
  • Can use helpers in the views
  • Testable
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment