views:

265

answers:

4

Here's a story:

  • User A should be able to upload an image.
  • User A should be able to set a privacy. ("Public" or "Private").
  • User B should not be able to access "Private" images of User A.

I'm planning to user Paperclip for dealing with uploads.

If I store the images under "RAILS_ROOT/public/images", anyone who could guess the name of the files might access the files. (e.g., accessing http://example.com/public/images/uploads/john/family.png )

I need to show the images using img tags, so I cannot place a file except public.

How can I ensure that images of a user or group is not accessible by others?

(If I cannot achieve this with Paperclip, what is a good solution?)

A: 

I don't know the specifics of how to do this with ROR, as I use PHP.

However what you want to do is make the image file dynamically generated (ie. use htaccess to point the image to a script or something), and only generate the image if the correct user is logged in.

To expand on that: You'd have the actual image files saved away privately somewhere, so that your script can access them whenever necessary, but so that the only way for public users to get at images is through your script. For the htaccess, I would do something like this for my PHP script (adapt accordingly for your ROR stuff):

RewriteRule ^private_images/(.+).(jpg|gif)$ imageScript.php?image=$1$2
Cam
+1  A: 

You may make your rails server output the contents of image files. This is done via a controller action (most of actions print HTML, but this one will print JPG, for example).

Then you may use your authorization system to restrict access on controller level!

class ImagesController
  #Default show Image method streams the file contents.
  #File doesn't have to be in public/ dir
  def show
    send_file @image.filename, :type => @image.content_type,
              :disposition => 'inline'
  end

  # Use your favorite authorization system to restrict access
  filter_access_to :show, :require => :view, :attribute_check => :true
end

In HTML code you may use:

<img src="/images/show/5" />
Pavel Shved
Thanks. This is interesting. I'll look into this.
TK
+1  A: 

I would have Paperclip use S3 on the back-end, set uploaded files to private, and then use "Query String Request Authentication Alternative" to generate the URLs for my image tags.

http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAuthentication.html

jdl
This is a very, very good idea--especially with CloudFront.
Zack
Yes, it works great. Callmeed posted the same thing as me at the same time, but he also shows some code. Take a good look at what he posted and give it serious consideration.
jdl
+3  A: 

Here's how I did this in a similar application.

  • Store your images on Amazon S3 instead of the local file system. Paperclip supports this.
  • Set your :s3_permissions to "private" in your Paperclip options
  • In your Image model, define a method that let's you output an authorized, time-limited url for the image.

Mine looks like this:

def s3_url(style = :original, time_limit = 30.minutes)
  self.attachment.s3.interface.get_link(attachment.s3_bucket.to_s, attachment.path(style), time_limit)
end
  • You can then show images to people only if they're authorized to see them (implement that however you like)–and not have to worry about people guessing/viewing private images. It also keeps them from passing URLs around since they expire (the URL has a token in it).
  • Be warned that it takes time for your app to generate the authorized urls for each image. So, if you have several images on a page, it will affect load time.
Callmeed