module Aws module S3 # Allows you to create presigned URLs for S3 operations. # # Example Use: # # signer = Aws::S3::Presigner.new # url = signer.presigned_url(:get_object, bucket: "bucket", key: "key") # class Presigner # @api private ONE_WEEK = 60 * 60 * 24 * 7 # @api private FIFTEEN_MINUTES = 60 * 15 # @option options [Client] :client Optionally provide an existing # S3 client def initialize(options = {}) @client = options[:client] || Aws::S3::Client.new end # @param [Symbol] method Symbolized method name of the operation you want # to presign. # # @option params [Integer] :expires_in (900) The number of seconds # before the presigned URL expires. Defaults to 15 minutes. # # @option params [Boolean] :secure (true) When `false`, a HTTP URL # is returned instead of the default HTTPS URL. # # @option params [Boolean] :virtual_host (false) When `true`, the # bucket name will be used as the hostname. This will cause # the returned URL to be 'http' and not 'https'. # # @raise [ArgumentError] Raises an ArgumentError if `:expires_in` # exceeds one week. # def presigned_url(method, params = {}) if params[:key].nil? or params[:key] == '' raise ArgumentError, ":key must not be blank" end virtual_host = !!params.delete(:virtual_host) scheme = http_scheme(params, virtual_host) req = @client.build_request(method, params) use_bucket_as_hostname(req) if virtual_host sign_but_dont_send(req, expires_in(params), scheme) req.send_request.data end private def http_scheme(params, virtual_host) if params.delete(:secure) == false || virtual_host 'http' else @client.config.endpoint.scheme end end def expires_in(params) if expires_in = params.delete(:expires_in) if expires_in > ONE_WEEK msg = "expires_in value of #{expires_in} exceeds one-week maximum" raise ArgumentError, msg end expires_in else FIFTEEN_MINUTES end end def use_bucket_as_hostname(req) req.handlers.remove(Plugins::S3BucketDns::Handler) req.handle do |context| uri = context.http_request.endpoint uri.host = context.params[:bucket] uri.path.sub!("/#{context.params[:bucket]}", '') uri.scheme = 'http' uri.port = 80 @handler.call(context) end end def sign_but_dont_send(req, expires_in, scheme) req.handlers.remove(Plugins::S3RequestSigner::SigningHandler) req.handlers.remove(Seahorse::Client::Plugins::ContentLength::Handler) req.handle(step: :send) do |context| if scheme != context.http_request.endpoint.scheme endpoint = context.http_request.endpoint.dup endpoint.scheme = scheme endpoint.port = (scheme == 'http' ? 80 : 443) context.http_request.endpoint = URI.parse(endpoint.to_s) end signer = Signers::V4.new( context.config.credentials, 's3', context.config.region ) url = signer.presigned_url( context.http_request, expires_in: expires_in, body_digest: "UNSIGNED-PAYLOAD" ) Seahorse::Client::Response.new(context: context, data: url) end end end end end