right_aws_api 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5329a8cc0261eacfd9bf681412e9064185934f7e
4
- data.tar.gz: f6d75ece8c71660a0e6c465827083c1e655ca385
3
+ metadata.gz: 164b81b2c4e7845571d6616ce59b22a3baa7788a
4
+ data.tar.gz: e7cbd22f3857d650a026b29699e22cc9718d1821
5
5
  SHA512:
6
- metadata.gz: 4b6aa4f90078247eae455daf5e90d71323ea0dcf74f224885b4fb89f4beac77da3c7448f9e849e5253ed6d8019c7d97afa58164916f3ed9038143acb74ab129c
7
- data.tar.gz: fddd2af29aa08ce0f364b69a45fb177f08799c6f28dc20ef03c47329ee8273e881bad92dad36bc42b8f6cc7c05246fbc80fecb65adb12b8573e313acc78c67cc
6
+ metadata.gz: 4b970d6a9794c29953c16e63018f7a97522476f66a7357ea5be7e0bd6d3d110b880615d86531a45a70b7ebe41d96d3bf18697074d2a7fc5844b1fc16caf51089
7
+ data.tar.gz: d0846816165f048ebfa43d026d144c03c35158f4006c750a7d0c14117a1f50d4031a3d46422b34321935d182e074f121bc426e84be043b3ba76371ed03b59ffa
data/HISTORY CHANGED
@@ -1,2 +1,11 @@
1
+ == 2015-01-16
2
+ - v0.2.2
3
+ Added an ability to not use DNS like buckets in AWS S3
4
+ Some minor bugs were fixed in singature V4
5
+
6
+ == 2014-10-30
7
+ - v0.2.1
8
+ Added support for AWS v4 signature and make it the default
9
+
1
10
  == 2013-06-28
2
11
  - pre-release candidate created
@@ -171,7 +171,7 @@ module RightScale
171
171
  #
172
172
  # @example
173
173
  # sign_s3_signature('secret', :get, 'xxx/yyy/zzz/object', {'header'=>'value'}) #=>
174
- # "i85igH0sftHD/cGZcLiBKcYEuks="
174
+ # "i85igH0sftHD/cGZcLiBKcYEuks="
175
175
  #
176
176
  # @see http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
177
177
  #
@@ -196,7 +196,210 @@ module RightScale
196
196
  sign(aws_secret_access_key, string_to_sign)
197
197
  end
198
198
 
