aws-sdk-s3 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/lib/aws-sdk-s3.rb +66 -0
  3. data/lib/aws-sdk-s3/bucket.rb +595 -0
  4. data/lib/aws-sdk-s3/bucket_acl.rb +168 -0
  5. data/lib/aws-sdk-s3/bucket_cors.rb +146 -0
  6. data/lib/aws-sdk-s3/bucket_lifecycle.rb +164 -0
  7. data/lib/aws-sdk-s3/bucket_logging.rb +142 -0
  8. data/lib/aws-sdk-s3/bucket_notification.rb +187 -0
  9. data/lib/aws-sdk-s3/bucket_policy.rb +138 -0
  10. data/lib/aws-sdk-s3/bucket_region_cache.rb +79 -0
  11. data/lib/aws-sdk-s3/bucket_request_payment.rb +128 -0
  12. data/lib/aws-sdk-s3/bucket_tagging.rb +143 -0
  13. data/lib/aws-sdk-s3/bucket_versioning.rb +188 -0
  14. data/lib/aws-sdk-s3/bucket_website.rb +177 -0
  15. data/lib/aws-sdk-s3/client.rb +3171 -0
  16. data/lib/aws-sdk-s3/client_api.rb +1991 -0
  17. data/lib/aws-sdk-s3/customizations.rb +29 -0
  18. data/lib/aws-sdk-s3/customizations/bucket.rb +127 -0
  19. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +42 -0
  20. data/lib/aws-sdk-s3/customizations/object.rb +257 -0
  21. data/lib/aws-sdk-s3/customizations/object_summary.rb +65 -0
  22. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +11 -0
  23. data/lib/aws-sdk-s3/encryption.rb +19 -0
  24. data/lib/aws-sdk-s3/encryption/client.rb +369 -0
  25. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +178 -0
  26. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +63 -0
  27. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +38 -0
  28. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +50 -0
  29. data/lib/aws-sdk-s3/encryption/errors.rb +13 -0
  30. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +50 -0
  31. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +29 -0
  32. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +69 -0
  33. data/lib/aws-sdk-s3/encryption/key_provider.rb +29 -0
  34. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +71 -0
  35. data/lib/aws-sdk-s3/encryption/materials.rb +58 -0
  36. data/lib/aws-sdk-s3/encryption/utils.rb +79 -0
  37. data/lib/aws-sdk-s3/errors.rb +23 -0
  38. data/lib/aws-sdk-s3/file_part.rb +75 -0
  39. data/lib/aws-sdk-s3/file_uploader.rb +58 -0
  40. data/lib/aws-sdk-s3/legacy_signer.rb +186 -0
  41. data/lib/aws-sdk-s3/multipart_file_uploader.rb +187 -0
  42. data/lib/aws-sdk-s3/multipart_upload.rb +287 -0
  43. data/lib/aws-sdk-s3/multipart_upload_error.rb +16 -0
  44. data/lib/aws-sdk-s3/multipart_upload_part.rb +314 -0
  45. data/lib/aws-sdk-s3/object.rb +942 -0
  46. data/lib/aws-sdk-s3/object_acl.rb +214 -0
  47. data/lib/aws-sdk-s3/object_copier.rb +99 -0
  48. data/lib/aws-sdk-s3/object_multipart_copier.rb +179 -0
  49. data/lib/aws-sdk-s3/object_summary.rb +794 -0
  50. data/lib/aws-sdk-s3/object_version.rb +406 -0
  51. data/lib/aws-sdk-s3/plugins/accelerate.rb +92 -0
  52. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +89 -0
  53. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +23 -0
  54. data/lib/aws-sdk-s3/plugins/dualstack.rb +70 -0
  55. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +29 -0
  56. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +23 -0
  57. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +47 -0
  58. data/lib/aws-sdk-s3/plugins/location_constraint.rb +33 -0
  59. data/lib/aws-sdk-s3/plugins/md5s.rb +79 -0
  60. data/lib/aws-sdk-s3/plugins/redirects.rb +41 -0
  61. data/lib/aws-sdk-s3/plugins/s3_signer.rb +208 -0
  62. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +68 -0
  63. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +94 -0
  64. data/lib/aws-sdk-s3/presigned_post.rb +647 -0
  65. data/lib/aws-sdk-s3/presigner.rb +160 -0
  66. data/lib/aws-sdk-s3/resource.rb +96 -0
  67. data/lib/aws-sdk-s3/types.rb +5750 -0
  68. data/lib/aws-sdk-s3/waiters.rb +178 -0
  69. metadata +154 -0
