Browse files

Merge pull request #7 from chmhofer/preflighted_request_signing

Support URL signing for multiple methods
  • Loading branch information...
2 parents 673715e + 89ef52f commit d63d5b426a48100f27ab622d4f11c6be4c159576 @Marsell Marsell committed Oct 23, 2013
Showing with 26 additions and 10 deletions.
  1. +11 −8 lib/ruby-manta.rb
  2. +15 −2 tests/test_ruby-manta.rb
View
19 lib/ruby-manta.rb
@@ -648,19 +648,20 @@ 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 /<user>/stor. Lastly, the optional args is an
# array containing pairs of query args that will be appended at the end of
# the URL.
#
# 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]
@@ -669,15 +670,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))
View
17 tests/test_ruby-manta.rb
@@ -301,12 +301,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

0 comments on commit d63d5b4

Please sign in to comment.