Search code examples
ruby-on-railsamazon-s3carrierwavefog

How to protect one carrierwave image version from public access on Amazon S3


I have an S3 backed image model, using carrierwave and fog, I'm processing the images with rmagick and uploading different versions to Amazon S3, the original image and two smaller versions. I would like to have the two smaller versions publicly accessible, but the original versions protected, like stored in a different folder on S3, with a different access or something like so that I can get them from my application backend, but not accessible to the public, only the other two versions should be.

I think this resumes to two questions: Configure carrierwave to save image versions on different places on the bucket, or even different buckets. Configure carrierwave/fog with different accesses to be used according to the image versions being requested, so that on my application front end i could use model.image_url(:small) and on another place just do model.image_url to retrieve the original one, using a different access.

Do you have any other ideas about this?


Solution

  • Ok, so i managed to accomplish this:

    I created a policy for my bucket stating that certain folders will be public, which are those of the smaller versions, and the rest will be private, and configured carrierwave to store the images in different paths.

    First the configuration on carrierwave, the base store_dir (were it will save the base version) , you can change the path as you like.

    def store_dir
      "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}/public"
    end
    

    And inside the version blocks i override the store_dir method like this:

    version :thumb do
      process :resize_to_fill => [200, 200]
      process :optimize
    
      def store_dir
        "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}/private"
      end
    
    end
    

    This way you'll end up with something like this for the base version:

    /uploads/model/attribute/id/private/000564cf-c7e9-3d7b-bf7f-48dd-1910667-e0bbd8b3.jpg
    

    and this for each of the other versions you override its base store_dir:

    /uploads/model/attribute/id/public/thumb_000564cf-c7e9-3d7b-bf7f-48dd-1910667-e0bbd8b3.jpg
    /uploads/model/attribute/id/public/display_000564cf-c7e9-3d7b-bf7f-48dd-1910667-e0bbd8b3.jpg
    

    After this you define a policy on your amazon s3 bucket, giving public access to your public folder, using wildcards across the path.

    {
       "Version": "2008-10-17",
        "Statement": [
            {
            "Sid": "AllowPublicRead",
            "Effect": "Allow",
            "Principal": {
            "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::BUCKETNAME/uploads/*/*/*/public/*"
            }
        ]
    }
    

    Hope it helps others with the same requirement.