mss-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +9 -0
  3. data/LICENSE.txt +0 -0
  4. data/README.md +192 -0
  5. data/bin/mss-rb +178 -0
  6. data/ca-bundle.crt +3554 -0
  7. data/lib/mss/core/async_handle.rb +89 -0
  8. data/lib/mss/core/cacheable.rb +76 -0
  9. data/lib/mss/core/client.rb +786 -0
  10. data/lib/mss/core/collection/simple.rb +81 -0
  11. data/lib/mss/core/collection/with_limit_and_next_token.rb +70 -0
  12. data/lib/mss/core/collection/with_next_token.rb +96 -0
  13. data/lib/mss/core/collection.rb +262 -0
  14. data/lib/mss/core/configuration.rb +527 -0
  15. data/lib/mss/core/credential_providers.rb +653 -0
  16. data/lib/mss/core/data.rb +251 -0
  17. data/lib/mss/core/deprecations.rb +83 -0
  18. data/lib/mss/core/endpoints.rb +36 -0
  19. data/lib/mss/core/http/connection_pool.rb +374 -0
  20. data/lib/mss/core/http/curb_handler.rb +150 -0
  21. data/lib/mss/core/http/handler.rb +88 -0
  22. data/lib/mss/core/http/net_http_handler.rb +144 -0
  23. data/lib/mss/core/http/patch.rb +98 -0
  24. data/lib/mss/core/http/request.rb +258 -0
  25. data/lib/mss/core/http/response.rb +80 -0
  26. data/lib/mss/core/indifferent_hash.rb +87 -0
  27. data/lib/mss/core/inflection.rb +55 -0
  28. data/lib/mss/core/ini_parser.rb +41 -0
  29. data/lib/mss/core/json_client.rb +46 -0
  30. data/lib/mss/core/json_parser.rb +75 -0
  31. data/lib/mss/core/json_request_builder.rb +34 -0
  32. data/lib/mss/core/json_response_parser.rb +78 -0
  33. data/lib/mss/core/lazy_error_classes.rb +107 -0
  34. data/lib/mss/core/log_formatter.rb +426 -0
  35. data/lib/mss/core/managed_file.rb +31 -0
  36. data/lib/mss/core/meta_utils.rb +44 -0
  37. data/lib/mss/core/model.rb +61 -0
  38. data/lib/mss/core/naming.rb +29 -0
  39. data/lib/mss/core/option_grammar.rb +737 -0
  40. data/lib/mss/core/options/json_serializer.rb +81 -0
  41. data/lib/mss/core/options/validator.rb +154 -0
  42. data/lib/mss/core/options/xml_serializer.rb +117 -0
  43. data/lib/mss/core/page_result.rb +74 -0
  44. data/lib/mss/core/policy.rb +938 -0
  45. data/lib/mss/core/query_client.rb +40 -0
  46. data/lib/mss/core/query_error_parser.rb +23 -0
  47. data/lib/mss/core/query_request_builder.rb +46 -0
  48. data/lib/mss/core/query_response_parser.rb +34 -0
  49. data/lib/mss/core/region.rb +84 -0
  50. data/lib/mss/core/region_collection.rb +79 -0
  51. data/lib/mss/core/resource.rb +412 -0
  52. data/lib/mss/core/resource_cache.rb +39 -0
  53. data/lib/mss/core/response.rb +214 -0
  54. data/lib/mss/core/response_cache.rb +49 -0
  55. data/lib/mss/core/rest_error_parser.rb +23 -0
  56. data/lib/mss/core/rest_json_client.rb +39 -0
  57. data/lib/mss/core/rest_request_builder.rb +153 -0
  58. data/lib/mss/core/rest_response_parser.rb +65 -0
  59. data/lib/mss/core/rest_xml_client.rb +46 -0
  60. data/lib/mss/core/service_interface.rb +82 -0
  61. data/lib/mss/core/signers/base.rb +45 -0
  62. data/lib/mss/core/signers/cloud_front.rb +55 -0
  63. data/lib/mss/core/signers/s3.rb +158 -0
  64. data/lib/mss/core/signers/version_2.rb +71 -0
  65. data/lib/mss/core/signers/version_3.rb +85 -0
  66. data/lib/mss/core/signers/version_3_https.rb +60 -0
  67. data/lib/mss/core/signers/version_4/chunk_signed_stream.rb +190 -0
  68. data/lib/mss/core/signers/version_4.rb +227 -0
  69. data/lib/mss/core/uri_escape.rb +43 -0
  70. data/lib/mss/core/xml/frame.rb +245 -0
  71. data/lib/mss/core/xml/frame_stack.rb +84 -0
  72. data/lib/mss/core/xml/grammar.rb +306 -0
  73. data/lib/mss/core/xml/parser.rb +69 -0
  74. data/lib/mss/core/xml/root_frame.rb +64 -0
  75. data/lib/mss/core/xml/sax_handlers/libxml.rb +46 -0
  76. data/lib/mss/core/xml/sax_handlers/nokogiri.rb +55 -0
  77. data/lib/mss/core/xml/sax_handlers/ox.rb +40 -0
  78. data/lib/mss/core/xml/sax_handlers/rexml.rb +46 -0
  79. data/lib/mss/core/xml/stub.rb +122 -0
  80. data/lib/mss/core.rb +602 -0
  81. data/lib/mss/errors.rb +161 -0
  82. data/lib/mss/rails.rb +194 -0
  83. data/lib/mss/s3/access_control_list.rb +262 -0
  84. data/lib/mss/s3/acl_object.rb +263 -0
  85. data/lib/mss/s3/acl_options.rb +200 -0
  86. data/lib/mss/s3/bucket.rb +757 -0
  87. data/lib/mss/s3/bucket_collection.rb +161 -0
  88. data/lib/mss/s3/bucket_lifecycle_configuration.rb +472 -0
  89. data/lib/mss/s3/bucket_region_cache.rb +51 -0
  90. data/lib/mss/s3/bucket_tag_collection.rb +110 -0
  91. data/lib/mss/s3/bucket_version_collection.rb +78 -0
  92. data/lib/mss/s3/cipher_io.rb +119 -0
  93. data/lib/mss/s3/client/xml.rb +265 -0
  94. data/lib/mss/s3/client.rb +2076 -0
  95. data/lib/mss/s3/config.rb +60 -0
  96. data/lib/mss/s3/cors_rule.rb +107 -0
  97. data/lib/mss/s3/cors_rule_collection.rb +193 -0
  98. data/lib/mss/s3/data_options.rb +190 -0
  99. data/lib/mss/s3/encryption_utils.rb +145 -0
  100. data/lib/mss/s3/errors.rb +93 -0
  101. data/lib/mss/s3/multipart_upload.rb +353 -0
  102. data/lib/mss/s3/multipart_upload_collection.rb +75 -0
  103. data/lib/mss/s3/object_collection.rb +355 -0
  104. data/lib/mss/s3/object_metadata.rb +102 -0
  105. data/lib/mss/s3/object_upload_collection.rb +76 -0
  106. data/lib/mss/s3/object_version.rb +153 -0
  107. data/lib/mss/s3/object_version_collection.rb +88 -0
  108. data/lib/mss/s3/paginated_collection.rb +74 -0
  109. data/lib/mss/s3/policy.rb +73 -0
  110. data/lib/mss/s3/prefix_and_delimiter_collection.rb +46 -0
  111. data/lib/mss/s3/prefixed_collection.rb +84 -0
  112. data/lib/mss/s3/presign_v4.rb +135 -0
  113. data/lib/mss/s3/presigned_post.rb +574 -0
  114. data/lib/mss/s3/region_detection.rb +75 -0
  115. data/lib/mss/s3/request.rb +61 -0
  116. data/lib/mss/s3/s3_object.rb +1795 -0
  117. data/lib/mss/s3/tree/branch_node.rb +67 -0
  118. data/lib/mss/s3/tree/child_collection.rb +103 -0
  119. data/lib/mss/s3/tree/leaf_node.rb +93 -0
  120. data/lib/mss/s3/tree/node.rb +21 -0
  121. data/lib/mss/s3/tree/parent.rb +86 -0
  122. data/lib/mss/s3/tree.rb +115 -0
  123. data/lib/mss/s3/uploaded_part.rb +81 -0
  124. data/lib/mss/s3/uploaded_part_collection.rb +83 -0
  125. data/lib/mss/s3/website_configuration.rb +101 -0
  126. data/lib/mss/s3.rb +161 -0
  127. data/lib/mss/version.rb +16 -0
  128. data/lib/mss-sdk.rb +2 -0
  129. data/lib/mss.rb +14 -0
  130. data/rails/init.rb +14 -0
  131. metadata +201 -0
