1

I'm working on a Rails app and using the restful-authentication plugin. The app will include user images. Only users who log in will be able to browse, but I wondered if I can also restrict direct URL access to the images to only allow logged-in users.

In other words, even if you know that an image is at thesite.com/user1/foo.jpg, you can't see that image unless you're logged in. I know this could theoretically be done in the Apache config, but the only way I know would involve manual editing of those files, which wouldn't scale.

Is this possible? Would it make more sense to just move the photos out of the public directory? (I'm not sure if that has any performance implications, as public is generally used for static content.)

Nathan Long
  • 122,748
  • 97
  • 336
  • 451
  • 1
    You'd have to send the images throw the controller like David Antaramian said, which, depending on your userbase, could be a performance bottleneck. May want to try to do the image filtering higher up at rack layer (http://guides.rubyonrails.org/rails_on_rack.html) or even higher up. – clyfe Mar 06 '10 at 19:58
  • @clyfe - why might this be a performance bottleneck? Because rails has to get involved with processing the binary data instead of just letting Apache handle it? – Nathan Long Mar 06 '10 at 22:53
  • Yes, Apache will be faster than ruby/Rails. – Eric Sep 20 '10 at 18:31

2 Answers2

3

You could create a controller action for images that just returns the binary data to the user (from a source not publicly accessible) only if the user is logged in. As a preface to this, I'm a Django/Python person, not Ruby/Rails, but from what I can tell, the configuration would look like this:

map.connect ':user/:picture', :controller => 'accounts', :action => 'displayPic'

and your actual controller would look something like:

class AccountsController < ApplicationController
    def image
        # check if user is logged in, then
        user = Account.find(params[:user])
        image = get_image(user, params[:picture])
        send_data(image,
             :filename => "#{params[:picture]}",
             :type => "image/jpeg")
    end

private

    def get_image(image_name)
        # do something to find image
        # and return the binary data
        # to the action
    end
end

Made this a community wiki so anyone with better Ruby/Rails skills can clean it up

David Antaramian
  • 4,145
  • 1
  • 23
  • 16
2

Just require a user for the action:

# images_controller.rb
ImagesController < ApplicationController

  before_filter :require_user, :only => 'show'

  def require_user
    unless current_user
      redirect_to root_url # or :back, or wherever
    end
  end

end

However, do you really mean direct access to the image not via http://yoursite.com/userfoo/bar.jpg, but via http://yoursite.com/images/system/file/5837/bar.jpg? Within rails, you could check the request object:

# images_controller.rb
ImagesController < ApplicationController

  before_filter :require_onsite, :only => 'show'

  def require_onsite
    # pseudoruby, might need some massage
    unless request.referer.match(/request.domain/) 
      redirect_to root_url
    end
  end

end
Eric
  • 2,539
  • 18
  • 23
  • If the URL is http://yoursite.com/images/system/file/5837/bar.jpg you are saying to check the request object. Does this work in Ruby on Rails 3? I couldn't find a reference. Thanks – DDDD Jan 15 '14 at 21:43
  • See here: http://stackoverflow.com/questions/2165665/how-do-i-get-the-current-url-in-ruby-on-rails – Eric Jan 15 '14 at 21:59