diff --git a/lib/ruby-manta.rb b/lib/ruby-manta.rb index df25500..f4dce9a 100644 --- a/lib/ruby-manta.rb +++ b/lib/ruby-manta.rb @@ -650,11 +650,11 @@ def list_jobs(state, opts = {}) # Generates a signed URL which can be used by unauthenticated users to # make a request to Manta at the given path. This is typically used to GET - # an object. + # an object, or to make a CORS preflighted PUT request. # # expires is a Time object or integer representing time after epoch; this - # determines how long the signed URL will be valid for. The method is the HTTP - # method (:get, :put, :post, :delete) the signed URL is allowed to be used + # determines how long the signed URL will be valid for. The method is either a single HTTP + # method (:get, :put, :post, :delete, :options) or a list of such methods that the signed URL is allowed to be used # for. The path must start with //stor. Lastly, the optional args is an # array containing pairs of query args that will be appended at the end of # the URL. @@ -662,7 +662,8 @@ def list_jobs(state, opts = {}) # The returned URL is signed, and can be used either over HTTP or HTTPS until # it reaches the expiry date. def gen_signed_url(expires, method, path, args=[]) - raise ArgumentError unless [:get, :put, :post, :delete].include? method + methods = method.is_a?(Array) ? method : [method] + raise ArgumentError unless (methods - [:get, :put, :post, :delete, :options]).empty? raise ArgumentError unless path =~ OBJ_PATH_REGEX key_id = '/%s/keys/%s' % [@user, @fingerprint] @@ -671,15 +672,17 @@ def gen_signed_url(expires, method, path, args=[]) args.push([ 'algorithm', @digest_name ]) args.push([ 'keyId', key_id ]) + method = methods.map {|m| m.to_s.upcase }.sort.join(",") + host = URI.encode(@host.split('/').last) + path = URI.encode(path) + + args.push(['method', method]) if methods.count > 1 + encoded_args = args.sort.map do |key, val| # to comply with RFC 3986 CGI.escape(key.to_s) + '=' + CGI.escape(val.to_s) end.join('&') - method = method.to_s.upcase - host = URI.encode(@host.split('/').last) - path = URI.encode(path) - plaintext = "#{method}\n#{host}\n#{path}\n#{encoded_args}" signature = @priv_key.sign(@digest, plaintext) encoded_signature = CGI.escape(Base64.strict_encode64(signature)) diff --git a/tests/test_ruby-manta.rb b/tests/test_ruby-manta.rb index 4c5e9c3..ac9b78a 100644 --- a/tests/test_ruby-manta.rb +++ b/tests/test_ruby-manta.rb @@ -292,12 +292,25 @@ def test_cors def test_signed_urls - @@client.put_object(@@test_dir_path + '/obj1', 'foo-data') + + client = HTTPClient.new + + put_url = @@client.gen_signed_url(Time.now + 500000, [:put, :options], + @@test_dir_path + '/obj1') + + result = client.options("https://" + put_url, { + 'Access-Control-Request-Headers' => 'access-control-allow-origin, accept, content-type', + 'Access-Control-Request-Method' => 'PUT' + }) + + assert_equal result.status, 200 + + result = client.put("https://" + put_url, 'foo-data', { 'Content-Type' => 'text/plain' }) + assert_equal result.status, 204 url = @@client.gen_signed_url(Time.now + 500000, :get, @@test_dir_path + '/obj1') - client = HTTPClient.new result = client.get('http://' + url) assert_equal result.body, 'foo-data' end