199
-
199
+
200
+ def self.sign_v4_get_service_and_region(host)
201
+ result =
202
+ case
203
+ when host[ /^iam\.amazonaws\.com$/i ] then ['iam', 'us-east-1']
204
+ when host[ /^(.*\.)?s3\.amazonaws\.com$/i ] then ['s3', 'us-east-1']
205
+ when host[ /^(.*\.)?s3-external-1\.amazonaws\.com$/i ] then ['s3', 'us-east-1']
206
+ when host[ /s3-website-([^.]+)\.amazonaws\.com$/i ] then ['s3', $1]
207
+ when host[ /^(.*\.)?s3-([^.]+).amazonaws\.com$/i ] then ['s3', $2]
208
+ when host[ /^(.*\.)?s3\.([^.]+)\.amazonaws\.com$/i ] then ['s3', $2]
209
+ else host[ /^([^.]+)\.([^.]+)\.amazonaws\.com$/i ] && [$1, $2]
210
+ end
211
+ fail(ArgumentError, "Cannot extract service name from %s host" % host.inspect) if !result || result[0].to_s.empty?
212
+ fail(ArgumentError, "Cannot extract region name from %s host" % host.inspect) if result[1].to_s.empty?
213
+ result
214
+ end
215
+
216
+
217
+ # Signs and Authenticates REST Requests
218
+ #
219
+ # @param [String] aws_secret_access_key
220
+ # @param [String] aws_access_key
221
+ # @param [String] host
222
+ # @param [Hash] request
223
+ #
224
+ # @return [String]
225
+ #
226
+ # @see http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
227
+ #
228
+ def self.sign_v4_signature(aws_access_key, aws_secret_access_key, host, request, method=:headers)
229
+ now = Time.now.utc
230
+ current_date = now.strftime("%Y%m%d")
231
+ current_time = now.strftime("%Y%m%dT%H%M%SZ")
232
+ host = host.downcase
233
+ service, region = sign_v4_get_service_and_region(host)
234
+ creds_scope = "%s/%s/%s/aws4_request" % [current_date, region, service]
235
+ algorithm = "AWS4-HMAC-SHA256"
236
+
237
+ # Verb
238
+ canonical_verb = sign_v4_get_canonical_verb(request[:verb])
239
+
240
+ # Path
241
+ request[:path] ||= '/'
242
+ canonical_path = sign_v4_get_canonical_path(request[:path])
243
+
244
+ # Headers (Auth)
245
+ request[:headers].delete('Authorization')
246
+ if method == :headers
247
+ canonical_payload = sign_v4_headers(request, host, current_time)
248
+ end
249
+ # Headers (Standard)
250
+ request[:headers]['Host'] = host
251
+ _headers = {}
252
+ request[:headers].each do |key, value|
253
+ _headers[key.to_s.downcase] = value.is_a?(Array) ? value.join(',') : value
254
+ end
255
+ canonical_headers = sign_v4_get_canonical_headers(_headers)
256
+ signed_headers = sign_v4_get_signed_headers(_headers)
257
+
258
+ # Params (Auth)
259
+ if method != :headers
260
+ canonical_payload = sign_v4_query_params(
261
+ request,
262
+ algorithm,
263
+ current_time,
264
+ signed_headers,
265
+ aws_access_key,
266
+ creds_scope
267
+ )
268
+ end
269
+ # Params (Standard)
270
+ canonical_query_string = Utils::params_to_urn(request[:params]){ |value| amz_escape(value) }
271
+
272
+ # Canonical String
273
+ canonical_string = sign_v4_get_canonical_string(
274
+ canonical_verb,
275
+ canonical_path,
276
+ canonical_query_string,
277
+ canonical_headers,
278
+ signed_headers,
279
+ canonical_payload
280
+ )
281
+
282
+ # StringToSign
283
+ string_to_sign = sign_v4_get_string_to_sign(algorithm, current_time, creds_scope, canonical_string)
284
+
285
+ # Signature
286
+ signature = sign_v4_get_signature_key(aws_secret_access_key, string_to_sign, current_date, region, service)
287
+
288
+ request[:path] += "?%s" % canonical_query_string unless canonical_query_string.empty?
289
+
290
+ if method == :headers
291
+ # Authorization Header
292
+ authorization_header = "%s Credential=%s/%s, SignedHeaders=%s, Signature=%s" %
293
+ [algorithm, aws_access_key, creds_scope, signed_headers, signature]
294
+ request[:headers]['Authorization'] = authorization_header
295
+ else
296
+ request[:path] += "&X-Amz-Signature=%s" % signature
297
+ end
298
+ end
299
+
300
+
301
+ def self.sign_v4_get_canonical_verb(verb)
302
+ verb.to_s.upcase
303
+ end
304
+
305
+
306
+ def self.sign_v4_get_canonical_path(path)
307
+ path
308
+ end
309
+
310
+
311
+ def self.sign_v4_query_params(request, algorithm, current_time, signed_headers, aws_access_key, creds_scope)
312
+ expires_at = request[:params]['X-Amz-Expires'] || 3600
313
+ expires_at = expires_at.to_i if expires_at.is_a?(Time)
314
+
315
+ request[:params]['X-Amz-Date'] = current_time
316
+ request[:params]['X-Amz-Expires'] = expires_at
317
+ request[:params]['X-Amz-Algorithm'] = algorithm
318
+ request[:params]['X-Amz-SignedHeaders'] = signed_headers
319
+ request[:params]['X-Amz-Credential'] = "%s/%s" % [aws_access_key, creds_scope]
320
+
321
+ 'UNSIGNED-PAYLOAD'
322
+ end
323
+
324
+
325
+ def self.sign_v4_get_canonical_headers(headers)
326
+ headers.sort.map{ |key, value| "#{key}:#{value}" }.join("\n")
327
+ end
328
+
329
+
330
+ def self.sign_v4_get_signed_headers(headers)
331
+ headers.keys.sort.join(';')
332
+ end
333
+
334
+
335
+ def self.sign_v4_headers(request, host, current_time)
336
+ expires_at = request[:headers]['X-Amz-Expires'].first || 3600
337
+ expires_at = expires_at.to_i if expires_at.is_a?(Time)
338
+
339
+ if request[:body].is_a?(IO)
340
+ canonical_payload = ''
341
+ request[:headers].set_if_blank('X-Amz-Content-Sha256', 'UNSIGNED-PAYLOAD')
342
+ else
343
+ request[:body] = request[:body].to_s
344
+ canonical_payload = hex_encode(Digest::SHA256.digest(request[:body]))
345
+ content_type = 'application/x-www-form-urlencoded; charset=utf-8'
346
+ content_md5 = Base64::encode64(Digest::MD5::digest(request[:body])).strip
347
+ request[:headers].set_if_blank('Content-Length', request[:body].bytesize)
348
+ request[:headers].set_if_blank('Content-Type', content_type)
349
+ request[:headers].set_if_blank('Content-Md5', content_md5)
350
+ request[:headers].set_if_blank('X-Amz-Content-Sha256', canonical_payload)
351
+ end
352
+ request[:headers]['X-Amz-Date'] = current_time
353
+ request[:headers]['X-Amz-Expires'] = expires_at
354
+
355
+ canonical_payload
356
+ end
357
+
358
+
359
+ # Signature V4: Returns Canonical String
360
+ #
361
+ # @return [String]
362
+ #
363
+ def self.sign_v4_get_canonical_string(verb, path, query_string, headers, signed_headers, payload)
364
+ verb + "\n" +
365
+ path + "\n" +
366
+ query_string + "\n" +
367
+ headers + "\n\n" +
368
+ signed_headers + "\n" +
369
+ payload
370
+ end
371
+
372
+
373
+ # Signature V4: A string to sign value
374
+ #
375
+ # @return [String]
376
+ #
377
+ def self.sign_v4_get_string_to_sign(algorithm, current_time, creds_scope, canonical_string)
378
+ algorithm + "\n" +
379
+ current_time + "\n" +
380
+ creds_scope + "\n" +
381
+ hex_encode(Digest::SHA256.digest(canonical_string)).downcase
382
+ end
383
+
384
+
385
+ #Helpers from AWS documentation http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html
386
+ def self.sign_v4_get_signature_key(key, string_to_sign, date, region, service, digest = @@digest256)
387
+ k_date = OpenSSL::HMAC.digest(digest, "AWS4" + key, date)
388
+ k_region = OpenSSL::HMAC.digest(digest, k_date, region)
389
+ k_service = OpenSSL::HMAC.digest(digest, k_region, service)
390
+ k_signing = OpenSSL::HMAC.digest(digest, k_service, "aws4_request")
391
+ hex_encode OpenSSL::HMAC.digest(digest, k_signing, string_to_sign)
392
+ end
393
+
394
+
395
+ def self.hex_encode(bindata)
396
+ result=""
397
+ data=bindata.unpack("C*")
398
+ data.each {|b| result+= "%02x" % b}
399
+ result
400
+ end
401
+
402
+
200
403
  # Parametrizes data to the format that Amazon EC2 (and compatible APIs) loves
