aws-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 (205) hide show
  1. data/.yardopts +6 -0
  2. data/LICENSE.txt +171 -0
  3. data/NOTICE.txt +2 -0
  4. data/README.rdoc +189 -0
  5. data/lib/aws-sdk.rb +14 -0
  6. data/lib/aws.rb +63 -0
  7. data/lib/aws/api_config.rb +45 -0
  8. data/lib/aws/api_config/.document +0 -0
  9. data/lib/aws/api_config/EC2-2011-02-28.yml +2314 -0
  10. data/lib/aws/api_config/SNS-2010-03-31.yml +171 -0
  11. data/lib/aws/api_config/SQS-2009-02-01.yml +161 -0
  12. data/lib/aws/api_config/SimpleDB-2009-04-15.yml +278 -0
  13. data/lib/aws/api_config/SimpleEmailService-2010-12-01.yml +147 -0
  14. data/lib/aws/api_config_transform.rb +32 -0
  15. data/lib/aws/async_handle.rb +90 -0
  16. data/lib/aws/authorize_v2.rb +37 -0
  17. data/lib/aws/authorize_v3.rb +37 -0
  18. data/lib/aws/base_client.rb +524 -0
  19. data/lib/aws/cacheable.rb +92 -0
  20. data/lib/aws/common.rb +228 -0
  21. data/lib/aws/configurable.rb +36 -0
  22. data/lib/aws/configuration.rb +272 -0
  23. data/lib/aws/configured_client_methods.rb +81 -0
  24. data/lib/aws/configured_grammars.rb +65 -0
  25. data/lib/aws/configured_option_grammars.rb +46 -0
  26. data/lib/aws/configured_xml_grammars.rb +47 -0
  27. data/lib/aws/default_signer.rb +38 -0
  28. data/lib/aws/ec2.rb +321 -0
  29. data/lib/aws/ec2/attachment.rb +149 -0
  30. data/lib/aws/ec2/attachment_collection.rb +57 -0
  31. data/lib/aws/ec2/availability_zone.rb +80 -0
  32. data/lib/aws/ec2/availability_zone_collection.rb +47 -0
  33. data/lib/aws/ec2/block_device_mappings.rb +53 -0
  34. data/lib/aws/ec2/client.rb +54 -0
  35. data/lib/aws/ec2/client/xml.rb +127 -0
  36. data/lib/aws/ec2/collection.rb +39 -0
  37. data/lib/aws/ec2/config_transform.rb +63 -0
  38. data/lib/aws/ec2/elastic_ip.rb +107 -0
  39. data/lib/aws/ec2/elastic_ip_collection.rb +85 -0
  40. data/lib/aws/ec2/errors.rb +29 -0
  41. data/lib/aws/ec2/filtered_collection.rb +65 -0
  42. data/lib/aws/ec2/has_permissions.rb +46 -0
  43. data/lib/aws/ec2/image.rb +245 -0
  44. data/lib/aws/ec2/image_collection.rb +235 -0
  45. data/lib/aws/ec2/instance.rb +515 -0
  46. data/lib/aws/ec2/instance_collection.rb +276 -0
  47. data/lib/aws/ec2/key_pair.rb +86 -0
  48. data/lib/aws/ec2/key_pair_collection.rb +102 -0
  49. data/lib/aws/ec2/permission_collection.rb +177 -0
  50. data/lib/aws/ec2/region.rb +81 -0
  51. data/lib/aws/ec2/region_collection.rb +55 -0
  52. data/lib/aws/ec2/request.rb +27 -0
  53. data/lib/aws/ec2/reserved_instances.rb +50 -0
  54. data/lib/aws/ec2/reserved_instances_collection.rb +44 -0
  55. data/lib/aws/ec2/reserved_instances_offering.rb +55 -0
  56. data/lib/aws/ec2/reserved_instances_offering_collection.rb +43 -0
  57. data/lib/aws/ec2/resource.rb +340 -0
  58. data/lib/aws/ec2/resource_tag_collection.rb +218 -0
  59. data/lib/aws/ec2/security_group.rb +246 -0
  60. data/lib/aws/ec2/security_group/ip_permission.rb +70 -0
  61. data/lib/aws/ec2/security_group/ip_permission_collection.rb +59 -0
  62. data/lib/aws/ec2/security_group_collection.rb +132 -0
  63. data/lib/aws/ec2/snapshot.rb +138 -0
  64. data/lib/aws/ec2/snapshot_collection.rb +90 -0
  65. data/lib/aws/ec2/tag.rb +88 -0
  66. data/lib/aws/ec2/tag_collection.rb +114 -0
  67. data/lib/aws/ec2/tagged_collection.rb +48 -0
  68. data/lib/aws/ec2/tagged_item.rb +87 -0
  69. data/lib/aws/ec2/volume.rb +190 -0
  70. data/lib/aws/ec2/volume_collection.rb +95 -0
  71. data/lib/aws/errors.rb +129 -0
  72. data/lib/aws/http/builtin_handler.rb +69 -0
  73. data/lib/aws/http/curb_handler.rb +123 -0
  74. data/lib/aws/http/handler.rb +77 -0
  75. data/lib/aws/http/httparty_handler.rb +61 -0
  76. data/lib/aws/http/request.rb +136 -0
  77. data/lib/aws/http/request_param.rb +63 -0
  78. data/lib/aws/http/response.rb +75 -0
  79. data/lib/aws/ignore_result_element.rb +38 -0
  80. data/lib/aws/indifferent_hash.rb +86 -0
  81. data/lib/aws/inflection.rb +46 -0
  82. data/lib/aws/lazy_error_classes.rb +64 -0
  83. data/lib/aws/meta_utils.rb +43 -0
  84. data/lib/aws/model.rb +57 -0
  85. data/lib/aws/naming.rb +32 -0
  86. data/lib/aws/option_grammar.rb +544 -0
  87. data/lib/aws/policy.rb +912 -0
  88. data/lib/aws/rails.rb +209 -0
  89. data/lib/aws/record.rb +79 -0
  90. data/lib/aws/record/attribute.rb +94 -0
  91. data/lib/aws/record/attribute_macros.rb +288 -0
  92. data/lib/aws/record/attributes/boolean.rb +49 -0
  93. data/lib/aws/record/attributes/datetime.rb +86 -0
  94. data/lib/aws/record/attributes/float.rb +48 -0
  95. data/lib/aws/record/attributes/integer.rb +68 -0
  96. data/lib/aws/record/attributes/sortable_float.rb +60 -0
  97. data/lib/aws/record/attributes/sortable_integer.rb +95 -0
  98. data/lib/aws/record/attributes/string.rb +69 -0
  99. data/lib/aws/record/base.rb +728 -0
  100. data/lib/aws/record/conversion.rb +38 -0
  101. data/lib/aws/record/dirty_tracking.rb +286 -0
  102. data/lib/aws/record/errors.rb +153 -0
  103. data/lib/aws/record/exceptions.rb +48 -0
  104. data/lib/aws/record/finder_methods.rb +262 -0
  105. data/lib/aws/record/naming.rb +31 -0
  106. data/lib/aws/record/scope.rb +157 -0
  107. data/lib/aws/record/validations.rb +653 -0
  108. data/lib/aws/record/validator.rb +237 -0
  109. data/lib/aws/record/validators/acceptance.rb +51 -0
  110. data/lib/aws/record/validators/block.rb +38 -0
  111. data/lib/aws/record/validators/confirmation.rb +43 -0
  112. data/lib/aws/record/validators/count.rb +108 -0
  113. data/lib/aws/record/validators/exclusion.rb +43 -0
  114. data/lib/aws/record/validators/format.rb +57 -0
  115. data/lib/aws/record/validators/inclusion.rb +56 -0
  116. data/lib/aws/record/validators/length.rb +107 -0
  117. data/lib/aws/record/validators/numericality.rb +138 -0
  118. data/lib/aws/record/validators/presence.rb +45 -0
  119. data/lib/aws/resource_cache.rb +39 -0
  120. data/lib/aws/response.rb +113 -0
  121. data/lib/aws/response_cache.rb +50 -0
  122. data/lib/aws/s3.rb +109 -0
  123. data/lib/aws/s3/access_control_list.rb +252 -0
  124. data/lib/aws/s3/acl_object.rb +266 -0
  125. data/lib/aws/s3/bucket.rb +320 -0
  126. data/lib/aws/s3/bucket_collection.rb +122 -0
  127. data/lib/aws/s3/bucket_version_collection.rb +85 -0
  128. data/lib/aws/s3/client.rb +999 -0
  129. data/lib/aws/s3/client/xml.rb +190 -0
  130. data/lib/aws/s3/data_options.rb +99 -0
  131. data/lib/aws/s3/errors.rb +43 -0
  132. data/lib/aws/s3/multipart_upload.rb +318 -0
  133. data/lib/aws/s3/multipart_upload_collection.rb +78 -0
  134. data/lib/aws/s3/object_collection.rb +159 -0
  135. data/lib/aws/s3/object_metadata.rb +67 -0
  136. data/lib/aws/s3/object_upload_collection.rb +83 -0
  137. data/lib/aws/s3/object_version.rb +141 -0
  138. data/lib/aws/s3/object_version_collection.rb +78 -0
  139. data/lib/aws/s3/paginated_collection.rb +94 -0
  140. data/lib/aws/s3/policy.rb +76 -0
  141. data/lib/aws/s3/prefix_and_delimiter_collection.rb +56 -0
  142. data/lib/aws/s3/prefixed_collection.rb +84 -0
  143. data/lib/aws/s3/presigned_post.rb +504 -0
  144. data/lib/aws/s3/request.rb +198 -0
  145. data/lib/aws/s3/s3_object.rb +794 -0
  146. data/lib/aws/s3/tree.rb +116 -0
  147. data/lib/aws/s3/tree/branch_node.rb +71 -0
  148. data/lib/aws/s3/tree/child_collection.rb +108 -0
  149. data/lib/aws/s3/tree/leaf_node.rb +99 -0
  150. data/lib/aws/s3/tree/node.rb +22 -0
  151. data/lib/aws/s3/tree/parent.rb +90 -0
  152. data/lib/aws/s3/uploaded_part.rb +82 -0
  153. data/lib/aws/s3/uploaded_part_collection.rb +86 -0
  154. data/lib/aws/service_interface.rb +60 -0
  155. data/lib/aws/simple_db.rb +202 -0
  156. data/lib/aws/simple_db/attribute.rb +159 -0
  157. data/lib/aws/simple_db/attribute_collection.rb +227 -0
  158. data/lib/aws/simple_db/client.rb +52 -0
  159. data/lib/aws/simple_db/client/options.rb +34 -0
  160. data/lib/aws/simple_db/client/xml.rb +68 -0
  161. data/lib/aws/simple_db/consistent_read_option.rb +42 -0
  162. data/lib/aws/simple_db/delete_attributes.rb +64 -0
  163. data/lib/aws/simple_db/domain.rb +118 -0
  164. data/lib/aws/simple_db/domain_collection.rb +116 -0
  165. data/lib/aws/simple_db/domain_metadata.rb +112 -0
  166. data/lib/aws/simple_db/errors.rb +46 -0
  167. data/lib/aws/simple_db/expect_condition_option.rb +45 -0
  168. data/lib/aws/simple_db/item.rb +84 -0
  169. data/lib/aws/simple_db/item_collection.rb +594 -0
  170. data/lib/aws/simple_db/item_data.rb +70 -0
  171. data/lib/aws/simple_db/put_attributes.rb +62 -0
  172. data/lib/aws/simple_db/request.rb +27 -0
  173. data/lib/aws/simple_email_service.rb +373 -0
  174. data/lib/aws/simple_email_service/client.rb +39 -0
  175. data/lib/aws/simple_email_service/client/options.rb +24 -0
  176. data/lib/aws/simple_email_service/client/xml.rb +38 -0
  177. data/lib/aws/simple_email_service/email_address_collection.rb +66 -0
  178. data/lib/aws/simple_email_service/errors.rb +29 -0
  179. data/lib/aws/simple_email_service/quotas.rb +64 -0
  180. data/lib/aws/simple_email_service/request.rb +27 -0
  181. data/lib/aws/sns.rb +69 -0
  182. data/lib/aws/sns/client.rb +37 -0
  183. data/lib/aws/sns/client/options.rb +24 -0
  184. data/lib/aws/sns/client/xml.rb +38 -0
  185. data/lib/aws/sns/errors.rb +29 -0
  186. data/lib/aws/sns/policy.rb +49 -0
  187. data/lib/aws/sns/request.rb +27 -0
  188. data/lib/aws/sns/subscription.rb +100 -0
  189. data/lib/aws/sns/subscription_collection.rb +84 -0
  190. data/lib/aws/sns/topic.rb +384 -0
  191. data/lib/aws/sns/topic_collection.rb +70 -0
  192. data/lib/aws/sns/topic_subscription_collection.rb +58 -0
  193. data/lib/aws/sqs.rb +70 -0
  194. data/lib/aws/sqs/client.rb +38 -0
  195. data/lib/aws/sqs/client/xml.rb +36 -0
  196. data/lib/aws/sqs/errors.rb +33 -0
  197. data/lib/aws/sqs/policy.rb +50 -0
  198. data/lib/aws/sqs/queue.rb +507 -0
  199. data/lib/aws/sqs/queue_collection.rb +105 -0
  200. data/lib/aws/sqs/received_message.rb +184 -0
  201. data/lib/aws/sqs/received_sns_message.rb +112 -0
  202. data/lib/aws/sqs/request.rb +44 -0
  203. data/lib/aws/xml_grammar.rb +923 -0
  204. data/rails/init.rb +15 -0
  205. metadata +298 -0
