class AWS::S3::PresignV4

Utility class for building pre-signed URLs for Amazon S3 objects using signature version 4.

Attributes

client[R]

@return [Client]

object[R]

@return [S3Object]

signer[R]

@return [Core::Signers::Version4]

Public Class Methods

new(object) click to toggle source

@param [S3Object] object

# File lib/aws/s3/presign_v4.rb, line 22
def initialize(object)
  @object = object
  @client = object.client
  @signer = object.client.send(:v4_signer)
end

Public Instance Methods

presign(method, options = {}) click to toggle source

@param (see AWS::S3::S3Object#url_for) @option (see AWS::S3::S3Object#url_for) @return (see AWS::S3::S3Object#url_for)

# File lib/aws/s3/presign_v4.rb, line 40
def presign(method, options = {})

  now = Time.now.utc
  one_week = 60 * 60 * 24 * 7
  if options[:expires] - now.to_i > one_week
    msg = "presigned URLs using sigv4 may not expire more than one week out"
    raise ArgumentError, msg
  end

  now = now.strftime("%Y%m%dT%H%M%SZ")

  request = build_request(method, options)

  request.headers.clear
  request.headers['host'] = request.host
  signed_headers = 'Host'

  if options[:acl]
    request.add_param("x-amz-acl", options[:acl].to_s.gsub(/_/, '-'))
  end

  # must be sent along with the PUT request headers
  if options[:content_md5]
    request.headers['Content-MD5'] = options[:content_md5]
    signed_headers << ';Content-MD5'
  end

  request_params = Core::Signers::S3::QUERY_PARAMS.map do |p|
    param = p.tr("-","_").to_sym
    if options.key?(param)
      request.add_param(p, options[param])
    end
  end

  token = client.credential_provider.session_token

  request.add_param("X-Amz-Algorithm", "AWS4-HMAC-SHA256")
  request.add_param("X-Amz-Date", now)
  request.add_param("X-Amz-SignedHeaders", signed_headers)
  request.add_param("X-Amz-Expires", seconds_away(options[:expires]))
  request.add_param('X-Amz-Security-Token', token) if token
  request.add_param("X-Amz-Credential", signer.credential(now))
  request.add_param("X-Amz-Signature", signature(request, now))

  build_uri(request, options)

end

Private Instance Methods

build_request(method, options) click to toggle source
# File lib/aws/s3/presign_v4.rb, line 90
def build_request(method, options)
  path_style = object.config.s3_force_path_style
  params = options.merge(
    :bucket_name => object.bucket.name,
    :key => object.key,
    :data => ''
  )
  req = client.send(:build_request, operation_name(method), params)
  req.force_path_style = options.fetch(:force_path_style, path_style)
  req
end
build_uri(request, options) click to toggle source
# File lib/aws/s3/presign_v4.rb, line 120
def build_uri(request, options)
  uri_class = options[:secure] ? URI::HTTPS : URI::HTTP
  uri_class.build(
    :host => request.host,
    :port => request.port,
    :path => request.path,
    :query => request.querystring
  )
end
operation_name(method) click to toggle source
# File lib/aws/s3/presign_v4.rb, line 102
def operation_name(method)
  case method
  when :get, :read then :get_object
  when :put, :write then :put_object
  when :delete then :delete_object
  when :head then :head_object
  else
    msg = "invalid method, expected :get, :put or :delete, got "
    msg << method.inspect
    raise ArgumentError msg
  end
end
seconds_away(expires) click to toggle source
# File lib/aws/s3/presign_v4.rb, line 130
def seconds_away(expires)
  expires - Time.now.to_i
end
signature(request, datetime) click to toggle source
# File lib/aws/s3/presign_v4.rb, line 115
def signature(request, datetime)
  key = signer.derive_key(datetime)
  signer.signature(request, key, datetime, 'UNSIGNED-PAYLOAD')
end