201
404
  #
202
405
  # @param [Hash] data
@@ -207,7 +410,7 @@ module RightScale
207
410
  # # Where hash is:
208
411
  # { Name.?.Mask => Value | [ Values ],
209
412
  # NamePrefix.? => [{ SubNameA.1 => ValueA.1, SubNameB.1 => ValueB.1 }, # any simple parameter
210
- # ...,
413
+ # ...,
211
414
  # { SubNameN.X => ValueN.X, SubNameM.X => ValueN.X }] # see BlockDeviceMapping case
212
415
  #
213
416
  # @example
@@ -237,15 +440,15 @@ module RightScale
237
440
  # 'MaxCount' => 2,
238
441
  # 'KeyName' => 'my-key',
239
442
  # 'SecurityGroupId' => ['sg-01234567', 'sg-12345670', 'sg-23456701'],
240
- # 'BlockDeviceMapping' => [
241
- # { 'DeviceName' => '/dev/sda1',
242
- # 'Ebs.SnapshotId' => 'snap-01234567',
443
+ # 'BlockDeviceMapping' => [
444
+ # { 'DeviceName' => '/dev/sda1',
445
+ # 'Ebs.SnapshotId' => 'snap-01234567',
243
446
  # 'Ebs.VolumeSize' => 20,
244
447
  # 'Ebs.DeleteOnTermination' => true },
