aws-sdk 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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