aws 2.3.34 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,238 @@
1
+ module Aws
2
+
3
+ # There are 2 ways to set permissions for a bucket or key (called a +thing+ below):
4
+ #
5
+ # 1 . Use +perms+ param to set 'Canned Access Policies' when calling the <tt>bucket.create</tt>,
6
+ # <tt>bucket.put</tt> and <tt>key.put</tt> methods.
7
+ # The +perms+ param can take these values: 'private', 'public-read', 'public-read-write' and
8
+ # 'authenticated-read'.
9
+ # (see http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html).
10
+ #
11
+ # bucket = s3.bucket('bucket_for_kd_test_13', true, 'public-read')
12
+ # key.put('Woohoo!','public-read-write' )
13
+ #
14
+ # 2 . Use Grantee instances (the permission is a +String+ or an +Array+ of: 'READ', 'WRITE',
15
+ # 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'):
16
+ #
17
+ # bucket = s3.bucket('my_awesome_bucket', true)
18
+ # grantee1 = Aws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL, :apply)
19
+ # grantee2 = Aws::S3::Grantee.new(bucket, 'xy3v3...5fhp', [READ, WRITE], :apply)
20
+ #
21
+ # There is only one way to get and to remove permission (via Grantee instances):
22
+ #
23
+ # grantees = bucket.grantees # a list of Grantees that have any access for this bucket
24
+ # grantee1 = Aws::S3::Grantee.new(bucket, 'a123b...223c')
25
+ # grantee1.perms #=> returns a list of perms for this grantee to that bucket
26
+ # ...
27
+ # grantee1.drop # remove all perms for this grantee
28
+ # grantee2.revoke('WRITE') # revoke write access only
29
+ #
30
+ class S3::Grantee
31
+ # A bucket or a key the grantee has an access to.
32
+ attr_reader :thing
33
+ # Grantee Amazon id.
34
+ attr_reader :id
35
+ # Grantee display name.
36
+ attr_reader :name
37
+ # Array of permissions.
38
+ attr_accessor :perms
39
+
40
+ # Retrieve Owner information and a list of Grantee instances that have
41
+ # a access to this thing (bucket or key).
42
+ #
43
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
44
+ # ...
45
+ # Aws::S3::Grantee.owner_and_grantees(bucket) #=> [owner, grantees]
46
+ #
47
+ def self.owner_and_grantees(thing)
48
+ if thing.is_a?(Bucket)
49
+ bucket, key = thing, ''
50
+ else
51
+ bucket, key = thing.bucket, thing
52
+ end
53
+ hash = bucket.s3.interface.get_acl_parse(bucket.to_s, key.to_s)
54
+ owner = Owner.new(hash[:owner][:id], hash[:owner][:display_name])
55
+
56
+ grantees = []
57
+ hash[:grantees].each do |id, params|
58
+ grantees << new(thing, id, params[:permissions], nil, params[:display_name])
59
+ end
60
+ [owner, grantees]
61
+ end
62
+
63
+ # Retrieves a list of Grantees instances that have an access to this thing(bucket or key).
64
+ #
65
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
66
+ # ...
67
+ # Aws::S3::Grantee.grantees(bucket) #=> grantees
68
+ #
69
+ def self.grantees(thing)
70
+ owner_and_grantees(thing)[1]
71
+ end
72
+
73
+ def self.put_acl(thing, owner, grantees) #:nodoc:
74
+ if thing.is_a?(Bucket)
75
+ bucket, key = thing, ''
76
+ else
77
+ bucket, key = thing.bucket, thing
78
+ end
79
+ body = "<AccessControlPolicy>" +
80
+ "<Owner>" +
81
+ "<ID>#{owner.id}</ID>" +
82
+ "<DisplayName>#{owner.name}</DisplayName>" +
83
+ "</Owner>" +
84
+ "<AccessControlList>" +
85
+ grantees.map { |grantee| grantee.to_xml }.join +
86
+ "</AccessControlList>" +
87
+ "</AccessControlPolicy>"
88
+ bucket.s3.interface.put_acl(bucket.to_s, key.to_s, body)
89
+ end
90
+
91
+ # Create a new Grantee instance.
92
+ # Grantee +id+ must exist on S3. If +action+ == :refresh, then retrieve
93
+ # permissions from S3 and update @perms. If +action+ == :apply, then apply
94
+ # perms to +thing+ at S3. If +action+ == :apply_and_refresh then it performs.
95
+ # both the actions. This is used for the new grantees that had no perms to
96
+ # this thing before. The default action is :refresh.
97
+ #
98
+ # bucket = s3.bucket('my_awesome_bucket', true, 'public-read')
99
+ # grantee1 = Aws::S3::Grantee.new(bucket, 'a123b...223c', FULL_CONTROL)
100
+ # ...
101
+ # grantee2 = Aws::S3::Grantee.new(bucket, 'abcde...asdf', [FULL_CONTROL, READ], :apply)
102
+ # grantee3 = Aws::S3::Grantee.new(bucket, 'aaaaa...aaaa', 'READ', :apply_and_refresh)
103
+ #
104
+ def initialize(thing, id, perms=[], action=:refresh, name=nil)
105
+ @thing = thing
106
+ @id = id
107
+ @name = name
108
+ @perms = perms.to_a
109
+ case action
110
+ when :apply then
111
+ apply
112
+ when :refresh then
113
+ refresh
114
+ when :apply_and_refresh then
115
+ apply; refresh
116
+ end
117
+ end
118
+
119
+ # Return +true+ if the grantee has any permissions to the thing.
120
+ def exists?
121
+ self.class.grantees(@thing).each do |grantee|
122
+ return true if @id == grantee.id
123
+ end
124
+ false
125
+ end
126
+
127
+ # Return Grantee type (+String+): "Group" or "CanonicalUser".
128
+ def type
129
+ @id[/^http:/] ? "Group" : "CanonicalUser"
130
+ end
131
+
132
+ # Return a name or an id.
133
+ def to_s
134
+ @name || @id
135
+ end
136
+
137
+ # Add permissions for grantee.
138
+ # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'.
139
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
140
+ # Returns +true+.
141
+ #
142
+ # grantee.grant('FULL_CONTROL') #=> true
143
+ # grantee.grant('FULL_CONTROL','WRITE','READ') #=> true
144
+ # grantee.grant(['WRITE_ACP','READ','READ_ACP']) #=> true
145
+ #
146
+ def grant(*permissions)
147
+ permissions.flatten!
148
+ old_perms = @perms.dup
149
+ @perms += permissions
150
+ @perms.uniq!
151
+ return true if @perms == old_perms
152
+ apply
153
+ end
154
+
155
+ # Revoke permissions for grantee.
156
+ # Permissions: 'READ', 'WRITE', 'READ_ACP', 'WRITE_ACP', 'FULL_CONTROL'
157
+ # See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingPermissions.html .
158
+ # Default value is 'FULL_CONTROL'.
159
+ # Returns +true+.
160
+ #
161
+ # grantee.revoke('READ') #=> true
162
+ # grantee.revoke('FULL_CONTROL','WRITE') #=> true
163
+ # grantee.revoke(['READ_ACP','WRITE_ACP']) #=> true
164
+ #
165
+ def revoke(*permissions)
166
+ permissions.flatten!
167
+ old_perms = @perms.dup
168
+ @perms -= permissions
169
+ @perms.uniq!
170
+ return true if @perms == old_perms
171
+ apply
172
+ end
173
+
174
+ # Revoke all permissions for this grantee.
175
+ # Returns +true+.
176
+ #
177
+ # grantee.drop #=> true
178
+ #
179
+ def drop
180
+ @perms = []
181
+ apply
182
+ end
183
+
184
+ # Refresh grantee perms for its +thing+.
185
+ # Returns +true+ if the grantee has perms for this +thing+ or
186
+ # +false+ otherwise, and updates @perms value as a side-effect.
187
+ #
188
+ # grantee.grant('FULL_CONTROL') #=> true
189
+ # grantee.refresh #=> true
190
+ # grantee.drop #=> true
191
+ # grantee.refresh #=> false
192
+ #
193
+ def refresh
194
+ @perms = []
195
+ self.class.grantees(@thing).each do |grantee|
196
+ if @id == grantee.id
197
+ @name = grantee.name
198
+ @perms = grantee.perms
199
+ return true
200
+ end
201
+ end
202
+ false
203
+ end
204
+
205
+ # Apply current grantee @perms to +thing+. This method is called internally by the +grant+
206
+ # and +revoke+ methods. In normal use this method should not
207
+ # be called directly.
208
+ #
209
+ # grantee.perms = ['FULL_CONTROL']
210
+ # grantee.apply #=> true
211
+ #
212
+ def apply
213
+ @perms.uniq!
214
+ owner, grantees = self.class.owner_and_grantees(@thing)
215
+ # walk through all the grantees and replace the data for the current one and ...
216
+ grantees.map! { |grantee| grantee.id == @id ? self : grantee }
217
+ # ... if this grantee is not known - add this bad boy to a list
218
+ grantees << self unless grantees.include?(self)
219
+ # set permissions
220
+ self.class.put_acl(@thing, owner, grantees)
221
+ end
222
+
223
+ def to_xml # :nodoc:
224
+ id_str = @id[/^http/] ? "<URI>#{@id}</URI>" : "<ID>#{@id}</ID>"
225
+ grants = ''
226
+ @perms.each do |perm|
227
+ grants << "<Grant>" +
228
+ "<Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
229
+ "xsi:type=\"#{type}\">#{id_str}</Grantee>" +
230
+ "<Permission>#{perm}</Permission>" +
231
+ "</Grant>"
232
+ end
233
+ grants
234
+ end
235
+
236
+ end
237
+
238
+ end
@@ -0,0 +1,281 @@
1
+ module Aws
2
+
3
+
4
+ class S3::Key
5
+
6
+ attr_reader :bucket, :name, :last_modified, :e_tag, :size, :storage_class, :owner
7
+ attr_accessor :headers, :meta_headers
8
+ attr_writer :data
9
+
10
+ # Separate Amazon meta headers from other headers
11
+ def self.split_meta(headers) #:nodoc:
12
+ hash = headers.dup
13
+ meta = {}
14
+ hash.each do |key, value|
15
+ if key[/^#{S3Interface::AMAZON_METADATA_PREFIX}/]
16
+ meta[key.gsub(S3Interface::AMAZON_METADATA_PREFIX, '')] = value
17
+ hash.delete(key)
18
+ end
19
+ end
20
+ [hash, meta]
21
+ end
22
+
23
+ def self.add_meta_prefix(meta_headers, prefix=S3Interface::AMAZON_METADATA_PREFIX)
24
+ meta = {}
25
+ meta_headers.each do |meta_header, value|
26
+ if meta_header[/#{prefix}/]
27
+ meta[meta_header] = value
28
+ else
29
+ meta["#{S3Interface::AMAZON_METADATA_PREFIX}#{meta_header}"] = value
30
+ end
31
+ end
32
+ meta
33
+ end
34
+
35
+
36
+ # Create a new Key instance, but do not create the actual key.
37
+ # The +name+ is a +String+.
38
+ # Returns a new Key instance.
39
+ #
40
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
41
+ # key.exists? #=> true | false
42
+ # key.put('Woohoo!') #=> true
43
+ # key.exists? #=> true
44
+ #
45
+ def self.create(bucket, name, data=nil, meta_headers={})
46
+ new(bucket, name, data, {}, meta_headers)
47
+ end
48
+
49
+ # Create a new Key instance, but do not create the actual key.
50
+ # In normal use this method should not be called directly.
51
+ # Use Aws::S3::Key.create or bucket.key() instead.
52
+ #
53
+ def initialize(bucket, name, data=nil, headers={}, meta_headers={},
54
+ last_modified=nil, e_tag=nil, size=nil, storage_class=nil, owner=nil)
55
+ raise 'Bucket must be a Bucket instance.' unless bucket.is_a?(S3::Bucket)
56
+ @bucket = bucket
57
+ @name = name
58
+ @data = data
59
+ @e_tag = e_tag
60
+ @size = size.to_i
61
+ @storage_class = storage_class
62
+ @owner = owner
63
+ @last_modified = last_modified
64
+ if @last_modified && !@last_modified.is_a?(Time)
65
+ @last_modified = Time.parse(@last_modified)
66
+ end
67
+ @headers, @meta_headers = self.class.split_meta(headers)
68
+ @meta_headers.merge!(meta_headers)
69
+ end
70
+
71
+ # Return key name as a String.
72
+ #
73
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
74
+ # puts key #=> 'logs/today/1.log'
75
+ #
76
+ def to_s
77
+ @name.to_s
78
+ end
79
+
80
+ # Return the full S3 path to this key (bucket/key).
81
+ #
82
+ # key.full_name #=> 'my_awesome_bucket/cool_key'
83
+ #
84
+ def full_name(separator='/')
85
+ "#{@bucket.to_s}#{separator}#{@name}"
86
+ end
87
+
88
+ # Return a public link to a key.
89
+ #
90
+ # key.public_link #=> 'https://s3.amazonaws.com:443/my_awesome_bucket/cool_key'
91
+ #
92
+ def public_link
93
+ params = @bucket.s3.interface.params
94
+ "#{params[:protocol]}://#{params[:server]}:#{params[:port]}/#{full_name('/')}"
95
+ end
96
+
97
+ # Return Key data. Retrieve this data from Amazon if it is the first time call.
98
+ # TODO TRB 6/19/07 What does the above mean? Clarify.
99
+ #
100
+ def data
101
+ get if !@data and exists?
102
+ @data
103
+ end
104
+
105
+ # Retrieve object data and attributes from Amazon.
106
+ # Returns a +String+.
107
+ #
108
+ def get(headers={}, &block)
109
+ response = @bucket.s3.interface.get(@bucket.name, @name, headers, &block)
110
+ @data = response[:object]
111
+ @headers, @meta_headers = self.class.split_meta(response[:headers])
112
+ # refresh(false) Holy moly, this was doing two extra hits to s3 for making 3 hits for every get!!
113
+ @data
114
+ end
115
+
116
+ # Store object data on S3.
117
+ # Parameter +data+ is a +String+ or S3Object instance.
118
+ # Returns +true+.
119
+ #
120
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log')
121
+ # key.data = 'Qwerty'
122
+ # key.put #=> true
123
+ # ...
124
+ # key.put('Olala!') #=> true
125
+ #
126
+ def put(data=nil, perms=nil, headers={})
127
+ headers['x-amz-acl'] = perms if perms
128
+ @data = data || @data
129
+ meta = self.class.add_meta_prefix(@meta_headers)
130
+ @bucket.s3.interface.put(@bucket.name, @name, @data, meta.merge(headers))
131
+ end
132
+
133
+ # Rename an object. Returns new object name.
134
+ #
135
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
136
+ # key.rename('logs/today/2.log') #=> 'logs/today/2.log'
137
+ # puts key.name #=> 'logs/today/2.log'
138
+ # key.exists? #=> true
139
+ #
140
+ def rename(new_name)
141
+ @bucket.s3.interface.rename(@bucket.name, @name, new_name)
142
+ @name = new_name
143
+ end
144
+
145
+ # Create an object copy. Returns a destination Aws::S3::Key instance.
146
+ #
147
+ # # Key instance as destination
148
+ # key1 = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
149
+ # key2 = Aws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
150
+ # key1.put('Olala!') #=> true
151
+ # key1.copy(key2) #=> #<Aws::S3::Key:0xb7b5e240 ... >
152
+ # key1.exists? #=> true
153
+ # key2.exists? #=> true
154
+ # puts key2.data #=> 'Olala!'
155
+ #
156
+ # # String as destination
157
+ # key = Aws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
158
+ # key.put('Olala!') #=> true
159
+ # new_key = key.copy('logs/today/888.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
160
+ # key.exists? #=> true
161
+ # new_key.exists? #=> true
162
+ #
163
+ def copy(new_key_or_name)
164
+ new_key_or_name = S3::Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(S3::Key)
165
+ @bucket.s3.interface.copy(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
166
+ new_key_or_name
167
+ end
168
+
169
+ # Move an object to other location. Returns a destination Aws::S3::Key instance.
170
+ #
171
+ # # Key instance as destination
172
+ # key1 = Aws::S3::Key.create(bucket, 'logs/today/1.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
173
+ # key2 = Aws::S3::Key.create(bucket, 'logs/today/2.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
174
+ # key1.put('Olala!') #=> true
175
+ # key1.move(key2) #=> #<Aws::S3::Key:0xb7b5e240 ... >
176
+ # key1.exists? #=> false
177
+ # key2.exists? #=> true
178
+ # puts key2.data #=> 'Olala!'
179
+ #
180
+ # # String as destination
181
+ # key = Aws::S3::Key.create(bucket, 'logs/today/777.log') #=> #<Aws::S3::Key:0xb7b1e240 ... >
182
+ # key.put('Olala!') #=> true
183
+ # new_key = key.move('logs/today/888.log') #=> #<Aws::S3::Key:0xb7b5e240 ... >
184
+ # key.exists? #=> false
185
+ # new_key.exists? #=> true
186
+ #
187
+ def move(new_key_or_name)
188
+ new_key_or_name = S3::Key.create(@bucket, new_key_or_name.to_s) unless new_key_or_name.is_a?(S3::Key)
189
+ @bucket.s3.interface.move(@bucket.name, @name, new_key_or_name.bucket.name, new_key_or_name.name)
190
+ new_key_or_name
191
+ end
192
+
193
+ # Retrieve key info from bucket and update attributes.
194
+ # Refresh meta-headers (by calling +head+ method) if +head+ is set.
195
+ # Returns +true+ if the key exists in bucket and +false+ otherwise.
196
+ #
197
+ # key = Aws::S3::Key.create(bucket, 'logs/today/1.log')
198
+ # key.e_tag #=> nil
199
+ # key.meta_headers #=> {}
200
+ # key.refresh #=> true
201
+ # key.e_tag #=> '12345678901234567890bf11094484b6'
202
+ # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
203
+ #
204
+ def refresh(head=true)
205
+ new_key = @bucket.key(self)
206
+ @last_modified = new_key.last_modified
207
+ @e_tag = new_key.e_tag
208
+ @size = new_key.size
209
+ @storage_class = new_key.storage_class
210
+ @owner = new_key.owner
211
+ if @last_modified
212
+ self.head
213
+ true
214
+ else
215
+ @headers = @meta_headers = {}
216
+ false
217
+ end
218
+ end
219
+
220
+ # Updates headers and meta-headers from S3.
221
+ # Returns +true+.
222
+ #
223
+ # key.meta_headers #=> {"family"=>"qwerty"}
224
+ # key.head #=> true
225
+ # key.meta_headers #=> {"family"=>"qwerty", "name"=>"asdfg"}
226
+ #
227
+ def head
228
+ @headers, @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name))
229
+ true
230
+ end
231
+
232
+ # Reload meta-headers only. Returns meta-headers hash.
233
+ #
234
+ # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
235
+ #
236
+ def reload_meta
237
+ @meta_headers = self.class.split_meta(@bucket.s3.interface.head(@bucket, @name)).last
238
+ end
239
+
240
+ # Replace meta-headers by new hash at S3. Returns new meta-headers hash.
241
+ #
242
+ # key.reload_meta #=> {"family"=>"qwerty", "name"=>"asdfg"}
243
+ # key.save_meta #=> {"family"=>"oops", "race" => "troll"}
244
+ # key.reload_meta #=> {"family"=>"oops", "race" => "troll"}
245
+ #
246
+ def save_meta(meta_headers)
247
+ meta = self.class.add_meta_prefix(meta_headers)
248
+ @bucket.s3.interface.copy(@bucket.name, @name, @bucket.name, @name, :replace, meta)
249
+ @meta_headers = self.class.split_meta(meta)[1]
250
+ end
251
+
252
+ # Check for existence of the key in the given bucket.
253
+ # Returns +true+ or +false+.
254
+ #
255
+ # key = Aws::S3::Key.create(bucket,'logs/today/1.log')
256
+ # key.exists? #=> false
257
+ # key.put('Woohoo!') #=> true
258
+ # key.exists? #=> true
259
+ #
260
+ def exists?
261
+ @bucket.key(self).last_modified ? true : false
262
+ end
263
+
264
+ # Remove key from bucket.
265
+ # Returns +true+.
266
+ #
267
+ # key.delete #=> true
268
+ #
269
+ def delete
270
+ raise 'Key name must be specified.' if @name.blank?
271
+ @bucket.s3.interface.delete(@bucket, @name)
272
+ end
273
+
274
+ # Return a list of grantees.
275
+ #
276
+ def grantees
277
+ Grantee::grantees(self)
278
+ end
279
+
280
+ end
281
+ end