245
- # { 'DeviceName' => '/dev/sdb1',
246
- # 'Ebs.SnapshotId' => 'snap-12345670',
448
+ # { 'DeviceName' => '/dev/sdb1',
449
+ # 'Ebs.SnapshotId' => 'snap-12345670',
247
450
  # 'Ebs.VolumeSize' => 10,
248
- # 'Ebs.DeleteOnTermination' => false } ] ) #=>
451
+ # 'Ebs.DeleteOnTermination' => false } ] ) #=>
249
452
  # {
250
453
  # "BlockDeviceMapping.1.DeviceName" => "/dev/sda1",
251
454
  # "BlockDeviceMapping.1.Ebs.DeleteOnTermination" => true,
@@ -262,7 +465,7 @@ module RightScale
262
465
  # "SecurityGroupId.1" => "sg-01234567",
263
466
  # "SecurityGroupId.2" => "sg-12345670",
264
467
  # "SecurityGroupId.3" => "sg-23456701"
265
- # }
468
+ # }
266
469
  #
267
470
  # @example
268
471
  # parametrize( 'DomainName' => 'kdclient',
@@ -276,7 +479,7 @@ module RightScale
276
479
  # { 'ItemName' => 'diana',
277
480
  # 'Attribute' => [ { 'Name' => 'sex', 'Value' => 'female' },
278
481
  # { 'Name' => 'weight', 'Value' => '120'},
279
- # { 'Name' => 'age', 'Value' => '25'} ] } ] ) #=>
482
+ # { 'Name' => 'age', 'Value' => '25'} ] } ] ) #=>
280
483
  # { "DomainName" => "kdclient",
281
484
  # "Item.1.ItemName" => "konstantin",
282
485
  # "Item.1.Attribute.1.Name" => "sex",
@@ -321,8 +524,8 @@ module RightScale
321
524
  end
322
525
  result
323
526
  end
324
-
527
+
325
528
  end
326
529
  end
327
530
  end
328
- end
531
+ end
@@ -116,7 +116,7 @@ module RightScale
116
116
  opts[:headers] = params.delete(:headers) || {}
117
117
  opts[:options] = params.delete(:options) || {}
118
118
  opts[:params] = parametrize(params)
119
- process_api_request(:get, '', opts, &block)
119
+ process_api_request(:post, '', opts, &block)
120
120
  end
121
121
 
122
122
 
@@ -43,38 +43,22 @@ module RightScale
43
43
  # no example
44
44
  #
45
45
  def process
46
- # Make sure all the required params are set
47
- @data[:request][:params]['AWSAccessKeyId'] = @data[:credentials][:aws_access_key_id]
48
- @data[:request][:params]['Version'] ||= @data[:options][:api_version]
49
46
  # Compile a final request path
50
47
  @data[:request][:path] = Utils::join_urn(@data[:connection][:uri].path, @data[:request][:relative_path])
51
- # Sign the request
52
- sign_proc = Proc::new do |data|
53
- Utils::AWS::sign_v2_signature( data[:credentials][:aws_secret_access_key],
54
- data[:request][:params] || {},
55
- data[:request][:verb],
56
- data[:connection][:uri].host,
57
- data[:request][:path] )
58
- end
59
- signed_path = sign_proc.call(@data)
60
- # Rebuild the request as POST if its path is too long
61
- if signed_path.size > MAX_GET_REQUEST_PATH_LENGTH && @data[:request][:verb] == :get
62
- @data[:request][:verb] = :post
63
- signed_path = sign_proc.call(@data)
64
- end
65
- # Set new path or body and content-type
66
- case @data[:request][:verb]
67
- when :get
68
- @data[:request][:path] << "?#{signed_path}"
69
- when :post
70
- @data[:request][:body] = signed_path
71
- @data[:request][:headers]['content-type'] = 'application/x-www-form-urlencoded; charset=utf-8'
72
- else
73
- fail Error::new("Unsupported HTTP verb: #{@data[:request][:verb]}")
74
- end
48
+
49
+ # Swap query params and body
50
+ @data[:request][:params]['Version'] ||= @data[:options][:api_version]
51
+ @data[:request][:body] = Utils::params_to_urn(@data[:request][:params]){ |value| Utils::AWS::amz_escape(value) }
52
+ @data[:request][:params] = {}
53
+
54
+ Utils::AWS::sign_v4_signature(
55
+ @data[:credentials][:aws_access_key_id],
56
+ @data[:credentials][:aws_secret_access_key],
57
+ @data[:connection][:uri].host,
58
+ @data[:request]
59
+ )
75
60
  end
