views:

144

answers:

1

Throughout our site there's a common model, let's say Video, that has a number of attributes like private/public/pending, downloadable, etc.

Based on the controller, action, request parameters and current user, we want to filter out particular videos from display. For instance, on the homepage we only want to show videos that are public. If there is a logged in user on the homepage we also want to show public downloadable videos.

We also want to make sure that these filters can be applied through sql queries alone, so that using Sphinx we can filter out undesired videos when a user does a search on the site.

Is this best handled through an authorization plugin, such as rails-authorization-plugin? Bottom line our goal is to prevent programmers from accidentally forgetting to filter out particular videos when they add a new action. The solution we're looking for should be very programmatic.

Here's a solution I'm thinking of (haven't written any code yet)

  1. Make use of an authroization plugin or roll our own that allows setting which videos are displayed, defined at the controller or action level.

  2. Create an association extension for any model that has_many(:videos) or has_one(:video), which allows us to overload the finder for videos in the association.

  3. Overload Video.find in a similar manner to restrict what to display based on the current rules.

+1  A: 

Your general strategy seems sound, but I'd strongly suggest you don't override rails default finders. Instead, here's something I've done in the past:

class Video < ActiveRecord::Base
  named_scope :available_to, lambda {|user|
    if user.nil?
      :conditions => {:public_accessible => true}
    elsif user.omnipotent?
      {}
    else 
      :conditions => [%{
        videos.public_accessible = ? OR EXISTS (
          ....boring security stuff linking to users....
        )
      }, true, user.id]
    end 
  }
end

Then in video_shop.rb:

class VideoShop < ActiveRecord::Base
  has_many :videos 
end

Finally, put it all together:

# All videos available to a user
Video.available_to(user)

# All videos in a given shop
video_shop.videos

# All videos in a given shop available to a given user
video_shop.videos.available_to(user)

Note that these work even if user is nil.

tomafro
That's pretty cool.
John Topley