Nathan Long
Nathan Long

Reputation: 126132

Rails - restrict URL access to images?

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.)

Upvotes: 1

Views: 1990

Answers (2)

Eric
Eric

Reputation: 2549

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

Upvotes: 2

David Antaramian
David Antaramian

Reputation: 4173

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

Upvotes: 3

Related Questions