Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using S3 Presigned-URL for upload a file that will then have public-read access

I am using Ruby on Rails and AWS gem. I can get pre-signed URL for upload and download. But when I get the URL there is no file, and so setting acl to 'public-read' on the download-url doesn't work.

Use case is this: 1, server provides the user a path to upload content to my bucket that is not readable without credentials. 2, And that content needs to be public later: readable by anyone.

To clarify: I am not uploading the file, I am providing URL for my users to upload. At that time, I also want to give the user a URL that is readable by the public. It seems like it would be easier if I uploaded the file by myself. Also, read URL needs to never expire.

like image 418
CoderStix Avatar asked Apr 13 '15 22:04

CoderStix


People also ask

What is Presigned URL S3 upload?

A presigned URL is a URL that you can provide to your users to grant temporary access to a specific S3 object. Using the URL, a user can either READ the object or WRITE an Object (or update an existing object). The URL contains specific parameters which are set by your application.

When should I use Presigned URL S3?

Pre-signed URLs are used to provide short-term access to a private object in your S3 bucket. They work by appending an AWS Access Key, expiration time, and Sigv4 signature as query parameters to the S3 object. There are two common use cases when you may want to use them: Simple, occasional sharing of private files.

Is Presigned URL public?

By default, all S3 objects are private. Only the object owner has permission to access them. However, the object owner can optionally share objects with others by creating a presigned URL, using their own security credentials, to grant time-limited permission to download the objects.


1 Answers

When you generate a pre-signed URL for a PUT object request, you can specify the key and the ACL the uploader must use. If I wanted the user to upload an objet to my bucket with the key "files/hello.txt" and the file should be publicly readable, I can do the following:

s3 = Aws::S3::Resource.new
obj = s3.bucket('bucket-name').object('files/hello.text')

put_url = obj.presigned_url(:put, acl: 'public-read', expires_in: 3600 * 24)
#=> "https://bucket-name.s3.amazonaws.com/files/hello.text?X-Amz-..."

obj.public_url
#=> "https://bucket-name.s3.amazonaws.com/files/hello.text"

I can give the put_url to someone else. This URL will allow them to PUT an object to the URL. It has the following conditions:

  • The PUT request must be made within the given expiration. In the example above I specified 24 hours. The :expires_in option may not exceed 1 week.
  • The PUT request must specify the HTTP header of 'x-amz-acl' with the value of 'public-read'.

Using the put_url, I can upload any an object using Ruby's Net::HTTP:

require 'net/http'

uri = URI.parse(put_url)

request = Net::HTTP::Put.new(uri.request_uri, 'x-amz-acl' => 'public-read')
request.body = 'Hello World!'

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true   
resp = http.request(request)

Now the object has been uploaded by someone else, I can make a vanilla GET request to the #public_url. This could be done by a browser, curl, wget, etc.

like image 51
Trevor Rowe Avatar answered Sep 22 '22 06:09

Trevor Rowe