76
61
  end
77
-
78
62
  end
79
63
  end
80
- end
64
+ end
@@ -43,25 +43,24 @@ module RightScale
43
43
  fail Error::new("Body must be blank") unless @data[:request][:body]._blank?
44
44
  fail Error::new("Headers must be blank") unless @data[:request][:headers]._blank?
45
45
 
46
- uri = @data[:connection][:uri]
47
- access_key = @data[:credentials][:aws_access_key_id]
48
- secret_key = @data[:credentials][:aws_secret_access_key]
49
- bucket = @data[:request][:bucket]
50
- object = @data[:request][:relative_path]
51
- params = @data[:request][:params]
52
- verb = @data[:request][:verb]
53
-
46
+ uri = @data[:connection][:uri]
47
+ bucket = @data[:request][:bucket]
48
+ object = @data[:request][:relative_path]
54
49
  bucket, object = compute_bucket_name_and_object_path(bucket, object)
55
50
  uri = compute_host(bucket, uri)
56
51
 
57
- compute_params!(params, access_key)
52
+ @data[:request][:path] = compute_path(bucket, object)
58
53
 
59
- # Set Auth param
60
- signature = compute_signature(secret_key, verb, bucket, object, params)
61
- params['Signature'] = signature
54
+ Utils::AWS::sign_v4_signature(
55
+ @data[:credentials][:aws_access_key_id],
56
+ @data[:credentials][:aws_secret_access_key],
57
+ @data[:connection][:uri].host,
58
+ @data[:request],
59
+ :query_params
60
+ )
62
61
 
63
62
  # Compute href
64
- path = compute_path(bucket, object, params)
63
+ path = @data[:request][:path]
65
64
  uri.path, uri.query = path.split('?')
66
65
  @data[:result] = uri.to_s
67
66
 
@@ -69,37 +68,6 @@ module RightScale
69
68
  @data[:vars][:system][:done] = true
70
69
  end
71
70
 
72
-
73
- # Sets response params
74
- #
75
- # @param [Hash] params
76
- #
77
- # @return [Hash]
78
- #
79
- def compute_params!(params, access_key)
80
- # Expires
81
- expires = params['Expires']
82
- expires ||= Time.now.utc.to_i + ONE_YEAR_OF_SECONDS
83
- expires = expires.to_i unless expires.is_a?(Fixnum)
84
- params['Expires'] = expires
85
- params['AWSAccessKeyId'] = access_key
86
- params
87
- end
88
-
89
-
90
- # Computes signature
91
- #
92
- # @param [String] secret_key
93
- # @param [String] verb
94
- # @param [String] bucket
95
- # @param [Hash] params
96
- #
97
- # @return [String]
98
- #
99
- def compute_signature(secret_key, verb, bucket, object, params)
100
- can_path = compute_canonicalized_path(bucket, object, params)
101
- Utils::AWS::sign_s3_signature(secret_key, verb, can_path, { 'expires' => params['Expires'] })
102
- end
103
71
  end
104
72
 
105
73
  end
@@ -293,6 +293,19 @@ module RightScale
293
293
  # 'https://s3.amazonaws.com/?AWSAccessKeyId=AK...TA&Expires=1436651780&
294
294
  # Signature=XK...53s%3D'
295
295
  #
296
+ # @example
297
+ # link = RightScale::CloudApi::AWS::S3::Link::Manager::new(key, secret, endpoint)
298
+ # link.GetObject('Bucket' => 'foo', 'Object' => 'bar') #=>
299
+ # 'https://foo.s3.amazonaws.com/bar?AWSAccessKeyId=AK...TA&Expires=1436557118&
300
+ # Signature=hg...%3D&response-content-type=image%2Fpeg'
301
+ #
302
+ # @example
303
+ # # Do not use DNS-like bucket hosts but put buckets into path
304
+ # link = RightScale::CloudApi::AWS::S3::Link::Manager::new(key, secret, endpoint, :no_dns_buckets => true)
305
+ # link.GetObject('Bucket' => 'foo', 'Object' => 'bar') #=>
306
+ # 'https://s3.amazonaws.com/foo/bar?AWSAccessKeyId=AK...TA&Expires=1436557118&
307
+ # Signature=hg...%3D&response-content-type=image%2Fpeg'
308
+ #
296
309
  # @see ApiManager
