s3 0.3.11 → 0.3.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1e94aa6f4e9214e728160e133f28264d1bd00b25
4
+ data.tar.gz: e3e15a25e2ef8f5882f088ec487ebb177cbc56b9
5
+ SHA512:
6
+ metadata.gz: 2faf2eb87b1bf05b904dea7134e4efddd14b118963a7a0bb817d26e3277a43072c180bb4fe92a1bec92ac5fc97f42f558144e33e27d890487bdbffdfff4969fb
7
+ data.tar.gz: e19ef4917499f81051c263883660353a5a17c1c334cae53cc212f251c2ded50520d0cc1970debc09f76876bcd03b0818f93958d27f3e2b7bc152337e6cfc40a6
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- s3 (0.3.11)
4
+ s3 (0.3.12)
5
5
  proxies (~> 0.2.0)
6
6
 
7
7
  GEM
data/README.rdoc CHANGED
@@ -28,6 +28,15 @@ It supports both: European and US buckets through the {REST API}[http://docs.ama
28
28
  first_bucket = service.buckets.find("first-bucket")
29
29
  #=> #<S3::Bucket:first-bucket>
30
30
 
31
+ === Create bucket
32
+
33
+ new_bucket = service.buckets.build("newbucketname")
34
+ new_bucket.save(:location => :eu)
35
+
36
+ Remember that bucket name for EU can't include "_" (underscore).
37
+
38
+ Please refer to: http://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html for more information about bucket name restrictions.
39
+
31
40
  === List objects in a bucket
32
41
 
33
42
  first_bucket.objects
@@ -67,6 +76,24 @@ It supports both: European and US buckets through the {REST API}[http://docs.ama
67
76
  Please note that new objects are created with "public-read" ACL by
68
77
  default.
69
78
 
79
+ === Fetch ACL
80
+
81
+ object = bucket.objects.find('lenna.png')
82
+ object.request_acl # or bucket.request_acl
83
+
84
+ This will return hash with all users/groups and theirs permissions
85
+
86
+ === Modify ACL
87
+
88
+ object = bucket.objects.find("lenna.png")
89
+ object.copy(:key => "lenna.png", :bucket => bucket, :acl => :private)
90
+
91
+ === Upload file direct to amazon
92
+
93
+ ==== Rails 3
94
+
95
+ To do that you just send file using proper form direct to amazon. You can create simple halper for that, for example like this one: https://gist.github.com/3169039
96
+
70
97
  == See also
71
98
 
72
99
  * rubygems[http://rubygems.org/gems/s3]
data/lib/s3/bucket.rb CHANGED
@@ -4,14 +4,14 @@ module S3
4
4
  include Proxies
5
5
  extend Forwardable
6
6
 
7
- attr_reader :name, :service
7
+ attr_reader :name, :service, :acl
8
8
 
9
9
  def_instance_delegators :service, :service_request
10
10
  private_class_method :new
11
11
 
12
12
  # Retrieves the bucket information from the server. Raises an
13
13
  # S3::Error exception if the bucket doesn't exist or you don't
14
- # have access to it, etc.
14
+ # have access to it.
15
15
  def retrieve
16
16
  bucket_headers
17
17
  self
@@ -30,11 +30,34 @@ module S3
30
30
  self.name == other.name and self.service == other.service
31
31
  end
32
32
 
33
+ # Retrieves acl for bucket from the server.
34
+ #
35
+ # Return:
36
+ # hash: user|group => permission
37
+ def request_acl
38
+ body = bucket_request(:get, :params => "acl").body
39
+ parse_acl(body)
40
+ end
41
+
42
+ # Assigns a new ACL to the bucket. Please note that ACL is not
43
+ # retrieved from the server and set to "public-read" by default.
44
+ #
45
+ # Valid Values: :private | :public_read | :public_read_write | authenticated_read
46
+ #
47
+ # ==== Example
48
+ # bucket.acl = :public_read
49
+ def acl=(acl)
50
+ @acl = acl.to_s.gsub("_","-") if acl
51
+ end
52
+
33
53
  # Similar to retrieve, but catches S3::Error::NoSuchBucket
34
- # exceptions and returns false instead.
54
+ # exceptions and returns false instead. Also catch S3::Error::ForbiddenBucket
55
+ # and return true
35
56
  def exists?
36
57
  retrieve
37
58
  true
59
+ rescue Error::ForbiddenBucket
60
+ true
38
61
  rescue Error::NoSuchBucket
39
62
  false
40
63
  end
@@ -101,6 +124,14 @@ module S3
101
124
  "#<#{self.class}:#{name}>"
102
125
  end
103
126
 
127
+ def save_acl(options = {})
128
+ headers = {}
129
+ headers[:content_length] = 0
130
+ headers[:x_amz_acl] = options[:acl] || acl || "public-read"
131
+
132
+ response = bucket_request(:put, :headers => headers, :path => name)
133
+ end
134
+
104
135
  private
105
136
 
106
137
  attr_writer :service
@@ -135,10 +166,13 @@ module S3
135
166
  def bucket_headers(options = {})
136
167
  response = bucket_request(:head, :params => options)
137
168
  rescue Error::ResponseError => e
138
- if e.response.code.to_i == 404
139
- raise Error::ResponseError.exception("NoSuchBucket").new("The specified bucket does not exist.", nil)
140
- else
141
- raise e
169
+ case e.response.code.to_i
170
+ when 404
171
+ raise Error::ResponseError.exception("NoSuchBucket").new("The specified bucket does not exist.", nil)
172
+ when 403
173
+ raise Error::ResponseError.exception("ForbiddenBucket").new("The specified bucket exist but you do not have access to it.", nil)
174
+ else
175
+ raise e
142
176
  end
143
177
  end
144
178
 
@@ -174,5 +208,6 @@ module S3
174
208
  def name_valid?(name)
175
209
  name =~ /\A[a-z0-9][a-z0-9\._-]{2,254}\Z/i and name !~ /\A#{URI::REGEXP::PATTERN::IPV4ADDR}\Z/
176
210
  end
211
+
177
212
  end
178
213
  end
@@ -5,10 +5,13 @@ module S3
5
5
  Bucket.send(:new, proxy_owner, name)
6
6
  end
7
7
 
8
- # Finds the bucket with given name
8
+ # Finds the bucket with given name (only those which exist and You have access to it)
9
+ # return nil in case Error::NoSuchBucket or Error::ForbiddenBucket
9
10
  def find_first(name)
10
11
  bucket = build(name)
11
12
  bucket.retrieve
13
+ rescue Error::ForbiddenBucket, Error::NoSuchBucket
14
+ nil
12
15
  end
13
16
  alias :find :find_first
14
17
 
data/lib/s3/connection.rb CHANGED
@@ -128,7 +128,7 @@ module S3
128
128
  # Hash of headers translated from symbol to string, containing
129
129
  # only interesting headers
130
130
  def self.parse_headers(headers)
131
- interesting_keys = [:content_type, :cache_control, :x_amz_acl, :x_amz_storage_class, :range,
131
+ interesting_keys = [:content_type, :content_length, :cache_control, :x_amz_acl, :x_amz_storage_class, :range,
132
132
  :if_modified_since, :if_unmodified_since,
133
133
  :if_match, :if_none_match,
134
134
  :content_disposition, :content_encoding,
data/lib/s3/exceptions.rb CHANGED
@@ -52,6 +52,7 @@ module S3
52
52
  class EntityTooSmall < ResponseError; end
53
53
  class EntityTooLarge < ResponseError; end
54
54
  class ExpiredToken < ResponseError; end
55
+ class ForbiddenBucket < ResponseError; end
55
56
  class IncompleteBody < ResponseError; end
56
57
  class IncorrectNumberOfFilesInPostRequestPOST < ResponseError; end
57
58
  class InlineDataTooLarge < ResponseError; end
data/lib/s3/object.rb CHANGED
@@ -52,9 +52,11 @@ module S3
52
52
  end
53
53
 
54
54
  # Retrieves the object from the server. Method is used to download
55
- # object information only (content type, size and so on). It does
56
- # NOT download the content of the object (use the #content method
57
- # to do it).
55
+ # object information only (content type, size).
56
+ # Notice: It does NOT download the content of the object
57
+ # (use the #content method to do it).
58
+ # Notice: this do not fetch acl information, use #request_acl
59
+ # method for that.
58
60
  def retrieve
59
61
  object_headers
60
62
  self
@@ -70,6 +72,15 @@ module S3
70
72
  false
71
73
  end
72
74
 
75
+ # Retrieves acl for object from the server.
76
+ #
77
+ # Return:
78
+ # hash: user|group => permission
79
+ def request_acl
80
+ response = object_request(:get, :params => "acl")
81
+ parse_acl(response.body)
82
+ end
83
+
73
84
  # Downloads the content of the object, and caches it. Pass true to
74
85
  # clear the cache and download the object again.
75
86
  def content(reload = false)
@@ -107,7 +118,7 @@ module S3
107
118
  # Returns Object's URL using protocol specified in service,
108
119
  # e.g. <tt>http://domain.com.s3.amazonaws.com/key/with/path.extension</tt>
109
120
  def url
110
- URI.escape("#{protocol}#{host}/#{path_prefix}#{key}")
121
+ "#{protocol}#{host}/#{path_prefix}#{URI.escape(key, /[^#{URI::REGEXP::PATTERN::UNRESERVED}\/]/)}"
111
122
  end
112
123
 
113
124
  # Returns a temporary url to the object that expires on the
data/lib/s3/parser.rb CHANGED
@@ -48,5 +48,43 @@ module S3
48
48
  def parse_is_truncated xml
49
49
  rexml_document(xml).elements["ListBucketResult/IsTruncated"].text =='true'
50
50
  end
51
+
52
+
53
+ # Parse acl response and return hash with grantee and their permissions
54
+ def parse_acl(xml)
55
+ grants = {}
56
+ rexml_document(xml).elements.each("AccessControlPolicy/AccessControlList/Grant") do |grant|
57
+ grants.merge!(extract_grantee(grant))
58
+ end
59
+ grants
60
+ end
61
+
62
+ private
63
+
64
+ def convert_uri_to_group_name(uri)
65
+ case uri
66
+ when "http://acs.amazonaws.com/groups/global/AllUsers"
67
+ return "Everyone"
68
+ when "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
69
+ return "Authenticated Users"
70
+ when "http://acs.amazonaws.com/groups/s3/LogDelivery"
71
+ return "Log Delivery"
72
+ else
73
+ return uri
74
+ end
75
+ end
76
+
77
+ def extract_grantee(grant)
78
+ grants = {}
79
+ grant.each_element_with_attribute("xsi:type", "Group") do |grantee|
80
+ group_name = convert_uri_to_group_name(grantee.get_text("URI").value)
81
+ grants[group_name] = grant.get_text("Permission").value
82
+ end
83
+ grant.each_element_with_attribute("xsi:type", "CanonicalUser") do |grantee|
84
+ user_name = grantee.get_text("DisplayName").value
85
+ grants[user_name] = grant.get_text("Permission").value
86
+ end
87
+ grants
88
+ end
51
89
  end
52
90
  end
data/lib/s3/signature.rb CHANGED
@@ -25,7 +25,9 @@ module S3
25
25
  request = options[:request]
26
26
  access_key_id = options[:access_key_id]
27
27
 
28
- options.merge!(:headers => request, :method => request.method, :resource => request.path)
28
+ options.merge!(:headers => request,
29
+ :method => request.method,
30
+ :resource => request.path)
29
31
 
30
32
  signature = canonicalized_signature(options)
31
33
 
@@ -45,6 +47,8 @@ module S3
45
47
  # the resource, defaults to GET
46
48
  # * <tt>:headers</tt> - Any additional HTTP headers you intend
47
49
  # to use when requesting the resource
50
+ # * <tt>:add_bucket_to_host</tt> - Use in case of virtual-host style,
51
+ # defaults to false
48
52
  def self.generate_temporary_url_signature(options)
49
53
  bucket = options[:bucket]
50
54
  resource = options[:resource]
@@ -54,7 +58,10 @@ module S3
54
58
  headers = options[:headers] || {}
55
59
  headers.merge!("date" => expires.to_i.to_s)
56
60
 
57
- options.merge!(:resource => "/#{bucket}/#{URI.escape(resource)}",
61
+ resource = "/#{URI.escape(resource, /[^#{URI::REGEXP::PATTERN::UNRESERVED}\/]/)}"
62
+ resource = "/#{bucket}" + resource unless options[:add_bucket_to_host]
63
+
64
+ options.merge!(:resource => resource,
58
65
  :method => options[:method] || :get,
59
66
  :headers => headers)
60
67
  signature = canonicalized_signature(options)
@@ -76,14 +83,25 @@ module S3
76
83
  # the resource, defaults to GET
77
84
  # * <tt>:headers</tt> - Any additional HTTP headers you intend
78
85
  # to use when requesting the resource
86
+ # * <tt>:add_bucket_to_host</tt> - Use in case of virtual-host style,
87
+ # defaults to false
79
88
  def self.generate_temporary_url(options)
80
89
  bucket = options[:bucket]
81
90
  resource = options[:resource]
82
91
  access_key = options[:access_key]
83
92
  expires = options[:expires_at].to_i
93
+ host = S3::HOST
94
+
95
+ if options[:add_bucket_to_host]
96
+ host = bucket + '.' + host
97
+ url = "http://#{host}/#{resource}"
98
+ else
99
+ url = "http://#{host}/#{bucket}/#{resource}"
100
+ end
101
+
102
+ options[:host] = host
84
103
  signature = generate_temporary_url_signature(options)
85
104
 
86
- url = "http://#{S3::HOST}/#{bucket}/#{resource}"
87
105
  url << "?AWSAccessKeyId=#{access_key}"
88
106
  url << "&Expires=#{expires}"
89
107
  url << "&Signature=#{signature}"
@@ -209,7 +227,7 @@ module S3
209
227
  # requests that don't address a bucket, do nothing. For more
210
228
  # information on virtual hosted-style requests, see Virtual
211
229
  # Hosting of Buckets.
212
- bucket_name = host.sub(/\.?s3\.amazonaws\.com\Z/, "")
230
+ bucket_name = host.sub(/\.?#{S3::HOST}\Z/, "")
213
231
  string << "/#{bucket_name}" unless bucket_name.empty?
214
232
 
215
233
  # 3. Append the path part of the un-decoded HTTP Request-URI,
data/lib/s3/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module S3
2
- VERSION = "0.3.11"
2
+ VERSION = "0.3.12"
3
3
  end
data/s3.gemspec CHANGED
@@ -9,9 +9,9 @@ Gem::Specification.new do |s|
9
9
  s.name = "s3"
10
10
  s.version = S3::VERSION
11
11
  s.platform = Gem::Platform::RUBY
12
- s.authors = ["Jakub Kuźma"]
13
- s.email = ["qoobaa@gmail.com"]
14
- s.homepage = "http://jah.pl/projects/s3.html"
12
+ s.authors = ["Kuba Kuźma"]
13
+ s.email = ["kuba@jah.pl"]
14
+ s.homepage = "http://github.com/qoobaa/s3"
15
15
  s.summary = "Library for accessing S3 objects and buckets"
16
16
  s.description = "S3 library provides access to Amazon's Simple Storage Service. It supports both: European and US buckets through REST API."
17
17
 
data/test/object_test.rb CHANGED
@@ -68,6 +68,11 @@ class ObjectTest < Test::Unit::TestCase
68
68
  expected = "http://images.s3.amazonaws.com/Lena%20S%C3%B6derberg.png"
69
69
  actual = object12.url
70
70
  assert_equal expected, actual
71
+
72
+ object13 = S3::Object.send(:new, bucket1, :key => "Lena Söderberg [1].png")
73
+ expected = "http://images.s3.amazonaws.com/Lena%20S%C3%B6derberg%20%5B1%5D.png"
74
+ actual = object13.url
75
+ assert_equal expected, actual
71
76
 
72
77
  bucket2 = S3::Bucket.send(:new, @service, "images_new")
73
78
 
data/test/service_test.rb CHANGED
@@ -87,10 +87,8 @@ class ServiceTest < Test::Unit::TestCase
87
87
  end
88
88
  end
89
89
 
90
- test "buckets find first fail" do
91
- assert_raise S3::Error::NoSuchBucket do
92
- @service_bucket_not_exists.buckets.find_first("data2.example.com")
93
- end
90
+ test "buckets find first return nil" do
91
+ assert_equal nil, @service_bucket_not_exists.buckets.find_first("data2.example.com")
94
92
  end
95
93
 
96
94
  test "buckets find all on empty list" do
@@ -151,6 +151,17 @@ class SignatureTest < Test::Unit::TestCase
151
151
  expected = "gs6xNznrLJ4Bd%2B1y9pcy2HOSVeg%3D"
152
152
  assert_equal expected, actual
153
153
  end
154
+
155
+ test "temporary signature for object get with non-unreserved URI characters" do
156
+ actual = S3::Signature.generate_temporary_url_signature(
157
+ :bucket => "johnsmith",
158
+ :resource => "photos/puppy[1].jpg",
159
+ :secret_access_key => "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o",
160
+ :expires_at => 1175046589
161
+ )
162
+ expected = "gwCM0mVb9IrEPiUf8iaml6EISPc%3D"
163
+ assert_equal expected, actual
164
+ end
154
165
 
155
166
  test "temporary signature for object post" do
156
167
  actual = S3::Signature.generate_temporary_url_signature(
@@ -189,6 +200,18 @@ class SignatureTest < Test::Unit::TestCase
189
200
  assert_equal expected, actual
190
201
  end
191
202
 
203
+ test "temporary url for object get with bucket in the hostname" do
204
+ actual = S3::Signature.generate_temporary_url(
205
+ :bucket => "johnsmith",
206
+ :resource => "photos/puppy.jpg",
207
+ :secret_access_key => "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o",
208
+ :expires_at => 1175046589,
209
+ :add_bucket_to_host => true
210
+ )
211
+ expected = "http://johnsmith.s3.amazonaws.com/photos/puppy.jpg?AWSAccessKeyId=&Expires=1175046589&Signature=gs6xNznrLJ4Bd%2B1y9pcy2HOSVeg%3D"
212
+ assert_equal expected, actual
213
+ end
214
+
192
215
  test "temporary url for object put with headers" do
193
216
  actual = S3::Signature.generate_temporary_url(
194
217
  :bucket => "johnsmith",
metadata CHANGED
@@ -1,64 +1,75 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: s3
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.11
5
- prerelease:
4
+ version: 0.3.12
6
5
  platform: ruby
7
6
  authors:
8
- - Jakub Kuźma
7
+ - Kuba Kuźma
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2011-12-24 00:00:00.000000000 Z
11
+ date: 2013-06-05 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: proxies
16
- requirement: &6899940 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
21
19
  version: 0.2.0
22
20
  type: :runtime
23
21
  prerelease: false
24
- version_requirements: *6899940
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.0
25
27
  - !ruby/object:Gem::Dependency
26
28
  name: test-unit
27
- requirement: &6952760 !ruby/object:Gem::Requirement
28
- none: false
29
+ requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
- - - ! '>='
31
+ - - '>='
31
32
  - !ruby/object:Gem::Version
32
33
  version: '2.0'
33
34
  type: :development
34
35
  prerelease: false
35
- version_requirements: *6952760
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
36
41
  - !ruby/object:Gem::Dependency
37
42
  name: mocha
38
- requirement: &6952100 !ruby/object:Gem::Requirement
39
- none: false
43
+ requirement: !ruby/object:Gem::Requirement
40
44
  requirements:
41
- - - ! '>='
45
+ - - '>='
42
46
  - !ruby/object:Gem::Version
43
47
  version: '0'
44
48
  type: :development
45
49
  prerelease: false
46
- version_requirements: *6952100
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
47
55
  - !ruby/object:Gem::Dependency
48
56
  name: bundler
49
- requirement: &6950720 !ruby/object:Gem::Requirement
50
- none: false
57
+ requirement: !ruby/object:Gem::Requirement
51
58
  requirements:
52
- - - ! '>='
59
+ - - '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: 1.0.0
55
62
  type: :development
56
63
  prerelease: false
57
- version_requirements: *6950720
58
- description: ! 'S3 library provides access to Amazon''s Simple Storage Service. It
59
- supports both: European and US buckets through REST API.'
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.0
69
+ description: 'S3 library provides access to Amazon''s Simple Storage Service. It supports
70
+ both: European and US buckets through REST API.'
60
71
  email:
61
- - qoobaa@gmail.com
72
+ - kuba@jah.pl
62
73
  executables: []
63
74
  extensions: []
64
75
  extra_rdoc_files: []
@@ -88,31 +99,27 @@ files:
88
99
  - test/service_test.rb
89
100
  - test/signature_test.rb
90
101
  - test/test_helper.rb
91
- homepage: http://jah.pl/projects/s3.html
102
+ homepage: http://github.com/qoobaa/s3
92
103
  licenses: []
104
+ metadata: {}
93
105
  post_install_message:
94
106
  rdoc_options: []
95
107
  require_paths:
96
108
  - lib
97
109
  required_ruby_version: !ruby/object:Gem::Requirement
98
- none: false
99
110
  requirements:
100
- - - ! '>='
111
+ - - '>='
101
112
  - !ruby/object:Gem::Version
102
113
  version: '0'
103
- segments:
104
- - 0
105
- hash: -3173124590106976282
106
114
  required_rubygems_version: !ruby/object:Gem::Requirement
107
- none: false
108
115
  requirements:
109
- - - ! '>='
116
+ - - '>='
110
117
  - !ruby/object:Gem::Version
111
118
  version: 1.3.6
112
119
  requirements: []
113
120
  rubyforge_project: s3
114
- rubygems_version: 1.8.10
121
+ rubygems_version: 2.0.2
115
122
  signing_key:
116
- specification_version: 3
123
+ specification_version: 4
117
124
  summary: Library for accessing S3 objects and buckets
118
125
  test_files: []