@@ -0,0 +1,71 @@
1
+ # Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ #
8
+ # or in the "license" file accompanying this file. This file is
9
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
10
+ # ANY KIND, either express or implied. See the License for the specific
11
+ # language governing permissions and limitations under the License.
12
+
13
+ module MSS
14
+ module Core
15
+ module Signers
16
+ # @api private
17
+ class Version2
18
+
19
+ include Base
20
+
21
+ # @param [CredentialProviders::Provider] credentials
22
+ def initialize credentials
23
+ @credentials = credentials
24
+ end
25
+
26
+ # @return [CredentialProviders::Provider]
27
+ attr_reader :credentials
28
+
29
+ # @param [Http::Request] req
30
+ # @return [Http::Request]
31
+ def sign_request req
32
+ req.add_param('AWSAccessKeyId', credentials.access_key_id)
33
+ if token = credentials.session_token
34
+ req.add_param("SecurityToken", token)
35
+ end
36
+ req.add_param('SignatureVersion', '2')
37
+ req.add_param('SignatureMethod', 'HmacSHA256')
38
+ req.add_param('Signature', signature(req))
39
+ req.body = req.url_encoded_params
40
+ req
41
+ end
42
+
43
+ private
44
+
45
+ # @param [Http::Request] req
46
+ def signature req
47
+ sign(credentials.secret_access_key, string_to_sign(req))
48
+ end
49
+
50
+ # @param [Http::Request] req
51
+ def string_to_sign req
52
+
53
+ host =
54
+ case req.port
55
+ when 80, 443 then req.host
56
+ else "#{req.host}:#{req.port}"
57
+ end
58
+
59
+ [
60
+ req.http_method,
61
+ host.to_s.downcase,
62
+ req.path,
63
+ req.url_encoded_params,
64
+ ].join("\n")
65
+
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,85 @@
1
+ # Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ #
8
+ # or in the "license" file accompanying this file. This file is
9
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
10
+ # ANY KIND, either express or implied. See the License for the specific
11
+ # language governing permissions and limitations under the License.
12
+
13
+ require 'openssl'
14
+ require 'time'
15
+
16
+ module MSS
17
+ module Core
18
+ module Signers
19
+ # @api private
20
+ class Version3
21
+
22
+ include Base
23
+
24
+ # @param [CredentialProviders::Provider] credentials
25
+ def initialize credentials
26
+ @credentials = credentials
27
+ end
28
+
29
+ # @return [CredentialProviders::Provider]
30
+ attr_reader :credentials
31
+
32
+ # @param [Http::Request] req
33
+ # @return [Http::Request]
34
+ def sign_request req
35
+ req.headers["x-amz-date"] ||= (req.headers["date"] ||= Time.now.httpdate)
36
+ req.headers["host"] ||= req.host
37
+ req.headers["x-amz-security-token"] = credentials.session_token if
38
+ credentials.session_token
39
+ req.headers["x-amzn-authorization"] =
40
+ "MSS3 "+
41
+ "AWSAccessKeyId=#{credentials.access_key_id},"+
42
+ "Algorithm=HmacSHA256,"+
43
+ "SignedHeaders=#{headers_to_sign(req).join(';')},"+
44
+ "Signature=#{signature(req)}"
45
+ end
46
+
47
+ private
48
+
49
+ # @param [Http::Request] req
50
+ def signature req, service_signing_name = nil
51
+ sign(credentials.secret_access_key, string_to_sign(req))
52
+ end
53
+
54
+ # @param [Http::Request] req
55
+ def string_to_sign req
56
+ OpenSSL::Digest::SHA256.digest([
57
+ req.http_method,
58
+ "/",
59
+ "",
60
+ canonical_headers(req),
61
+ req.body
62
+ ].join("\n"))
63
+ end
64
+
65
+ # @param [Http::Request] req
66
+ def canonical_headers req
67
+ headers_to_sign(req).map do |name|
68
+ value = req.headers[name]
69
+ "#{name.downcase.strip}:#{value.strip}\n"
70
+ end.sort.join
71
+ end
72
+
73
+ # @param [Http::Request] req
74
+ def headers_to_sign req
75
+ req.headers.keys.select do |header|
76
+ header == "host" ||
77
+ header == "content-encoding" ||
78
+ header =~ /^x-amz/
79
+ end
80
+ end
81
+
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,60 @@
1
+ # Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ #
8
+ # or in the "license" file accompanying this file. This file is
9
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
10
+ # ANY KIND, either express or implied. See the License for the specific
11
+ # language governing permissions and limitations under the License.
12
+
13
+ require 'time'
14
+
15
+ module MSS
16
+ module Core
17
+ module Signers
18
+ # @api private
19
+ class Version3Https
20
+
21
+ include Base
22
+
23
+ # @param [CredentialProviders::Provider] credentials
24
+ def initialize credentials
25
+ @credentials = credentials
26
+ end
27
+
28
+ # @return [CredentialProviders::Provider]
29
+ attr_reader :credentials
30
+
31
+ # @param [Http::Request] req
32
+ # @return [Http::Request]
33
+ def sign_request req
34
+ parts = []
35
+ parts << "MSS3-HTTPS AWSAccessKeyId=#{credentials.access_key_id}"
36
+ parts << "Algorithm=HmacSHA256"
37
+ parts << "Signature=#{signature(req)}"
38
+ req.headers['x-amzn-authorization'] = parts.join(',')
39
+ req.headers['x-amz-security-token'] = credentials.session_token if
40
+ credentials.session_token
41
+ req
42
+ end
43
+
44
+ private
45
+
46
+ # @param [Http::Request] req
47
+ def signature req
48
+ sign(credentials.secret_access_key, string_to_sign(req))
49
+ end
50
+
51
+ # @param [Http::Request] req
52
+ def string_to_sign req
53
+ req.headers['date'] ||= Time.now.httpdate
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,190 @@
1
+ # Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ #
8
+ # or in the "license" file accompanying this file. This file is
9
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
10
+ # ANY KIND, either express or implied. See the License for the specific
11
+ # language governing permissions and limitations under the License.
12
+
13
+ require 'stringio'
14
+
15
+ module MSS
16
+ module Core
17
+ module Signers
18
+ class Version4
19
+ class ChunkSignedStream
20
+
21
+ # @api private
22
+ DEFAULT_CHUNK_SIZE = 128 * 1024
23
+
24
+ # @api private
25
+ MAX_BUFFER_SIZE = 256 * 1024
26
+
27
+ # @api private
28
+ CHUNK_SIGNATURE_HEADER = ";chunk-signature="
29
+
30
+ # @api private
31
+ CHUNK_STRING_TO_SIGN_PREFIX = "MSS4-HMAC-SHA256-PAYLOAD"
32
+
33
+ # @api private
34
+ SIGNATURE_LENGTH = 64
35
+
36
+ # @api private
37
+ CLRF = "\r\n"
38
+
39
+ # @param [IO] stream The original http request body stream.
40
+ # @param [Integer] stream_size Size of the original stream in bytes.
41
+ # This must be greater than 0.
42
+ # @param [String] key The derived sigv4 signing key.
43
+ # @param [String] key_path The scope of the derived key.
44
+ # @param [String] datetime The iso8601 formatted datetime.
45
+ # @param [String] signature The computed signature of the request headers.
46
+ # @return [IO] Returns an IO-like object.
47
+ def initialize stream, stream_size, key, key_path, datetime, signature
48
+ @stream = stream || StringIO.new('')
49
+ @size = self.class.signed_size(stream_size)
50
+ @key = key
51
+ @key_path = key_path
52
+ @datetime = datetime
53
+ @prev_chunk_signature = signature
54
+ reset
55
+ end
56
+
57
+ # @return [Integer] the size of the final (signed) stream
58
+ attr_reader :size
59
+
60
+ # @param [Integer] bytes (nil)
61
+ # @param [String] output_buffer (nil)
62
+ # @return [String,nil]
63
+ def read bytes = nil, output_buffer = nil
64
+ data = read_bytes(bytes || @size)
65
+ if output_buffer
66
+ output_buffer.replace(data || '')
67
+ else
68
+ (data.nil? and bytes.nil?) ? '' : data
69
+ end
70
+ end
71
+
72
+ # @return [Integer]
73
+ def rewind
74
+ @stream.rewind
75
+ reset
76
+ end
77
+
78
+ private
79
+
80
+ def reset
81
+ @buffer = ''
82
+ @more_chunks = true
83
+ end
84
+
85
+ # @param [Integer] num_bytes The maximum number of bytes to return.
86
+ # @return [String,nil] `nil` once the complete stream has been read
87
+ def read_bytes num_bytes
88
+ fill_buffer(num_bytes)
89
+ bytes = @buffer[0,num_bytes]
90
+ @buffer = @buffer[num_bytes..-1] || '' # flatten the buffer
91
+ bytes == '' ? nil : bytes
92
+ end
93
+
94
+ # Fills the internal buffer at least +num_bytes+ of data.
95
+ # @param [Integer] num_bytes
96
+ def fill_buffer num_bytes
97
+ while @buffer.bytesize < num_bytes && more_chunks?
98
+ @buffer << next_chunk
99
+ end
100
+ end
101
+
102
+ def more_chunks?
103
+ @more_chunks
104
+ end
105
+
106
+ def next_chunk
107
+ chunk = @stream.read(DEFAULT_CHUNK_SIZE)
108
+ if chunk.nil?
109
+ chunk = ''
110
+ @more_chunks = false
111
+ end
112
+ sign_chunk(chunk)
113
+ end
114
+
115
+ # Given a chunk of the original stream, this method returns a signed
116
+ # chunk with the prefixed header.
117
+ # @param [String] chunk
118
+ # @return [String]
119
+ def sign_chunk chunk
120
+ [
121
+ chunk.bytesize.to_s(16),
122
+ CHUNK_SIGNATURE_HEADER,
123
+ next_chunk_signature(chunk),
124
+ CLRF,
125
+ chunk,
126
+ CLRF,
127
+ ].join
128
+ end
129
+
130
+ # @param [String] chunk
131
+ # @return [String]
132
+ def next_chunk_signature chunk
133
+ string_to_sign = [
134
+ "MSS4-HMAC-SHA256-PAYLOAD",
135
+ @datetime,
136
+ @key_path,
137
+ @prev_chunk_signature,
138
+ hash(''),
139
+ hash(chunk),
140
+ ].join("\n")
141
+ signature = sign(string_to_sign)
142
+ @prev_chunk_signature = signature
143
+ signature
144
+ end
145
+
146
+ def sign value
147
+ @digest ||= OpenSSL::Digest.new('sha256')
148
+ OpenSSL::HMAC.hexdigest(@digest, @key, value)
149
+ end
150
+
151
+ def hash value
152
+ OpenSSL::Digest::SHA256.new.update(value).hexdigest
153
+ end
154
+
155
+ class << self
156
+
157
+ # Computes the final size of a chunked signed stream.
158
+ # @param [Integer] size Size of the original, unsigned stream.
159
+ # @return [Integer]
160
+ def signed_size size
161
+ full_sized_chunks = size / DEFAULT_CHUNK_SIZE
162
+ trailing_bytes = size % DEFAULT_CHUNK_SIZE
163
+ length = 0
164
+ length += full_sized_chunks * header_length(DEFAULT_CHUNK_SIZE)
165
+ length += trailing_bytes > 0 ? header_length(trailing_bytes) : 0
166
+ length += header_length(0)
167
+ length
168
+ end
169
+
170
+ private
171
+
172
+ # Computes the size of a header that prefixes a chunk. The size
173
+ # appears in the header as a string.
174
+ # @param [Integer] size
175
+ # @return [Integer]
176
+ def header_length size
177
+ size.to_s(16).length +
178
+ CHUNK_SIGNATURE_HEADER.length +
179
+ SIGNATURE_LENGTH +
180
+ CLRF.length +
181
+ size +
182
+ CLRF.length
183
+ end
184
+
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,227 @@
1
+ # Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ #
8
+ # or in the "license" file accompanying this file. This file is
9
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
10
+ # ANY KIND, either express or implied. See the License for the specific
11
+ # language governing permissions and limitations under the License.
12
+
13
+ require 'time'
14
+ require 'openssl'
15
+ require 'digest'
16
+
17
+ module MSS
18
+ module Core
19
+ module Signers
20
+ # @api private
21
+ class Version4
22
+
23
+ autoload :ChunkSignedStream, 'mss/core/signers/version_4/chunk_signed_stream'
24
+
25
+ # @api private
26
+ # SHA256 hex digest of the empty string
27
+ EMPTY_DIGEST = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
28
+
29
+ # @api private
30
+ STREAMING_CHECKSUM = "STREAMING-MSS4-HMAC-SHA256-PAYLOAD"
31
+
32
+ # @param [CredentialProviders::Provider] credentials
33
+ # @param [String] service_name
34
+ # @param [String] region
35
+ def initialize credentials, service_name, region
36
+ @credentials = credentials
37
+ @service_name = service_name
38
+ @region = region
39
+ end
40
+
41
+ # @return [CredentialProviders::Provider]
42
+ attr_reader :credentials
43
+
44
+ # @return [String]
45
+ attr_reader :service_name
46
+
47
+ # @return [String]
48
+ attr_reader :region
49
+
50
+ # @param [Http::Request] req
51
+ # @option options [Boolean] :chunk_signing (false) When +true+, the
52
+ # request body will be signed in chunk.
53
+ # @option options [DateTime String<YYYYMMDDTHHMMSSZ>] :datetime
54
+ # @return [Http::Request]
55
+ def sign_request req, options = {}
56
+ datetime = options[:datetime] || Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
57
+ key = derive_key(datetime)
58
+ token = credentials.session_token
59
+ chunk_signing = !!options[:chunk_signing]
60
+ content_sha256 = req.headers['x-amz-content-sha256'] || body_digest(req, chunk_signing)
61
+
62
+ req.headers['host'] = req.host
63
+ req.headers['x-amz-date'] = datetime
64
+ req.headers['x-amz-security-token'] = token if token
65
+ req.headers['x-amz-content-sha256'] = content_sha256
66
+
67
+ if chunk_signing
68
+ orig_size = req.headers['content-length'].to_i
69
+ signed_size = ChunkSignedStream.signed_size(orig_size.to_i)
70
+ req.headers['content-length'] = signed_size.to_s
71
+ req.headers['x-amz-decoded-content-length'] = orig_size.to_s
72
+ end
73
+
74
+ req.headers['authorization'] = authorization(req, key, datetime, content_sha256)
75
+
76
+ req.body_stream = chunk_signed_stream(req, key) if chunk_signing
77
+
78
+ req
79
+ end
80
+
81
+ def signature(request, key, datetime, content_sha256)
82
+ string = string_to_sign(request, datetime, content_sha256)
83
+ hexhmac(key, string)
84
+ end
85
+
86
+ def credential(datetime)
87
+ "#{credentials.access_key_id}/#{key_path(datetime)}"
88
+ end
89
+
90
+ def derive_key(datetime)
91
+ k_secret = credentials.secret_access_key
92
+ k_date = hmac("MSS4" + k_secret, datetime[0,8])
93
+ k_region = hmac(k_date, region)
94
+ k_service = hmac(k_region, service_name)
95
+ k_credentials = hmac(k_service, 'mss4_request')
96
+ end
97
+
98
+ private
99
+
100
+ # Wraps the req body stream with another stream. The wrapper signs
101
+ # the original body as it is read, injecting signatures of indiviaul
102
+ # chunks into the resultant stream.
103
+ # @param [Http::Request] req
104
+ # @param [String] key
105
+ # @param [String] datetime
106
+ def chunk_signed_stream req, key
107
+ args = []
108
+ args << req.body_stream
109
+ args << req.headers['x-amz-decoded-content-length'].to_i
110
+ args << key
111
+ args << key_path(req.headers['x-amz-date'])
112
+ args << req.headers['x-amz-date']
113
+ args << req.headers['authorization'].split('Signature=')[1]
114
+ ChunkSignedStream.new(*args)
115
+ end
116
+
117
+ def authorization req, key, datetime, content_sha256
118
+ parts = []
119
+ parts << "MSS4-HMAC-SHA256 Credential=#{credential(datetime)}"
120
+ parts << "SignedHeaders=#{signed_headers(req)}"
121
+ parts << "Signature=#{signature(req, key, datetime, content_sha256)}"
122
+ parts.join(', ')
123
+ end
124
+
125
+ def string_to_sign req, datetime, content_sha256
126
+ parts = []
127
+ parts << 'MSS4-HMAC-SHA256'
128
+ parts << datetime
129
+ parts << key_path(datetime)
130
+ parts << hexdigest(canonical_request(req, content_sha256))
131
+ parts.join("\n")
132
+ end
133
+
134
+ # @param [String] datetime
135
+ # @return [String] the signature scope.
136
+ def key_path datetime
137
+ parts = []
138
+ parts << datetime[0,8]
139
+ parts << region
140
+ parts << service_name
141
+ parts << 'mss4_request'
142
+ parts.join("/")
143
+ end
144
+
145
+ # @param [Http::Request] req
146
+ def canonical_request req, content_sha256
147
+ parts = []
148
+ parts << req.http_method
149
+ parts << req.path
150
+ parts << req.querystring
151
+ parts << canonical_headers(req) + "\n"
152
+ parts << signed_headers(req)
153
+ parts << content_sha256
154
+ parts.join("\n")
155
+ end
156
+
157
+ # @param [Http::Request] req
158
+ def signed_headers req
159
+ to_sign = req.headers.keys.map{|k| k.to_s.downcase }
160
+ to_sign.delete('authorization')
161
+ to_sign.sort.join(";")
162
+ end
163
+
164
+ # @param [Http::Request] req
165
+ def canonical_headers req
166
+ headers = []
167
+ req.headers.each_pair do |k,v|
168
+ headers << [k,v] unless k == 'authorization'
169
+ end
170
+ headers = headers.sort_by(&:first)
171
+ headers.map{|k,v| "#{k}:#{canonical_header_values(v)}" }.join("\n")
172
+ end
173
+
174
+ # @param [String,Array<String>] values
175
+ def canonical_header_values values
176
+ values = [values] unless values.is_a?(Array)
177
+ values.map(&:to_s).join(',').gsub(/\s+/, ' ').strip
178
+ end
179
+
180
+ # @param [Http::Request] req
181
+ # @param [Boolean] chunk_signing
182
+ # @return [String]
183
+ def body_digest req, chunk_signing
184
+ case
185
+ when chunk_signing then STREAMING_CHECKSUM
186
+ when ['', nil].include?(req.body) then EMPTY_DIGEST
187
+ else hexdigest(req.body)
188
+ end
189
+ end
190
+
191
+ # @param [String] value
192
+ # @return [String]
193
+ def hexdigest value
194
+ digest = OpenSSL::Digest::SHA256.new
195
+ if value.respond_to?(:read)
196
+ chunk = nil
197
+ chunk_size = 1024 * 1024 # 1 megabyte
198
+ digest.update(chunk) while chunk = value.read(chunk_size)
199
+ value.rewind
200
+ else
201
+ digest.update(value)
202
+ end
203
+ digest.hexdigest
204
+ end
205
+
206
+ # @param [String] key
207
+ # @param [String] value
208
+ # @return [String]
209
+ def hmac key, value
210
+ OpenSSL::HMAC.digest(sha256_digest, key, value)
211
+ end
212
+
213
+ # @param [String] key
214
+ # @param [String] value
215
+ # @return [String]
216
+ def hexhmac key, value
217
+ OpenSSL::HMAC.hexdigest(sha256_digest, key, value)
218
+ end
219
+
220
+ def sha256_digest
221
+ OpenSSL::Digest.new('sha256')
222
+ end
223
+
224
+ end
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,43 @@
1
+ # Copyright 2011-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ #
8
+ # or in the "license" file accompanying this file. This file is
9
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
10
+ # ANY KIND, either express or implied. See the License for the specific
11
+ # language governing permissions and limitations under the License.
12
+
13
+ require 'cgi'
14
+
15
+ module MSS
16
+ module Core
17
+
18
+ # Provides helper methods for URI escaping values and paths.
19
+ module UriEscape
20
+
21
+ # @param [String] value
22
+ # @return [String] Returns a URI escaped string.
23
+ def escape value
24
+ value = value.encode("UTF-8") if value.respond_to?(:encode)
25
+ CGI::escape(value.to_s).gsub('+', '%20').gsub('%7E', '~')
26
+ end
27
+ module_function :escape
28
+
29
+ # @param [String] value
30
+ # @return [String] Returns a URI-escaped path without escaping the
31
+ # separators.
32
+ def escape_path value
33
+ escaped = ""
34
+ value.scan(%r{(/*)([^/]*)(/*)}) do |(leading, part, trailing)|
35
+ escaped << leading + escape(part) + trailing
36
+ end
37
+ escaped
38
+ end
39
+ module_function :escape_path
40
+
41
+ end
42
+ end
43
+ end