297
310
  # @see Wrapper::DEFAULT.extended Wrapper::DEFAULT.extended (click [View source])
298
311
  # @see http://docs.aws.amazon.com/AmazonS3/latest/API/APIRest.html
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  #--
2
3
  # Copyright (c) 2013 RightScale, Inc.
3
4
  #
@@ -75,91 +76,28 @@ module RightScale
75
76
  # # no example
76
77
  #
77
78
  def process
78
- uri = @data[:connection][:uri]
79
- access_key = @data[:credentials][:aws_access_key_id]
80
- secret_key = @data[:credentials][:aws_secret_access_key]
81
- body = @data[:request][:body]
82
- bucket = @data[:request][:bucket]
83
- headers = @data[:request][:headers]
84
- object = @data[:request][:relative_path]
85
- params = @data[:request][:params]
86
- verb = @data[:request][:verb]
87
-
79
+ uri = @data[:connection][:uri]
80
+ body = @data[:request][:body]
81
+ bucket = @data[:request][:bucket]
82
+ object = @data[:request][:relative_path]
88
83
  bucket, object = compute_bucket_name_and_object_path(bucket, object)
89
- body = compute_body(body, headers['content-type'])
84
+ body = compute_body(body, @data[:request][:headers]['content-type'])
90
85
  uri = compute_host(bucket, uri)
91
86
 
92
- compute_headers!(headers, body, uri.host)
93
-
94
- # Set Authorization header
95
- signature = compute_signature(access_key, secret_key, verb, bucket, object, params, headers)
96
- headers['authorization'] = "AWS #{access_key}:#{signature}"
97
-
98
- @data[:request][:body] = body
99
- @data[:request][:bucket] = bucket
100
- @data[:request][:headers] = headers
101
- @data[:request][:params] = params
102
- @data[:request][:path] = compute_path(bucket, object, params)
103
- @data[:request][:relative_path] = object
104
- @data[:connection][:uri] = uri
105
- end
106
-
107
-
108
- # Returns a list of sub-resource(s)
109
- #
110
- # Sub-resources are acl, torrent, versioning, location, etc. See SUB_RESOURCES
111
- #
112
- # @return [Hash]
113
- #
114
- def get_subresources(params)
115
- result = {}
116
- params.each do |key, value|
117
- next unless SUB_RESOURCES.include?(key) || key[OVERRIDE_RESPONSE_HEADERS]
118
- result[key] = (value._blank? ? nil : value)
119
- end
120
- result
121
- end
87
+ compute_headers!(@data[:request][:headers], body, uri.host)
122
88
 
89
+ @data[:connection][:uri] = uri
90
+ @data[:request][:bucket] = bucket
91
+ @data[:request][:relative_path] = object
92
+ @data[:request][:body] = body
93
+ @data[:request][:path] = compute_path(bucket, object)
123
94
 
124
- # Returns canonicalized bucket
125
- #
126
- # @param [String] bucket
127
- #
128
- # @return [String]
129
- #
130
- # @example
131
- # # DNS bucket
132
- # compute_canonicalized_bucket('foo-bar') #=> 'foo-bar/'
133
- #
134
- # @example
135
- # # non DNS bucket
136
- # compute_canonicalized_bucket('foo_bar') #=> 'foo_bar'
137
- #
138
- def compute_canonicalized_bucket(bucket)
139
- bucket += '/' if Utils::AWS::is_dns_bucket?(bucket)
140
- bucket
141
- end
142
-
143
-
144
- # Returns canonicalized path
145
- #
146
- # @param [String] bucket
147
- # @param [String] relative_path
148
- # @param [Hash] params
149
- #
150
- # @return [String]
151
- #
152
- # @example
153
- # params = { 'Foo' => 1, 'acl' => '2', 'response-content-type' => 'jpg' }
154
- # compute_canonicalized_path('foo-bar_bucket', 'a/b/c/d.jpg', params)
155
- # #=> '/foo-bar_bucket/a/b/c/d.jpg?acl=3&response-content-type=jpg'
156
- #
157
- def compute_canonicalized_path(bucket, relative_path, params)
158
- can_bucket = compute_canonicalized_bucket(bucket)
159
- sub_params = get_subresources(params)
160
- # We use the block below to avoid escaping: Amazon does not like escaped bucket and '/'
161
- # in canonicalized path (relative path has been escaped above already)
162
- Utils::join_urn(can_bucket, relative_path, sub_params) { |value| value }
95
+ Utils::AWS::sign_v4_signature(
96
+ @data[:credentials][:aws_access_key_id],
97
+ @data[:credentials][:aws_secret_access_key],
98
+ @data[:connection][:uri].host,
99
+ @data[:request]
100
+ )
163
101
  end
