= FileAuthorization
Use this plugin if you want to fine-tune permissions on some or all the files
in your +public+ directory.
Normally these files are served by the webserver before Rails ever gets a
chance to see the request. This plugin tells the webserver to pass certain
file requests on to Rails, where a +before_filter+ intercepts them and passes
them to a callback you provide to determine whether you would like to serve
them or not. It provides a handy controller method that lets you serve the
file efficiently, complete with flexible cache-control support.
== Example
You can register the files you are interested in anywhere, really, but it
probably makes the most sense to put it in
<tt>app/controllers/application.rb</tt>, together with the
<tt>before_filter</tt>, so that it's all in one place:
class ApplicationController < ActionController::Base
# Make sure we've had a chance to login the user, first, if we support
# "remember-me" style login via cookies.
before_filter :autologin
# Now intercept file requests and evaluate permissions.
before_filter :file_permissions
# Simplest example: just check if the current user has permission to see
# a given file. It serves the file automatically if you return true,
# otherwise it renders /public/403.html (this is not supplied with Rails).
FilePermissions.register("/public/images/user") do |controller, file|
controller.session[:user].has_permission_to_view_image(file)
end
# You may customize things very easily: This time we look up the Image
# object associated with our images, query a database to see if the current
# user has access. If they do, render it, but instruct the browser to keep
# the image in a private cache, and tell it to revalidate once a day. If
# the user does not have access, render the "access_denied" view.
FilePermissions.register(/public.images.user.(\d+).jpg$/) do |controller, file, match|
if (img = Image.find(match[1])) &&
img.can_user_see_me?(controller.session[:user])
controller.render_file(file,
:private => true,
:max_age => 1.day,
:must_revalidate => true
)
else
controller.render(:action => "access_denied")
end
end
end
== Limitations
Currently it only works with Mongrel. But it should be relatively easy to hack
Webrick or any other Ruby webservers the same way we've done Mongrel.
You will need to make sure there is a route defined for the files you are
protecting. I recommend something like what's shown below to capture
everything under a given directory. Remember, it doesn't actually ever reach
the bogus action. In fact, even the controller is unimportant if you place
<tt>before_filter :file_permissions</tt> in ApplicationController.
ActionController::Routing::Routes.draw do |map|
# Route everything under /images to "anycontroller".
map.connect "/images/*file", :controller => "anycontroller", :action => "bogus"
end
If you are running Mongrel under Apache, then you will further need to instruct
Apache to pass these files. Typically this is done in the configuration file
using ProxyPass directives. For example, a typical configuration might look
like this:
<VirtualHost *:80>
ServerName yourdomain.com
DocumentRoot "/usr/local/rails/yourdomain"
SetEnv RAILS_ENV production
<Directory /usr/local/rails/yourdomain>
Options FollowSymLinks
AllowOverride AuthConfig Limit
Order allow,deny
Allow from all
</Directory>
# Tell Apache to pass requests for user images on to Rails.
ProxyPass /images/user balancer://mongrel_cluster/
# Everything else in public is okay to serve up as-is.
ProxyPass /images !
ProxyPass /stylesheets !
ProxyPass /javascripts !
ProxyPass /robots.txt !
ProxyPass /favicon.ico !
# Everything else goes to Rails.
ProxyPass / balancer://mongrel_cluster/
ProxyPreserveHost on
<Proxy balancer://mongrel_cluster>
BalancerMember http://127.0.0.1:3000
BalancerMember http://127.0.0.1:3001
BalancerMember http://127.0.0.1:3002
</Proxy>
RewriteEngine on
etc...
</VirtualHost>
Author:: Kamen Bonov
License:: Free - do whatever you want to with it.
Last Update:: July 30, 2010