Reputation: 667
I'm upgrading my rails 5 app to rails 6. During this update, I updated my aws-sdk from v1 to v3. I have included the aws-sdk-s3
gem in my gemfile as mentioned in the documentation.
One of the main features of my app is allowing users to upload songs. When I've tried to manually test this feature I've encountered the following error:
PUT https://bucketname.s3.amazonaws.com/ 400 (Bad Request)
Here is the attached XML:
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>request id code</RequestId>
<HostId>host id code</HostId>
</Error>
I was trying to check how to solve it in this S3 documentation but there I found another confusing detail. While my console returned a 400 status code, 'AccessDenied' is actually written as 403 status code in the documentation.
I'm attaching the Upload model. The commented out lines are the original lines of code I had. I changed it according to what was mentioned on the S3 documentation.
class Upload
attr_reader :filename
# New lines of code: ####################################################
require 'aws-sdk-s3'
Aws.config.update({
region: 'us-east-1',
credentials: Aws::Credentials.new(Rails.application.credentials.dig(:aws, :access_key_id), Rails.application.credentials.dig(:aws, :secret_access_key)),
})
#########################################################################
def initialize(filename)
@filename = filename
end
def url
#@url ||= bucket_file.url_for(:write, content_type: content_type, acl: :public_read).to_s
@url ||= bucket_file.url().to_s
end
def content_type
@content_type ||= MIME::Types.type_for(filename).first.content_type
end
def to_json(*args)
{ url: url, content_type: content_type }.to_json
end
private
def bucket_file
#@bucket_file ||= bucket.object("uploads/#{SecureRandom.uuid}/#{filename}")
@bucket_file ||= bucket.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public_read', content_type: content_type)
end
def bucket
#@bucket ||= AWS::S3.new.buckets(ENV['S3_BUCKET_NAME'])
@bucket ||= Aws::S3::Resource.new(region: 'us-east-1').bucket(Rails.application.credentials.dig(:aws, :bucket))
end
end
It is important to mention that I made sure that the credentials are accessible and valid.
Upvotes: 1
Views: 1415
Reputation: 23
Dealt with a similar problem as you described.
Note that despite the fact that you fixed up this problem, you will need to make further adjustment before moving to production environment as production env enforces a more thight csrf token regulations.
There is a further explanation here: https://docs.aws.amazon.com/AmazonS3/latest/API/archive-RESTObjectPUT.html
Upvotes: 1
Reputation: 667
The url_for
function is deprecated and therefore had to be change to public_url
.
This is a working version of my original code:
class Upload
attr_reader :filename
require 'aws-sdk-s3'
Aws.config.update({
region: 'us-east-1',
credentials: Aws::Credentials.new(Rails.application.credentials.dig(:aws, :access_key_id), Rails.application.credentials.dig(:aws, :secret_access_key)),
})
def initialize(filename)
@filename = filename
end
def url
@url ||= bucket_file.public_url().to_s
end
def content_type
@content_type ||= MIME::Types.type_for(filename).first.content_type
end
def to_json(*args)
{ url: url, content_type: content_type }.to_json
end
private
def bucket_file
@bucket_file ||= bucket.object("uploads/#{SecureRandom.uuid}/${filename}")
end
def bucket
@bucket ||= Aws::S3::Resource.new(region: 'us-east-1').bucket(Rails.application.credentials.dig(:aws, :bucket))
end
end
Upvotes: 3