164
102
 
165
103
 
@@ -180,7 +118,10 @@ module RightScale
180
118
  relative_path.to_s[/^([^\/]*)\/?(.*)$/]
181
119
  # Escape part of the path that may have UTF-8 chars (in S3 Object name for instance).
182
120
  # Amazon wants them to be escaped before we sign the request.
183
- [ $1, Utils::AWS::amz_escape($2) ]
121
+ # P.S. but do not escape "/" (signature v4 does not like this)
122
+ #
123
+ object = $2.to_s.split('/').map{|i| Utils::AWS::amz_escape(i)}.join('/')
124
+ [ $1, object ]
184
125
  end
185
126
 
186
127
 
@@ -206,7 +147,7 @@ module RightScale
206
147
  # @return [URI]
207
148
  #
208
149
  def compute_host(bucket, uri)
209
- return uri unless Utils::AWS::is_dns_bucket?(bucket)
150
+ return uri unless is_dns_bucket?(bucket)
210
151
  return uri if uri.host[/^#{bucket}\..+\.[^.]+\.[^.]+$/]
211
152
  uri.host = "#{bucket}.#{uri.host}"
212
153
  uri
@@ -221,7 +162,7 @@ module RightScale
221
162
  # @return [Object]
222
163
  #
223
164
  def compute_body(body, content_type)
224
- return body if body._blank?
165
+ return body if body.nil?
225
166
  # Make sure it is a String instance
226
167
  return body unless body.is_a?(Hash)
227
168
  Utils::contentify_body(body, content_type)
@@ -243,30 +184,10 @@ module RightScale
243
184
  # 'The request signature we calculated does not match the signature you provided.
244
185
  # Check your key and signing method.'
245
186
  headers.set_if_blank('content-type', 'application/octet-stream')
246
- headers.set_if_blank('date', Time::now.utc.httpdate)
247
- headers['content-md5'] = Base64::encode64(Digest::MD5::digest(body)).strip if !body._blank?
248
- headers['host'] = host
249
187
  headers
250
188
  end
251
189
 
252
190
 
253
- # Computes signature
254
- #
255
- # @param [String] access_key
256
- # @param [String] secret_key
257
- # @param [String] verb
258
- # @param [String] bucket
259
- # @param [Hash] params
260
- # @param [Hash] headers
261
- #
262
- # @return [String]
263
- #
264
- def compute_signature(access_key, secret_key, verb, bucket, object, params, headers)
265
- can_path = compute_canonicalized_path(bucket, object, params)
266
- Utils::AWS::sign_s3_signature(secret_key, verb, can_path, headers)
267
- end
268
-
269
-
270
191
  # Builds request path
271
192
  #
272
193
  # @param [String] bucket
@@ -275,16 +196,26 @@ module RightScale
275
196
  #
276
197
  # @return [String]
277
198
  #
278
- def compute_path(bucket, object, params)
199
+ def compute_path(bucket, object)
279
200
  data = []
280
- data << bucket unless Utils::AWS::is_dns_bucket?(bucket)
201
+ data << bucket unless is_dns_bucket?(bucket)
281
202
  data << object
282
- data << params
283
203
  Utils::join_urn(*data)
284
204
  end
285
205
 
206
+ # Returns +true+ if DNS compatible buckets are enabled (default) and the
207
+ # given bucket is DNS compatible
208
+ #
209
+ # @param [String] bucket
210
+ # @return [Boolean]
211
+ #
212
+ def is_dns_bucket?(bucket)
213
+ return false if @data[:options][:cloud][:no_dns_buckets]
214
+ Utils::AWS::is_dns_bucket?(bucket)
215
+ end
216
+
286
217
  end
287
218
  end
288
219
  end
289
220
  end
290
- end
221
+ end