Rails Tip: Always Scope Your Finders
written by Steven on April 10, 2008
It is easy to open a security hole in your Rails application. Fortunately, by scoping your finders, it is also easy to write your code without opening it. Here is an example. Let's say you have a expense tracking application and the url is /expenses/151. Obviously this calls the expenses controller with a params[:id] = 151.
#bad:
def show
@expense = Expenses.find(params[:id])
end
#good:
#@user is the logged on user.
def show
@expense = @user.expenses.find(params[:id])
end
The scoped finders actually add the proper where clause to the sql. It happens automatically. Without scoping the expense finder, anyone can see anyone else's data. Generally you will want to set this up as a before filter. This also works for nested routes. Let's say the url is /invoices/25/line_items/87:
class LineItemsController < ApplicationController
before_filter :setup
#snip many lines
protected
def setup
@invoice = @user.invoices.find(params[:invoice_id]) unless params[:invoice_id].blank?
@line_item = @invoice.blank? ? @user.line_items.find(params[:id]) : @invoices.line_items.find(params[:id])
end
end
You don't have to use the @user variable. In Less Accounting we use sub-domains for each business. Since each business may have several users, all the controllers are scoped around the @business variable, which is determined by the sub-domain of the url. The @business variable itself is scoped by the @user variable.
Leave a Comment

Steven Bristol has written code for the past 20 years. He like green vegetables and kittens, oh and butterflies too. He loves to throw ninja stars at his enemies.

2 Comments
Perhaps a bit verbose, but I like the result, especially if it can be reused for a couple of controllers …
http://pastie.caboo.se/179238
@Lourens
As always, very good. I will have to mull this over for a few days to decide if it’s to far from KISS, but I love the passion behind it. You always give me something to think about. You are a rock star.
steve