@@ -0,0 +1,68 @@
1
+ require 'uri'
2
+ require 'openssl'
3
+
4
+ module Aws
5
+ module S3
6
+ module Plugins
7
+ class SseCpk < Seahorse::Client::Plugin
8
+
9
+ option(:require_https_for_sse_cpk,
10
+ default: true,
11
+ doc_type: 'Boolean',
12
+ docstring: <<-DOCS)
13
+ When `true`, the endpoint **must** be HTTPS for all operations
14
+ where server-side-encryption is used with customer-provided keys.
15
+ This should only be disabled for local testing.
16
+ DOCS
17
+
18
+ class Handler < Seahorse::Client::Handler
19
+
20
+ def call(context)
21
+ compute_key_md5(context)
22
+ @handler.call(context)
23
+ end
24
+
25
+ private
26
+
27
+ def compute_key_md5(context)
28
+ params = context.params
29
+ if key = params[:sse_customer_key]
30
+ require_https(context)
31
+ params[:sse_customer_key] = base64(key)
32
+ params[:sse_customer_key_md5] = base64(md5(key))
33
+ end
34
+ if key = params[:copy_source_sse_customer_key]
35
+ require_https(context)
36
+ params[:copy_source_sse_customer_key] = base64(key)
37
+ params[:copy_source_sse_customer_key_md5] = base64(md5(key))
38
+ end
39
+ end
40
+
41
+ def require_https(context)
42
+ unless URI::HTTPS === context.config.endpoint
43
+ msg = <<-MSG.strip.gsub("\n", ' ')
44
+ Attempting to send customer-provided-keys for S3
45
+ server-side-encryption over HTTP; Please configure a HTTPS
46
+ endpoint. If you are attempting to use a test endpoint,
47
+ you can disable this check via `:require_https_for_sse_cpk`
48
+ MSG
49
+ raise ArgumentError, msg
50
+ end
51
+ end
52
+
53
+ def md5(str)
54
+ OpenSSL::Digest::MD5.digest(str)
55
+ end
56
+
57
+ def base64(str)
58
+ Base64.encode64(str).strip
59
+ end
60
+
61
+ end
62
+
63
+ handler(Handler, step: :initialize)
64
+
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,94 @@
1
+ require 'uri'
2
+
3
+ module Aws
4
+ module S3
5
+ module Plugins
6
+
7
+ # This plugin auto-populates the `:encoding_type` request parameter
8
+ # to all calls made to Amazon S3 that accept it.
9
+ #
10
+ # This enables Amazon S3 to return object keys that might contain
11
+ # invalid XML characters as URL encoded strings. This plugin also
12
+ # automatically decodes these keys so that the key management is
13
+ # transparent to the user.
14
+ #
15
+ # If you specify the `:encoding_type` parameter, then this plugin
16
+ # will be disabled, and you will need to decode the keys yourself.
17
+ #
18
+ # The following operations are managed:
19
+ #
20
+ # * {S3::Client#list_objects}
21
+ # * {S3::Client#list_multipart_uploads}
22
+ # * {S3::Client#list_object_versions}
23
+ #
24
+ class UrlEncodedKeys < Seahorse::Client::Plugin
25
+
26
+ class Handler < Seahorse::Client::Handler
27
+
28
+ def call(context)
29
+ if context.params.key?(:encoding_type)
30
+ @handler.call(context) # user managed
31
+ else
32
+ manage_keys(context)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def manage_keys(context)
39
+ context.params[:encoding_type] = 'url'
40
+ @handler.call(context).on_success do |resp|
41
+ send("decode_#{resp.context.operation_name}_keys", resp.data)
42
+ end
43
+ end
44
+
45
+ def decode_list_objects_keys(data)
46
+ decode(:marker, data)
47
+ decode(:next_marker, data)
48
+ decode(:prefix, data)
49
+ decode(:delimiter, data)
50
+ data.contents.each { |o| decode(:key, o) } if data.contents
51
+ data.common_prefixes.each { |o| decode(:prefix, o) } if data.common_prefixes
52
+ end
53
+
54
+ def decode_list_object_versions_keys(data)
55
+ decode(:key_marker, data)
56
+ decode(:next_key_marker, data)
57
+ decode(:prefix, data)
58
+ decode(:delimiter, data)
59
+ data.versions.each { |o| decode(:key, o) } if data.versions
60
+ data.delete_markers.each { |o| decode(:key, o) } if data.delete_markers
61
+ data.common_prefixes.each { |o| decode(:prefix, o) } if data.common_prefixes
62
+ end
63
+
64
+ def decode_list_multipart_uploads_keys(data)
65
+ decode(:key_marker, data)
66
+ decode(:next_key_marker, data)
67
+ decode(:prefix, data)
68
+ decode(:delimiter, data)
69
+ data.uploads.each { |o| decode(:key, o) } if data.uploads
70
+ data.common_prefixes.each { |o| decode(:prefix, o) } if data.common_prefixes
71
+ end
72
+
73
+ def decode(member, struct)
74
+ if struct[member]
75
+ struct[member] = CGI.unescape(struct[member])
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+ handler(Handler,
82
+ step: :validate,
83
+ priority: 0,
84
+ operations: [
85
+ :list_objects,
86
+ :list_object_versions,
87
+ :list_multipart_uploads,
88
+ ]
89
+ )
90
+
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,647 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ module Aws
5
+ module S3
6
+
7
+ # @note Normally you do not need to construct a {PresignedPost} yourself.
8
+ # See {Bucket#presigned_post} and {Object#presigned_post}.
9
+ #
10
+ # ## Basic Usage
11
+ #
12
+ # To generate a presigned post, you need AWS credentials, the region
13
+ # your bucket is in, and the name of your bucket. You can apply constraints
14
+ # to the post object as options to {#initialize} or by calling
15
+ # methods such as {#key} and {#content_length_range}.
16
+ #
17
+ # The following two examples are equivalent.
18
+ #
19
+ # ```ruby
20
+ # post = Aws::S3::PresignedPost.new(creds, region, bucket, {
21
+ # key: '/uploaded/object/key',
22
+ # content_length_range: 0..1024,
23
+ # acl: 'public-read',
24
+ # metadata: {
25
+ # 'original-filename' => '${filename}'
26
+ # }
27
+ # })
28
+ # post.fields
29
+ # #=> { ... }
30
+ #
31
+ # post = Aws::S3::PresignedPost.new(creds, region, bucket).
32
+ # key('/uploaded/object/key').
33
+ # content_length_range(0..1024).
34
+ # acl('public-read').
35
+ # metadata('original-filename' => '${filename}').
36
+ # fields
37
+ # #=> { ... }
38
+ # ```
39
+ #
40
+ # ## HTML Forms
41
+ #
42
+ # You can use a {PresignedPost} object to build an HTML form. It is
43
+ # recommended to use some helper to build the form tag and input
44
+ # tags that properly escapes values.
45
+ #
46
+ # ### Form Tag
47
+ #
48
+ # To upload a file to Amazon S3 using a browser, you need to create
49
+ # a post form. The {#url} method returns the value you should use
50
+ # as the form action.
51
+ #
52
+ # ```erb
53
+ # <form action="<%= @post.url %>" method="post" enctype="multipart/form-data">
54
+ # ...
55
+ # </form>
56
+ # ```
57
+ #
58
+ # The follow attributes must be set on the form:
59
+ #
60
+ # * `action` - This must be the {#url}.
61
+ # * `method` - This must be `post`.
62
+ # * `enctype` - This must be `multipart/form-data`.
63
+ #
64
+ # ### Form Fields
65
+ #
66
+ # The {#fields} method returns a hash of form fields to render inside
67
+ # the form. Typically these are rendered as hidden input fields.
68
+ #
69
+ # ```erb
70
+ # <% @post.fields.each do |name, value| %>
71
+ # <input type="hidden" name="<%= name %>" value="<%= value %>"/>
72
+ # <% end %>
73
+ # ```
74
+ #
75
+ # Lastly, the form must have a file field with the name `file`.
76
+ #
77
+ # ```erb
78
+ # <input type="file" name="file"/>
79
+ # ```
80
+ #
81
+ # ## Post Policy
82
+ #
83
+ # When you construct a {PresignedPost}, you must specify every form
84
+ # field name that will be posted by the browser. If you omit a form
85
+ # field sent by the browser, Amazon S3 will reject the request.
86
+ # You can specify accepted form field values three ways:
87
+ #
88
+ # * Specify exactly what the value must be.
89
+ # * Specify what value the field starts with.
90
+ # * Specify the field may have any value.
91
+ #
92
+ # ### Field Equals
93
+ #
94
+ # You can specify that a form field must be a certain value.
95
+ # Simply pass an option like `:content_type` to the constructor,
96
+ # or call the associated method.
97
+ #
98
+ # ```ruby
99
+ # post = Aws::S3::PresignedPost.new(creds, region, bucket).
100
+ # post.content_type('text/plain')
101
+ # ```
102
+ #
103
+ # If any of the given values are changed by the user in the form, then
104
+ # Amazon S3 will reject the POST request.
105
+ #
106
+ # ### Field Starts With
107
+ #
108
+ # You can specify prefix values for many of the POST form fields.
109
+ # To specify a required prefix, use the `:<fieldname>_starts_with`
110
+ # option or call the associated `#<field_name>_starts_with` method.
111
+ #
112
+ # ```ruby
113
+ # post = Aws::S3::PresignedPost.new(creds, region, bucket, {
114
+ # key_starts_with: '/images/',
115
+ # content_type_starts_with: 'image/',
116
+ # # ...
117
+ # })
118
+ # ```
119
+ #
120
+ # When using starts with, the form must contain a field where the
121
+ # user can specify the value. The {PresignedPost} will not add
122
+ # a value for these fields.
123
+ #
124
+ # ### Any Field Value
125
+ #
126
+ # To white-list a form field to send any value, you can name that
127
+ # field with `:allow_any` or {#allow_any}.
128
+ #
129
+ # ```ruby
130
+ # post = Aws::S3::PresignedPost.new(creds, region, bucket, {
131
+ # key: 'object-key',
132
+ # allow_any: ['Filename'],
133
+ # # ...
134
+ # })
135
+ # ```
136
+ #
137
+ # ### Metadata
138
+ #
139
+ # You can add rules for metadata fields using `:metadata`, {#metadata},
140
+ # `:metadata_starts_with` and {#metadata_starts_with}. Unlike other
141
+ # form fields, you pass a hash value to these options/methods:
142
+ #
143
+ # ```ruby
144
+ # post = Aws::S3::PresignedPost.new(creds, region, bucket).
145
+ # key('/fixed/key').
146
+ # metadata(foo: 'bar')
147
+ #
148
+ # post.fields['x-amz-meta-foo']
149
+ # #=> 'bar'
150
+ # ```
151
+ #
152
+ # ### The `${filename}` Variable
153
+ #
154
+ # The string `${filename}` is automatically replaced with the name of the
155
+ # file provided by the user and is recognized by all form fields. It is
156
+ # not supported with `starts_with` conditions.
157
+ #
158
+ # If the browser or client provides a full or partial path to the file,
159
+ # only the text following the last slash (/) or backslash (\) will be used
160
+ # (e.g., "C:\Program Files\directory1\file.txt" will be interpreted
161
+ # as "file.txt"). If no file or file name is provided, the variable is
162
+ # replaced with an empty string.
163
+ #
164
+ # In the following example, we use `${filename}` to store the original
165
+ # filename in the `x-amz-meta-` hash with the uploaded object.
166
+ #
167
+ # ```ruby
168
+ # post = Aws::S3::PresignedPost.new(creds, region, bucket, {
169
+ # key: '/fixed/key',
170
+ # metadata: {
171
+ # 'original-filename': '${filename}'
172
+ # }
173
+ # })
174
+ # ```
175
+ #
176
+ class PresignedPost
177
+
178
+ # @param [Credentials] credentials Security credentials for signing
179
+ # the post policy.
180
+ # @param [String] bucket_region Region of the target bucket.
181
+ # @param [String] bucket_name Name of the target bucket.
182
+ # @option options [Time] :signature_expiration Specify when the signature on
183
+ # the post will expire. Defaults to one hour from creation of the
184
+ # presigned post. May not exceed one week from creation time.
185
+ # @option options [String] :key See {PresignedPost#key}.
186
+ # @option options [String] :key_starts_with See {PresignedPost#key_starts_with}.
187
+ # @option options [String] :acl See {PresignedPost#acl}.
188
+ # @option options [String] :acl_starts_with See {PresignedPost#acl_starts_with}.
189
+ # @option options [String] :cache_control See {PresignedPost#cache_control}.
190
+ # @option options [String] :cache_control_starts_with See {PresignedPost#cache_control_starts_with}.
191
+ # @option options [String] :content_type See {PresignedPost#content_type}.
192
+ # @option options [String] :content_type_starts_with See {PresignedPost#content_type_starts_with}.
193
+ # @option options [String] :content_disposition See {PresignedPost#content_disposition}.
194
+ # @option options [String] :content_disposition_starts_with See {PresignedPost#content_disposition_starts_with}.
195
+ # @option options [String] :content_encoding See {PresignedPost#content_encoding}.
196
+ # @option options [String] :content_encoding_starts_with See {PresignedPost#content_encoding_starts_with}.
197
+ # @option options [String] :expires See {PresignedPost#expires}.
198
+ # @option options [String] :expires_starts_with See {PresignedPost#expires_starts_with}.
199
+ # @option options [Range<Integer>] :content_length_range See {PresignedPost#content_length_range}.
200
+ # @option options [String] :success_action_redirect See {PresignedPost#success_action_redirect}.
201
+ # @option options [String] :success_action_redirect_starts_with See {PresignedPost#success_action_redirect_starts_with}.
202
+ # @option options [String] :success_action_status See {PresignedPost#success_action_status}.
203
+ # @option options [String] :storage_class See {PresignedPost#storage_class}.
204
+ # @option options [String] :website_redirect_location See {PresignedPost#website_redirect_location}.
205
+ # @option options [Hash<String,String>] :metadata See {PresignedPost#metadata}.
206
+ # @option options [Hash<String,String>] :metadata_starts_with See {PresignedPost#metadata_starts_with}.
207
+ # @option options [String] :server_side_encryption See {PresignedPost#server_side_encryption}.
208
+ # @option options [String] :server_side_encryption_aws_kms_key_id See {PresignedPost#server_side_encryption_aws_kms_key_id}.
209
+ # @option options [String] :server_side_encryption_customer_algorithm See {PresignedPost#server_side_encryption_customer_algorithm}.
210
+ # @option options [String] :server_side_encryption_customer_key See {PresignedPost#server_side_encryption_customer_key}.
211
+ def initialize(credentials, bucket_region, bucket_name, options = {})
212
+ @credentials = credentials.credentials
213
+ @bucket_region = bucket_region
214
+ @bucket_name = bucket_name
215
+ @url = options.delete(:url) || bucket_url
216
+ @fields = {}
217
+ @key_set = false
218
+ @signature_expiration = Time.now + 3600
219
+ @conditions = [{ 'bucket' => @bucket_name }]
220
+ options.each do |option_name, option_value|
221
+ case option_name
222
+ when :allow_any then allow_any(option_value)
223
+ when :signature_expiration then @signature_expiration = option_value
224
+ else send("#{option_name}", option_value)
225
+ end
226
+ end
227
+ end
228
+
229
+ # @return [String] The URL to post a file upload to. This should be
230
+ # the form action.
231
+ attr_reader :url
232
+
233
+ # @return [Hash] A hash of fields to render in an HTML form
234
+ # as hidden input fields.
235
+ def fields
236
+ check_required_values!
237
+ datetime = Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
238
+ fields = @fields.dup
239
+ fields.update('policy' => policy(datetime))
240
+ fields.update(signature_fields(datetime))
241
+ fields.update('x-amz-signature' => signature(datetime, fields['policy']))
242
+ end
243
+
244
+ # A list of form fields to white-list with any value.
245
+ # @param [Sting, Array<String>] field_names
246
+ # @return [self]
247
+ def allow_any(*field_names)
248
+ field_names.flatten.each do |field_name|
249
+ @key_set = true if field_name.to_s == 'key'
250
+ starts_with(field_name, '')
251
+ end
252
+ self
253
+ end
254
+
255
+ # @api private
256
+ def self.define_field(field, *args)
257
+ options = args.last.is_a?(Hash) ? args.pop : {}
258
+ field_name = args.last || field.to_s
259
+
260
+ define_method("#{field}") do |value|
261
+ with(field_name, value)
262
+ end
263
+
264
+ if options[:starts_with]
265
+ define_method("#{field}_starts_with") do |value|
266
+ starts_with(field_name, value)
267
+ end
268
+ end
269
+ end
270
+
271
+ # @!group Fields
272
+
273
+ # The key to use for the uploaded object. Use can use `${filename}`
274
+ # as a variable in the key. This will be replaced with the name
275
+ # of the file as provided by the user.
276
+ #
277
+ # For example, if the key is given as `/user/betty/${filename}` and
278
+ # the file uploaded is named `lolcatz.jpg`, the resultant key will
279
+ # be `/user/betty/lolcatz.jpg`.
280
+ #
281
+ # @param [String] key
282
+ # @see http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html)
283
+ # @return [self]
284
+ def key(key)
285
+ @key_set = true
286
+ with('key', key)
287
+ end
288
+
289
+ # Specify a prefix the uploaded
290
+ # @param [String] prefix
291
+ # @see #key
292
+ # @return [self]
293
+ def key_starts_with(prefix)
294
+ @key_set = true
295
+ starts_with('key', prefix)
296
+ end
297
+
298
+ # @!method acl(canned_acl)
299
+ # Specify the cannedl ACL (access control list) for the object.
300
+ # May be one of the following values:
301
+ #
302
+ # * `private`
303
+ # * `public-read`
304
+ # * `public-read-write`
305
+ # * `authenticated-read`
306
+ # * `bucket-owner-read`
307
+ # * `bucket-owner-full-control`
308
+ #
309
+ # @param [String] canned_acl
310
+ # @see http://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html
311
+ # @return [self]
312
+ #
313
+ # @!method acl_starts_with(prefix)
314
+ # @param [String] prefix
315
+ # @see #acl
316
+ # @return [self]
317
+ define_field(:acl, starts_with: true)
318
+
319
+ # @!method cache_control(value)
320
+ # Specify caching behavior along the request/reply chain.
321
+ # @param [String] value
322
+ # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.
323
+ # @return [self]
324
+ #
325
+ # @!method cache_control_starts_with(prefix)
326
+ # @param [String] prefix
327
+ # @see #cache_control
328
+ # @return [self]
329
+ define_field(:cache_control, 'Cache-Control', starts_with: true)
330
+
331
+ # @return [String]
332
+ # @!method content_type(value)
333
+ # A standard MIME type describing the format of the contents.
334
+ # @param [String] value
335
+ # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21
336
+ # @return [self]
337
+ #
338
+ # @!method content_type_starts_with(prefix)
339
+ # @param [String] prefix
340
+ # @see #content_type
341
+ # @return [self]
342
+ define_field(:content_type, 'Content-Type', starts_with: true)
343
+
344
+ # @!method content_disposition(value)
345
+ # Specifies presentational information for the object.
346
+ # @param [String] value
347
+ # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1
348
+ # @return [self]
349
+ #
350
+ # @!method content_disposition_starts_with(prefix)
351
+ # @param [String] prefix
352
+ # @see #content_disposition
353
+ # @return [self]
354
+ define_field(:content_disposition, 'Content-Disposition', starts_with: true)
355
+
356
+ # @!method content_encoding(value)
357
+ # Specifies what content encodings have been applied to the object
358
+ # and thus what decoding mechanisms must be applied to obtain the
359
+ # media-type referenced by the Content-Type header field.
360
+ # @param [String] value
361
+ # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
362
+ # @return [self]
363
+ #
364
+ # @!method content_encoding_starts_with(prefix)
365
+ # @param [String] prefix
366
+ # @see #content_encoding
367
+ # @return [self]
368
+ define_field(:content_encoding, 'Content-Encoding', starts_with: true)
369
+
370
+ # The date and time at which the object is no longer cacheable.
371
+ # @note This does not affect the expiration of the presigned post
372
+ # signature.
373
+ # @param [Time] time
374
+ # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21
375
+ # @return [self]
376
+ def expires(time)
377
+ with('Expires', time.httpdate)
378
+ end
379
+
380
+ # @param [String] prefix
381
+ # @see #expires
382
+ # @return [self]
383
+ def expires_starts_with(prefix)
384
+ starts_with('Expires', prefix)
385
+ end
386
+
387
+ # The minimum and maximum allowable size for the uploaded content.
388
+ # @param [Range<Integer>] byte_range
389
+ # @return [self]
390
+ def content_length_range(byte_range)
391
+ min = byte_range.begin
392
+ max = byte_range.end
393
+ max -= 1 if byte_range.exclude_end?
394
+ @conditions << ['content-length-range', min, max]
395
+ self
396
+ end
397
+
398
+ # @!method success_action_redirect(value)
399
+ # The URL to which the client is redirected
400
+ # upon successful upload. If {#success_action_redirect} is not
401
+ # specified, Amazon S3 returns the empty document type specified
402
+ # by {#success_action_status}.
403
+ #
404
+ # If Amazon S3 cannot interpret the URL, it acts as if the field
405
+ # is not present. If the upload fails, Amazon S3 displays an error
406
+ # and does not redirect the user to a URL.
407
+ #
408
+ # @param [String] value
409
+ # @return [self]
410
+ #
411
+ # @!method success_action_redirect_starts_with(prefix)
412
+ # @param [String] prefix
413
+ # @see #success_action_redirect
414
+ # @return [self]
415
+ define_field(:success_action_redirect, starts_with: true)
416
+
417
+ # @!method success_action_status(value)
418
+ # The status code returned to the client upon
419
+ # successful upload if {#success_action_redirect} is not
420
+ # specified.
421
+ #
422
+ # Accepts the values `200`, `201`, or `204` (default).
423
+ #
424
+ # If the value is set to 200 or 204, Amazon S3 returns an empty
425
+ # document with a 200 or 204 status code. If the value is set to 201,
426
+ # Amazon S3 returns an XML document with a 201 status code.
427
+ #
428
+ # If the value is not set or if it is set to an invalid value, Amazon
429
+ # S3 returns an empty document with a 204 status code.
430
+ #
431
+ # @param [String] value The status code returned to the client upon
432
+ # @return [self]
433
+ define_field(:success_action_status)
434
+
435
+ # @!method storage_class(value)
436
+ # Storage class to use for storing the object. Defaults to
437
+ # `STANDARD`. Must be one of:
438
+ #
439
+ # * `STANDARD`
440
+ # * `REDUCED_REDUNDANCY`
441
+ #
442
+ # You cannot specify `GLACIER` as the storage class. To transition
443
+ # objects to the GLACIER storage class you can use lifecycle
444
+ # configuration.
445
+ # @param [String] value Storage class to use for storing the
446
+ # @return [self]
447
+ define_field(:storage_class, 'x-amz-storage-class')
448
+
449
+ # @!method website_redirect_location(value)
450
+ # If the bucket is configured as a website,
451
+ # redirects requests for this object to another object in the
452
+ # same bucket or to an external URL. Amazon S3 stores this value
453
+ # in the object metadata.
454
+ #
455
+ # The value must be prefixed by, "/", "http://" or "https://".
456
+ # The length of the value is limited to 2K.
457
+ #
458
+ # @param [String] value
459
+ # @see http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html
460
+ # @see http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html
461
+ # @see http://docs.aws.amazon.com/AmazonS3/latest/dev/how-to-page-redirect.html
462
+ # @return [self]
463
+ define_field(:website_redirect_location, 'x-amz-website-redirect-location')
464
+
465
+ # Metadata hash to store with the uploaded object. Hash keys will be
466
+ # prefixed with "x-amz-meta-".
467
+ # @param [Hash<String,String>] hash
468
+ # @return [self]
469
+ def metadata(hash)
470
+ hash.each do |key, value|
471
+ with("x-amz-meta-#{key}", value)
472
+ end
473
+ self
474
+ end
475
+
476
+ # Specify allowable prefix for each key in the metadata hash.
477
+ # @param [Hash<String,String>] hash
478
+ # @see #metadata
479
+ # @return [self]
480
+ def metadata_starts_with(hash)
481
+ hash.each do |key, value|
482
+ starts_with("x-amz-meta-#{key}", value)
483
+ end
484
+ self
485
+ end
486
+
487
+ # @!endgroup
488
+
489
+ # @!group Server-Side Encryption Fields
490
+
491
+ # @!method server_side_encryption(value)
492
+ # Specifies a server-side encryption algorithm to use when Amazon
493
+ # S3 creates an object. Valid values include:
494
+ #
495
+ # * `aws:kms`
496
+ # * `AES256`
497
+ #
498
+ # @param [String] value
499
+ # @return [self]
500
+ define_field(:server_side_encryption, 'x-amz-server-side-encryption')
501
+
502
+ # @!method server_side_encryption_aws_kms_key_id(value)
503
+ # If {#server_side_encryption} is called with the value of `aws:kms`,
504
+ # this method specifies the ID of the AWS Key Management Service
505
+ # (KMS) master encryption key to use for the object.
506
+ # @param [String] value
507
+ # @return [self]
508
+ define_field(:server_side_encryption_aws_kms_key_id, 'x-amz-server-side-encryption-aws-kms-key-id')
509
+
510
+ # @!endgroup
511
+
512
+ # @!group Server-Side Encryption with Customer-Provided Key Fields
513
+
514
+ # @!method server_side_encryption_customer_algorithm(value)
515
+ # Specifies the algorithm to use to when encrypting the object.
516
+ # Must be set to `AES256` when using customer-provided encryption
517
+ # keys. Must also call {#server_side_encryption_customer_key}.
518
+ # @param [String] value
519
+ # @see #server_side_encryption_customer_key
520
+ # @return [self]
521
+ define_field(:server_side_encryption_customer_algorithm, 'x-amz-server-side-encryption-customer-algorithm')
522
+
523
+ # Specifies the customer-provided encryption key for Amazon S3 to use
524
+ # in encrypting data. This value is used to store the object and then
525
+ # it is discarded; Amazon does not store the encryption key.
526
+ #
527
+ # You must also call {#server_side_encryption_customer_algorithm}.
528
+ #
529
+ # @param [String] value
530
+ # @see #server_side_encryption_customer_algorithm
531
+ # @return [self]
532
+ def server_side_encryption_customer_key(value)
533
+ field_name = 'x-amz-server-side-encryption-customer-key'
534
+ with(field_name, base64(value))
535
+ with(field_name + '-MD5', base64(OpenSSL::Digest::MD5.digest(value)))
536
+ end
537
+
538
+ # @param [String] prefix
539
+ # @see #server_side_encryption_customer_key
540
+ # @return [self]
541
+ def server_side_encryption_customer_key_starts_with(prefix)
542
+ field_name = 'x-amz-server-side-encryption-customer-key'
543
+ starts_with(field_name, prefix)
544
+ end
545
+
546
+ # @!endgroup
547
+
548
+ private
549
+
550
+ def with(field_name, value)
551
+ fvar = '${filename}'
552
+ if index = value.rindex(fvar)
553
+ if index + fvar.size == value.size
554
+ @fields[field_name] = value
555
+ starts_with(field_name, value[0,index])
556
+ else
557
+ msg = "${filename} only supported at the end of #{field_name}"
558
+ raise ArgumentError, msg
559
+ end
560
+ else
561
+ @fields[field_name] = value.to_s
562
+ @conditions << { field_name => value.to_s }
563
+ end
564
+ self
565
+ end
566
+
567
+ def starts_with(field_name, value, &block)
568
+ @conditions << ['starts-with', "$#{field_name}", value.to_s]
569
+ self
570
+ end
571
+
572
+ def check_required_values!
573
+ unless @key_set
574
+ msg = "key required; you must provide a key via :key, "
575
+ msg << ":key_starts_with, or :allow_any => ['key']"
576
+ raise msg
577
+ end
578
+ end
579
+
580
+ def bucket_url
581
+ url = Aws::Partitions::EndpointProvider.resolve(@bucket_region, 's3')
582
+ url = URI.parse(url)
583
+ if Plugins::BucketDns.dns_compatible?(@bucket_name, true)
584
+ url.host = @bucket_name + '.' + url.host
585
+ else
586
+ url.path = '/' + @bucket_name
587
+ end
588
+ url.to_s
589
+ end
590
+
591
+ # @return [Hash]
592
+ def policy(datetime)
593
+ check_required_values!
594
+ policy = {}
595
+ policy['expiration'] = @signature_expiration.utc.iso8601
596
+ policy['conditions'] = @conditions.dup
597
+ signature_fields(datetime).each do |name, value|
598
+ policy['conditions'] << { name => value }
599
+ end
600
+ base64(Json.dump(policy))
601
+ end
602
+
603
+ def signature_fields(datetime)
604
+ fields = {}
605
+ fields['x-amz-credential'] = credential_scope(datetime)
606
+ fields['x-amz-algorithm'] = 'AWS4-HMAC-SHA256'
607
+ fields['x-amz-date'] = datetime
608
+ if session_token = @credentials.session_token
609
+ fields['x-amz-security-token'] = session_token
610
+ end
611
+ fields
612
+ end
613
+
614
+ def signature(datetime, string_to_sign)
615
+ k_secret = @credentials.secret_access_key
616
+ k_date = hmac("AWS4" + k_secret, datetime[0,8])
617
+ k_region = hmac(k_date, @bucket_region)
618
+ k_service = hmac(k_region, 's3')
619
+ k_credentials = hmac(k_service, 'aws4_request')
620
+ hexhmac(k_credentials, string_to_sign)
621
+ end
622
+
623
+ def hmac(key, value)
624
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, value)
625
+ end
626
+
627
+ def hexhmac(key, value)
628
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, value)
629
+ end
630
+
631
+ def credential_scope(datetime)
632
+ parts = []
633
+ parts << @credentials.access_key_id
634
+ parts << datetime[0,8]
635
+ parts << @bucket_region
636
+ parts << 's3'
637
+ parts << 'aws4_request'
638
+ parts.join('/')
639
+ end
640
+
641
+ def base64(str)
642
+ Base64.strict_encode64(str)
643
+ end
644
+
645
+ end
646
+ end
647
+ end