@@ -0,0 +1,78 @@
1
+ # Copyright 2011 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
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/model'
15
+ require 'aws/s3/object_version'
16
+
17
+ module AWS
18
+ class S3
19
+
20
+ # For S3 buckets with versioning enabled, objects will store versions
21
+ # each time you write to them.
22
+ #
23
+ # object = bucket.objects['myobj']
24
+ # object.write('1')
25
+ # object.write('2')
26
+ # object.write('3')
27
+ #
28
+ # object.versions.collect(&:read)
29
+ # #=> ['1', '2', '3']
30
+ #
31
+ # If you know the id of a particular version you can get that object.
32
+ #
33
+ # bucket.objets['myobj'].version[version_id].delete
34
+ #
35
+ class ObjectVersionCollection
36
+
37
+ include Model
38
+ include Enumerable
39
+
40
+ # @return [S3Object] The object this collection belongs to.
41
+ attr_reader :object
42
+
43
+ # @param [S3Object] object
44
+ def initialize object, options = {}
45
+ @object = object
46
+ super(options)
47
+ end
48
+
49
+ # Returns an object that represents a single version of the {#object}.
50
+ # @param [String] version_id
51
+ # @return [ObjectVersion]
52
+ def [] version_id
53
+ ObjectVersion.new(object, version_id)
54
+ end
55
+
56
+ # @note Generally you will just want to grab the object key its key.
57
+ # @return [ObjectVersion] Returns the latest version of this object.
58
+ def latest
59
+ self.find{|version| true }
60
+ end
61
+
62
+ # Yields once for each version of the {#object}.
63
+ #
64
+ # @yield [object_version]
65
+ # @yieldparam [ObectVersion] object_version
66
+ # @return [nil]
67
+ def each &block
68
+ object.bucket.versions.with_prefix(object.key).each do |version|
69
+ if version.key == object.key
70
+ yield(version)
71
+ end
72
+ end
73
+ nil
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,94 @@
1
+ # Copyright 2011 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
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ module AWS
15
+ class S3
16
+
17
+ # @private
18
+ module PaginatedCollection
19
+
20
+ def each(options = {}, &block)
21
+ each_page(options) do |page|
22
+ each_member_in_page(page, &block)
23
+ end
24
+ nil
25
+ end
26
+
27
+ protected
28
+ def each_member_in_page(page, &block); end
29
+
30
+ protected
31
+ def each_page(options = {}, &block)
32
+ opts = list_options(options)
33
+ limit = options[:limit]
34
+ batch_size = options[:batch_size] || 1000
35
+ markers = {}
36
+ received = 0
37
+
38
+ loop do
39
+ page_opts = opts.dup
40
+ page_opts.merge!(markers)
41
+ page_opts[limit_param] =
42
+ limit ? [limit - received, batch_size].min : batch_size
43
+
44
+ page = list_request(page_opts)
45
+ markers = next_markers(page)
46
+ received += page_size(page)
47
+
48
+ yield(page)
49
+
50
+ return unless page.truncated?
51
+ end
52
+ end
53
+
54
+ protected
55
+ def list_request(options)
56
+ raise NotImplementedError
57
+ end
58
+
59
+ protected
60
+ def list_options(options)
61
+ opts = {}
62
+ opts[:bucket_name] = bucket.name if respond_to?(:bucket)
63
+ opts
64
+ end
65
+
66
+ protected
67
+ def limit_param
68
+ raise NotImplementedError
69
+ end
70
+
71
+ protected
72
+ def pagination_markers
73
+ [:key_marker]
74
+ end
75
+
76
+ protected
77
+ def next_markers(page)
78
+ pagination_markers.inject({}) do |markers, marker|
79
+ markers[marker] = page.send("next_#{marker}")
80
+ markers
81
+ end
82
+ end
83
+
84
+ protected
85
+ def page_size(resp)
86
+ (resp.respond_to?(:common_prefixes) and
87
+ prefixes = resp.common_prefixes and
88
+ prefixes.size) or 0
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,76 @@
1
+ # Copyright 2011 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
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/policy'
15
+
16
+ module AWS
17
+ class S3
18
+
19
+ # @private
20
+ class Policy < AWS::Policy
21
+
22
+ class Statement < AWS::Policy::Statement
23
+
24
+ ACTION_MAPPING = {
25
+ :list_buckets => "s3:ListAllMyBuckets",
26
+ :create_bucket => "s3:CreateBucket",
27
+ :delete_bucket => "s3:DeleteBucket",
28
+ :list_objects => "s3:ListBucket",
29
+ :list_object_versions => "s3:ListBucketVersions",
30
+ :list_multipart_uploads => "s3:ListBucketMultipartUploads",
31
+ :get_object => "s3:GetObject",
32
+ :get_object_version => "s3:GetObjectVersion",
33
+ :put_object => "s3:PutObject",
34
+ :get_object_acl => "s3:GetObjectAcl",
35
+ :get_object_version_acl => "s3:GetObjectVersionAcl",
36
+ :set_object_acl => "s3:PutObjectAcl",
37
+ :set_object_acl_version => "s3:PutObjectAclVersion",
38
+ :delete_object => "s3:DeleteObject",
39
+ :delete_object_version => "s3:DeleteObjectVersion",
40
+ :list_multipart_upload_parts => "s3:ListMultipartUploadParts",
41
+ :abort_multipart_upload => "s3:AbortMultipartUpload",
42
+ :get_bucket_acl => "s3:GetBucketAcl",
43
+ :set_bucket_acl => "s3:PutBucketAcl",
44
+ :get_bucket_versioning => "s3:GetBucketVersioning",
45
+ :set_bucket_versioning => "s3:PutBucketVersioning",
46
+ :get_bucket_requester_pays => "s3:GetBucketRequesterPays",
47
+ :set_bucket_requester_pays => "s3:PutBucketRequesterPays",
48
+ :get_bucket_location => "s3:GetBucketLocation",
49
+ :get_bucket_policy => "s3:GetBucketPolicy",
50
+ :set_bucket_policy => "s3:PutBucketPolicy",
51
+ :get_bucket_notification => "s3:GetBucketNotification",
52
+ :set_bucket_notification => "s3:PutBucketNotification"
53
+ }
54
+
55
+ protected
56
+ def resource_arn resource
57
+ prefix = 'arn:aws:s3:::'
58
+ case resource
59
+ when Bucket
60
+ "#{prefix}#{resource.name}"
61
+ when S3Object
62
+ "#{prefix}#{resource.bucket.name}/#{resource.key}"
63
+ when ObjectCollection
64
+ "#{prefix}#{resource.bucket.name}/#{resource.prefix}*"
65
+ when /^arn:/
66
+ resource
67
+ else
68
+ "arn:aws:s3:::#{resource}"
69
+ end
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright 2011 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
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/s3/prefixed_collection'
15
+
16
+ module AWS
17
+ class S3
18
+
19
+ # @private
20
+ module PrefixAndDelimiterCollection
21
+
22
+ include PrefixedCollection
23
+
24
+ def each(options = {}, &block)
25
+ each_page(options) do |page|
26
+ each_member_in_page(page, &block)
27
+ end
28
+ nil
29
+ end
30
+
31
+ # @see Bucket#as_tree
32
+ def as_tree options = {}
33
+ Tree.new(self, { :prefix => prefix }.merge(options))
34
+ end
35
+
36
+ # @private
37
+ protected
38
+ def each_member_in_page(page, &block)
39
+ super
40
+ page.common_prefixes.each do |p|
41
+ yield(with_prefix(p))
42
+ end
43
+ end
44
+
45
+ # @private
46
+ protected
47
+ def list_options(options)
48
+ opts = super
49
+ opts[:delimiter] = options[:delimiter] if options.key?(:delimiter)
50
+ opts
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,84 @@
1
+ # Copyright 2011 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
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/s3/paginated_collection'
15
+
16
+ module AWS
17
+ class S3
18
+
19
+ module PrefixedCollection
20
+
21
+ include PaginatedCollection
22
+
23
+ # @private
24
+ def initialize *args
25
+ options = args.last.is_a?(Hash) ? args.pop : {}
26
+ @prefix = options[:prefix]
27
+ args.push(options)
28
+ super(*args)
29
+ end
30
+
31
+ # @return [String,nil] The prefix of this collection.
32
+ attr_reader :prefix
33
+
34
+ # Returns a new collection with a different prefix
35
+ #
36
+ # @example
37
+ # objects = collection.with_prefix('photos')
38
+ # objects.prefix #=> 'photos'
39
+ #
40
+ # @example Chaining with_prefix replaces previous prefix
41
+ # objects = collection.with_prefix('photos').with_prefix('videos')
42
+ # objects.prefix #=> 'videos'
43
+ #
44
+ # @example Chaining with_prefix with :append
45
+ # objects = collection.with_prefix('a/').with_prefix('b/', :append)
46
+ # objects.prefix #=> 'a/b/'
47
+ #
48
+ # @example Chaining with_prefix with :prepend
49
+ # objects = collection.with_prefix('a/').with_prefix('b/', :prepend)
50
+ # objects.prefix #=> 'b/a/'
51
+ #
52
+ # @param [String] prefix The prefix condition that limits what objects
53
+ # are returned by this collection.
54
+ # @param [Symbol] mode (:replace) If you chain calls to #with_prefix
55
+ # the +mode+ affects if the prefix prepends, appends, or replaces.
56
+ # Valid modes are:
57
+ # * +:replace+
58
+ # * +:append+
59
+ # * +:prepend+
60
+ # @return [Collection] Returns a new collection with a modified prefix.
61
+ def with_prefix prefix, mode = :replace
62
+ new_prefix = case mode
63
+ when :replace then prefix
64
+ when :append then "#{@prefix}#{prefix}"
65
+ when :prepend then "#{prefix}#{@prefix}"
66
+ else
67
+ raise ArgumentError, "invalid prefix mode `#{mode}`, it must be " +
68
+ ":replace, :append or :prepend"
69
+ end
70
+ self.class.new(bucket,
71
+ :prefix => new_prefix)
72
+ end
73
+
74
+ # @private
75
+ protected
76
+ def list_options(options)
77
+ opts = super
78
+ opts[:prefix] = prefix if prefix
79
+ opts
80
+ end
81
+
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,504 @@
1
+ # Copyright 2011 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
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ require 'aws/model'
15
+ require 'aws/s3/request'
16
+ require 'uri'
17
+ require 'base64'
18
+ require 'time'
19
+
20
+ module AWS
21
+ class S3
22
+
23
+ # Helper to generate form fields for presigned POST requests to
24
+ # a bucket. You can use this to create a form that can be used
25
+ # from a web browser to upload objects to S3 while specifying
26
+ # conditions on what can be uploaded and how it is processed and
27
+ # stored.
28
+ #
29
+ # @example Form fields for uploading by file name
30
+ # form = bucket.presigned_post(:key => "photos/${filename}")
31
+ # form.url.to_s # => "https://mybucket.s3.amazonaws.com/"
32
+ # form.fields # => { "AWSAccessKeyId" => "...", ... }
33
+ #
34
+ # @example Generating a minimal HTML form
35
+ # form = bucket.objects.myobj.presigned_post
36
+ # hidden_inputs = form.fields.map do |(name, value)|
37
+ # %(<input type="hidden" name="#{name}" value="#{value}" />)
38
+ # end
39
+ # <<-END
40
+ # <form action="#{form.url}"
41
+ # method="post"
42
+ # enctype="multipart/form-data">
43
+ # #{hidden_inputs}
44
+ # <input type="file" name="file" />
45
+ # </form>
46
+ # END
47
+ #
48
+ # @example Restricting the size of the uploaded object
49
+ # bucket.presigned_post(:content_length => 1..(10*1024))
50
+ #
51
+ # @example Restricting the key prefix
52
+ # bucket.presigned_post.where(:key).starts_with("photos/")
53
+ class PresignedPost
54
+
55
+ include Model
56
+
57
+ # @return [Bucket] The bucket to which data can be uploaded
58
+ # using the form fields
59
+ attr_reader :bucket
60
+
61
+ # @return [String] The key of the object that will be
62
+ # uploaded. If this is nil, then the object can be uploaded
63
+ # with any key that satisfies the conditions specified for
64
+ # the upload (see {#where}).
65
+ attr_reader :key
66
+
67
+ # @return [Hash] A hash of the metadata fields included in the
68
+ # signed fields. Additional metadata fields may be provided
69
+ # with the upload as long as they satisfy the conditions
70
+ # specified for the upload (see {#where}).
71
+ attr_reader :metadata
72
+
73
+ # @return [Range] The range of acceptable object sizes for the
74
+ # upload. By default any size object may be uploaded.
75
+ attr_reader :content_length
76
+
77
+ # @private
78
+ SPECIAL_FIELDS = [:cache_control,
79
+ :content_type,
80
+ :content_disposition,
81
+ :content_encoding,
82
+ :expires_header,
83
+ :acl,
84
+ :success_action_redirect,
85
+ :success_action_status]
86
+
87
+ # @private
88
+ attr_reader :conditions
89
+
90
+ # @return [Array<String>] Additional fields which may be sent
91
+ # with the upload. These will be included in the policy so
92
+ # that they can be sent with any value. S3 will ignore
93
+ # them.
94
+ attr_reader :ignored_fields
95
+
96
+ # @return The expiration time for the signature. By default
97
+ # the signature will expire an hour after it is generated.
98
+ attr_reader :expires
99
+
100
+ # Creates a new presigned post object.
101
+ #
102
+ # @param [Bucket] bucket The bucket to which data can be uploaded
103
+ # using the form fields.
104
+ #
105
+ # @param [Hash] opts Additional options for the upload. Aside
106
+ # from +:secure+, +:expires+, +:content_length+ and +:ignore+
107
+ # the values provided here will be stored in the hash returned
108
+ # from the {#fields} method, and the policy in that hash will
109
+ # restrict their values to the values provided. If you
110
+ # instead want to only restrict the values and not provide
111
+ # them -- for example, if your application generates separate
112
+ # form fields for those values -- you should use the {#where}
113
+ # method on the returned object instead of providing the
114
+ # values here.
115
+ #
116
+ # @option opts [String] :key The key of the object that will
117
+ # be uploaded. If this is nil, then the object can be
118
+ # uploaded with any key that satisfies the conditions
119
+ # specified for the upload (see {#where}).
120
+ #
121
+ # @option opts [Boolean] :secure By setting this to false, you
122
+ # can cause {#url} to return an HTTP URL. By default it
123
+ # returns an HTTPS URL.
124
+ #
125
+ # @option opts [Time, DateTime, Integer, String] :expires The
126
+ # time at which the signature will expire. By default the
127
+ # signature will expire one hour after it is generated
128
+ # (e.g. when {#fields} is called).
129
+ #
130
+ # When the value is a Time or DateTime, the signature
131
+ # expires at the specified time. When it is an integer, the
132
+ # signature expires the specified number of seconds after it
133
+ # is generated. When it is a string, the string is parsed
134
+ # as a time (using Time.parse) and the signature expires at
135
+ # that time.
136
+ #
137
+ # @option opts [String] :cache_control Sets the Cache-Control
138
+ # header stored with the object.
139
+ #
140
+ # @option opts [String] :content_type Sets the Content-Type
141
+ # header stored with the object.
142
+ #
143
+ # @option opts [String] :content_disposition Sets the
144
+ # Content-Disposition header stored with the object.
145
+ #
146
+ # @option opts [String] :expires_header Sets the Expires
147
+ # header stored with the object.
148
+ #
149
+ # @option options [Symbol] :acl A canned access control
150
+ # policy. Valid values are:
151
+ # * +:private+
152
+ # * +:public_read+
153
+ # * +:public_read_write+
154
+ # * +:authenticated_read+
155
+ # * +:bucket_owner_read+
156
+ # * +:bucket_owner_full_control+
157
+ #
158
+ # @option opts [String] :success_action_redirect The URL to
159
+ # which the client is redirected upon successful upload.
160
+ #
161
+ # @option opts [Integer] :success_action_status The status
162
+ # code returned to the client upon successful upload if
163
+ # +:success_action_redirect+ is not specified. Accepts the
164
+ # values 200, 201, or 204 (default).
165
+ #
166
+ # If the value is set to 200 or 204, Amazon S3 returns an
167
+ # empty document with a 200 or 204 status code.
168
+ #
169
+ # If the value is set to 201, Amazon S3 returns an XML
170
+ # document with a 201 status code. For information on the
171
+ # content of the XML document, see
172
+ # {POST Object}[http://docs.amazonwebservices.com/AmazonS3/2006-03-01/API/index.html?RESTObjectPOST.html].
173
+ #
174
+ # @option opts [Hash] :metadata A hash of the metadata fields
175
+ # included in the signed fields. Additional metadata fields
176
+ # may be provided with the upload as long as they satisfy
177
+ # the conditions specified for the upload (see {#where}).
178
+ #
179
+ # @option opts [Integer, Range] :content_length The range of
180
+ # acceptable object sizes for the upload. By default any
181
+ # size object may be uploaded.
182
+ #
183
+ # @option opts [Array<String>] :ignore Additional fields which
184
+ # may be sent with the upload. These will be included in
185
+ # the policy so that they can be sent with any value. S3
186
+ # will ignore them.
187
+ def initialize(bucket, opts = {})
188
+ @bucket = bucket
189
+ @key = opts[:key]
190
+ @secure = (opts[:secure] != false)
191
+ @fields = {}
192
+ SPECIAL_FIELDS.each do |name|
193
+ @fields[name] = opts[name] if opts.key?(name)
194
+ end
195
+ @metadata = opts[:metadata] || {}
196
+ @content_length = range_value(opts[:content_length])
197
+ @conditions = opts[:conditions] || {}
198
+ @ignored_fields = [opts[:ignore]].flatten.compact
199
+ @expires = opts[:expires]
200
+
201
+ super
202
+ end
203
+
204
+ # @return [Boolean] True if {#url} generates an HTTPS url.
205
+ def secure?
206
+ @secure
207
+ end
208
+
209
+ # @return [URI::HTTP, URI::HTTPS] The URL to which the form
210
+ # fields should be POSTed. If you are using the fields in
211
+ # an HTML form, this is the URL to put in the +action+
212
+ # attribute of the form tag.
213
+ def url
214
+ req = Request.new
215
+ req.bucket = bucket.name
216
+ req.host = config.s3_endpoint
217
+ build_uri(req)
218
+ end
219
+
220
+ # Lets you specify conditions on a field. See
221
+ # {PresignedPost#where} for usage examples.
222
+ class ConditionBuilder
223
+
224
+ # @private
225
+ def initialize(post, field)
226
+ @post = post
227
+ @field = field
228
+ end
229
+
230
+ # Specifies that the value of the field must equal the
231
+ # provided value.
232
+ def is(value)
233
+ if @field == :content_length
234
+ self.in(value)
235
+ else
236
+ @post.with_equality_condition(@field, value)
237
+ end
238
+ end
239
+
240
+ # Specifies that the value of the field must begin with the
241
+ # provided value. If you are specifying a condition on the
242
+ # "key" field, note that this check takes place after the
243
+ # +${filename}+ variable is expanded. This is only valid
244
+ # for the following fields:
245
+ #
246
+ # * +:key+
247
+ # * +:cache_control+
248
+ # * +:content_type+
249
+ # * +:content_disposition+
250
+ # * +:content_encoding+
251
+ # * +:expires_header+
252
+ # * +:acl+
253
+ # * +:success_action_redirect+
254
+ # * metadata fields (see {#where_metadata})
255
+ def starts_with(prefix)
256
+ @post.with_prefix_condition(@field, prefix)
257
+ end
258
+
259
+ # Specifies that the value of the field must be in the given
260
+ # range. This may only be used to constrain the
261
+ # +:content_length+ field,
262
+ # e.g. <tt>presigned_post.with(:conent_length).in(1..4)</tt>.
263
+ def in(range)
264
+ @post.refine(:content_length => range)
265
+ end
266
+
267
+ end
268
+
269
+ # Adds a condition to the policy for the POST. Use
270
+ # {#where_metadata} to add metadata conditions.
271
+ #
272
+ # @example Restricting the ACL to "bucket-owner" ACLs
273
+ # presigned_post.where(:acl).starts_with("bucket-owner")
274
+ #
275
+ # @param [Symbol] field The field for which a condition should
276
+ # be added. Valid values:
277
+ #
278
+ # * +:key+
279
+ # * +:content_length+
280
+ # * +:cache_control+
281
+ # * +:content_type+
282
+ # * +:content_disposition+
283
+ # * +:content_encoding+
284
+ # * +:expires_header+
285
+ # * +:acl+
286
+ # * +:success_action_redirect+
287
+ # * +:success_action_status+
288
+ #
289
+ # @return [ConditionBuilder] An object that allows you to
290
+ # specify a condition on the field.
291
+ def where(field)
292
+ raise ArgumentError.new("unrecognized field name #{field}") unless
293
+ [:key, :content_length, *SPECIAL_FIELDS].include?(field) or
294
+ field =~ /^x-amz-meta-/
295
+ ConditionBuilder.new(self, field)
296
+ end
297
+
298
+ # Adds a condition to the policy for the POST to constrain the
299
+ # values of metadata fields uploaded with the object. If a
300
+ # metadata field does not have a condition associated with it
301
+ # and is not specified in the constructor (see {#metadata})
302
+ # then S3 will reject it.
303
+ #
304
+ # @param [Symbol, String] field The name of the metadata
305
+ # attribute. For example, +:color+ corresponds to the
306
+ # "x-amz-meta-color" field in the POST body.
307
+ #
308
+ # @return [ConditionBuilder] An object that allows you to
309
+ # specify a condition on the metadata attribute.
310
+ def where_metadata(field)
311
+ where("x-amz-meta-#{field}")
312
+ end
313
+
314
+ # @return [String] The Base64-encoded JSON policy document.
315
+ def policy
316
+ json = {
317
+ "expiration" => format_expiration,
318
+ "conditions" => generate_conditions
319
+ }.to_json
320
+ Base64.encode64(json)
321
+ end
322
+
323
+ # @return [Hash] A collection of form fields (including a
324
+ # signature and a policy) that can be used to POST data to
325
+ # S3. Additional form fields may be added after the fact as
326
+ # long as they are described by a policy condition (see
327
+ # {#where}).
328
+ def fields
329
+ signature =
330
+ config.signer.sign(policy, "sha1")
331
+
332
+ {
333
+ "AWSAccessKeyId" => config.signer.access_key_id,
334
+ "key" => key,
335
+ "policy" => policy,
336
+ "signature" => signature
337
+ }.merge(optional_fields)
338
+ end
339
+
340
+ # @private
341
+ def with_equality_condition(option_name, value)
342
+ field_name = field_name(option_name)
343
+ with_condition(option_name, Hash[[[field_name, value]]])
344
+ end
345
+
346
+ # @private
347
+ def with_prefix_condition(option_name, prefix)
348
+ field_name = field_name(option_name)
349
+ with_condition(option_name,
350
+ ["starts-with", "$#{field_name}", prefix])
351
+ end
352
+
353
+ # @private
354
+ def refine(opts)
355
+ self.class.new(bucket, {
356
+ :conditions => conditions,
357
+ :key => key,
358
+ :metadata => metadata,
359
+ :secure => secure?,
360
+ :content_length => content_length
361
+ }.merge(@fields).
362
+ merge(opts))
363
+ end
364
+
365
+ # @private
366
+ private
367
+ def with_condition(field, condition)
368
+ conditions = self.conditions.dup
369
+ (conditions[field] ||= []) << condition
370
+ refine(:conditions => conditions)
371
+ end
372
+
373
+ # @private
374
+ private
375
+ def format_expiration
376
+ time = expires || Time.now.utc + 60*60
377
+ time =
378
+ case time
379
+ when Time
380
+ time
381
+ when DateTime
382
+ Time.parse(time.to_s)
383
+ when Integer
384
+ (Time.now + time)
385
+ when String
386
+ Time.parse(time)
387
+ end
388
+ time.utc.iso8601
389
+ end
390
+
391
+ # @private
392
+ private
393
+ def range_value(range)
394
+ case range
395
+ when Integer
396
+ range..range
397
+ when Range
398
+ range
399
+ end
400
+ end
401
+
402
+ # @private
403
+ private
404
+ def split_range(range)
405
+ range = range_value(range)
406
+ [range.begin,
407
+ (range.exclude_end? ?
408
+ range.end-1 :
409
+ range.end)]
410
+ end
411
+
412
+ # @private
413
+ private
414
+ def optional_fields
415
+ fields = (SPECIAL_FIELDS &
416
+ @fields.keys).inject({}) do |fields, option_name|
417
+ fields[field_name(option_name)] =
418
+ @fields[option_name].to_s
419
+ fields
420
+ end
421
+
422
+ fields["acl"] = fields["acl"].tr("_", "-") if
423
+ fields["acl"]
424
+
425
+ @metadata.each do |key, value|
426
+ fields["x-amz-meta-#{key}"] = value.to_s
427
+ end
428
+
429
+ fields
430
+ end
431
+
432
+ # @private
433
+ private
434
+ def field_name(option_name)
435
+ case option_name
436
+ when :expires_header
437
+ "Expires"
438
+ when :acl, :success_action_redirect, :success_action_status
439
+ option_name.to_s
440
+ else
441
+ # e.g. Cache-Control from cache_control
442
+ field_name = option_name.to_s.tr("_", "-").
443
+ gsub(/-(.)/) { |m| m.upcase }
444
+ field_name[0,1] = field_name[0,1].upcase
445
+ field_name
446
+ end
447
+ end
448
+
449
+ # @private
450
+ private
451
+ def generate_conditions
452
+ conditions.inject([]) do |ary, (field, field_conds)|
453
+ ary += field_conds
454
+ end +
455
+ [{ "bucket" => bucket.name }] +
456
+ key_conditions +
457
+ optional_fields.map { |(n, v)| Hash[[[n, v]]] } +
458
+ range_conditions +
459
+ ignored_conditions
460
+ end
461
+
462
+ # @private
463
+ private
464
+ def ignored_conditions
465
+ ignored_fields.map do |field|
466
+ ["starts-with", "$#{field}", ""]
467
+ end
468
+ end
469
+
470
+ # @private
471
+ private
472
+ def range_conditions
473
+ if content_length
474
+ [["content-length-range", *split_range(content_length)]]
475
+ else
476
+ []
477
+ end
478
+ end
479
+
480
+ # @private
481
+ private
482
+ def key_conditions
483
+ [if key && key.include?("${filename}")
484
+ ["starts-with", "$key", key[/^(.*)\$\{filename\}/, 1]]
485
+ elsif key
486
+ { "key" => key }
487
+ else
488
+ ["starts-with", "$key", ""]
489
+ end]
490
+ end
491
+
492
+ # @private
493
+ private
494
+ def build_uri(request)
495
+ uri_class = secure? ? URI::HTTPS : URI::HTTP
496
+ uri_class.build(:host => request.host,
497
+ :path => request.path,
498
+ :query => request.querystring)
499
+ end
500
+
501
+ end
502
+
503
+ end
504
+ end