s33r 0.4.2 → 0.5
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.
- data/examples/cli/instant_download_server.rb +88 -0
- data/examples/cli/s3cli.rb +31 -52
- data/examples/cli/simple.rb +16 -6
- data/examples/fores33r/app/controllers/browser_controller.rb +12 -10
- data/examples/fores33r/app/helpers/application_helper.rb +2 -1
- data/examples/fores33r/app/views/browser/_upload.rhtml +1 -1
- data/examples/fores33r/app/views/browser/index.rhtml +4 -4
- data/examples/fores33r/config/environment.rb +5 -3
- data/examples/fores33r/log/development.log +2259 -0
- data/examples/fores33r/log/mongrel.log +59 -0
- data/examples/s3.yaml +2 -6
- data/lib/s33r/bucket.rb +103 -0
- data/lib/s33r/bucket_listing.rb +33 -76
- data/lib/s33r/client.rb +305 -446
- data/lib/s33r/networking.rb +197 -0
- data/lib/s33r/s33r_exception.rb +29 -18
- data/lib/s33r/s33r_http.rb +36 -18
- data/lib/s33r/s3_acl.rb +32 -52
- data/lib/s33r/s3_logging.rb +117 -0
- data/lib/s33r/s3_obj.rb +124 -69
- data/lib/s33r/utility.rb +447 -0
- data/test/cases/spec_acl.rb +10 -40
- data/test/cases/spec_bucket_listing.rb +12 -32
- data/test/cases/spec_logging.rb +47 -0
- data/test/cases/spec_networking.rb +11 -0
- data/test/cases/spec_s3_object.rb +44 -5
- data/test/cases/spec_utility.rb +264 -0
- data/test/files/acl.xml +0 -6
- data/test/files/config.yaml +5 -0
- data/test/files/logging_status_disabled.xml +3 -0
- data/test/files/logging_status_enabled.xml +7 -0
- data/test/test_setup.rb +7 -2
- metadata +16 -94
- data/examples/cli/acl_x.rb +0 -41
- data/examples/cli/logging_x.rb +0 -20
- data/examples/fores33r/README +0 -183
- data/html/classes/MIME.html +0 -120
- data/html/classes/MIME/InvalidContentType.html +0 -119
- data/html/classes/MIME/Type.html +0 -1173
- data/html/classes/MIME/Types.html +0 -566
- data/html/classes/Net.html +0 -108
- data/html/classes/Net/HTTPGenericRequest.html +0 -233
- data/html/classes/Net/HTTPResponse.html +0 -271
- data/html/classes/S33r.html +0 -986
- data/html/classes/S33r/BucketListing.html +0 -434
- data/html/classes/S33r/Client.html +0 -1575
- data/html/classes/S33r/LoggingResource.html +0 -222
- data/html/classes/S33r/NamedBucket.html +0 -693
- data/html/classes/S33r/OrderlyXmlMarkup.html +0 -165
- data/html/classes/S33r/S33rException.html +0 -124
- data/html/classes/S33r/S33rException/BucketListingMaxKeysError.html +0 -111
- data/html/classes/S33r/S33rException/BucketNotLogTargetable.html +0 -119
- data/html/classes/S33r/S33rException/InvalidBucketListing.html +0 -111
- data/html/classes/S33r/S33rException/InvalidPermission.html +0 -111
- data/html/classes/S33r/S33rException/InvalidS3GroupType.html +0 -111
- data/html/classes/S33r/S33rException/MalformedBucketName.html +0 -111
- data/html/classes/S33r/S33rException/MethodNotAvailable.html +0 -111
- data/html/classes/S33r/S33rException/MissingBucketName.html +0 -111
- data/html/classes/S33r/S33rException/MissingRequiredHeaders.html +0 -111
- data/html/classes/S33r/S33rException/MissingResource.html +0 -111
- data/html/classes/S33r/S33rException/S3FallenOver.html +0 -111
- data/html/classes/S33r/S33rException/TryingToPutEmptyResource.html +0 -117
- data/html/classes/S33r/S33rException/UnsupportedCannedACL.html +0 -111
- data/html/classes/S33r/S33rException/UnsupportedHTTPMethod.html +0 -111
- data/html/classes/S33r/S3ACL.html +0 -125
- data/html/classes/S33r/S3ACL/ACLDoc.html +0 -521
- data/html/classes/S33r/S3ACL/AmazonCustomer.html +0 -168
- data/html/classes/S33r/S3ACL/CanonicalUser.html +0 -212
- data/html/classes/S33r/S3ACL/Grant.html +0 -403
- data/html/classes/S33r/S3ACL/Grantee.html +0 -239
- data/html/classes/S33r/S3ACL/Group.html +0 -178
- data/html/classes/S33r/S3Object.html +0 -618
- data/html/classes/S33r/Sync.html +0 -152
- data/html/classes/XML.html +0 -202
- data/html/classes/XML/Document.html +0 -125
- data/html/classes/XML/Node.html +0 -124
- data/html/created.rid +0 -1
- data/html/files/CHANGELOG.html +0 -107
- data/html/files/MIT-LICENSE.html +0 -129
- data/html/files/README_txt.html +0 -259
- data/html/files/lib/s33r/bucket_listing_rb.html +0 -101
- data/html/files/lib/s33r/builder_rb.html +0 -108
- data/html/files/lib/s33r/client_rb.html +0 -111
- data/html/files/lib/s33r/core_rb.html +0 -113
- data/html/files/lib/s33r/libxml_extensions_rb.html +0 -101
- data/html/files/lib/s33r/libxml_loader_rb.html +0 -109
- data/html/files/lib/s33r/logging_rb.html +0 -108
- data/html/files/lib/s33r/mimetypes_rb.html +0 -120
- data/html/files/lib/s33r/named_bucket_rb.html +0 -101
- data/html/files/lib/s33r/s33r_exception_rb.html +0 -101
- data/html/files/lib/s33r/s33r_http_rb.html +0 -108
- data/html/files/lib/s33r/s3_acl_rb.html +0 -108
- data/html/files/lib/s33r/s3_obj_rb.html +0 -108
- data/html/files/lib/s33r/sync_rb.html +0 -101
- data/html/files/lib/s33r_rb.html +0 -101
- data/html/fr_class_index.html +0 -66
- data/html/fr_file_index.html +0 -44
- data/html/fr_method_index.html +0 -183
- data/html/index.html +0 -24
- data/html/rdoc-style.css +0 -208
- data/lib/s33r/core.rb +0 -296
- data/lib/s33r/logging.rb +0 -43
- data/lib/s33r/named_bucket.rb +0 -148
- data/lib/s33r/sync.rb +0 -13
- data/test/cases/spec_all_buckets.rb +0 -28
- data/test/cases/spec_client.rb +0 -101
- data/test/cases/spec_core.rb +0 -128
- data/test/cases/spec_namedbucket.rb +0 -46
- data/test/cases/spec_sync.rb +0 -34
- data/test/files/all_buckets.xml +0 -21
- data/test/files/client_config.yml +0 -5
- data/test/files/namedbucket_config.yml +0 -8
- data/test/files/namedbucket_config2.yml +0 -8
- data/test/test_bucket_setup.rb +0 -41
data/test/cases/spec_acl.rb
CHANGED
|
@@ -3,7 +3,7 @@ require base + '/../test_setup'
|
|
|
3
3
|
|
|
4
4
|
context 'S33r ACL functions' do
|
|
5
5
|
setup do
|
|
6
|
-
@acl_grant_xml1 =
|
|
6
|
+
@acl_grant_xml1 = load_test_xml('acl_grant1.xml')
|
|
7
7
|
@canonical_user = CanonicalUser.new('a9a7b886d6fd24a52fe8ca5bef65f89a64e0193f23000e241bf9b1c61be666e9',
|
|
8
8
|
'chriscustomer')
|
|
9
9
|
@canonical_user_grant = Grant.new(@canonical_user, :all)
|
|
@@ -11,24 +11,22 @@ context 'S33r ACL functions' do
|
|
|
11
11
|
'mimbo')
|
|
12
12
|
@canonical_user_grant2 = Grant.new(@canonical_user2, :read)
|
|
13
13
|
|
|
14
|
-
@acl_grant_xml2 =
|
|
14
|
+
@acl_grant_xml2 = load_test_xml('acl_grant2.xml')
|
|
15
15
|
@amazon_grantee = AmazonCustomer.new('elliot@test.org')
|
|
16
16
|
@amazon_grant = Grant.new(@amazon_grantee, :read)
|
|
17
|
-
@amazon_grantee2 = AmazonCustomer.new('bingo@blinky.com')
|
|
18
|
-
@amazon_grant2 = Grant.new(@amazon_grantee2, :write)
|
|
19
17
|
|
|
20
|
-
@acl_grant_xml3 =
|
|
18
|
+
@acl_grant_xml3 = load_test_xml('acl_grant3.xml')
|
|
21
19
|
@group_grantee = Group.new(:all_users)
|
|
22
20
|
@group_grant = Grant.new(@group_grantee, :write)
|
|
23
21
|
|
|
24
|
-
@acl_grant_xml4 =
|
|
22
|
+
@acl_grant_xml4 = load_test_xml('acl_grant4.xml')
|
|
25
23
|
@group_grantee2 = Group.new(:authenticated_users)
|
|
26
24
|
@group_grant2 = Grant.new(@group_grantee2, :read_acl)
|
|
27
25
|
|
|
28
|
-
@full_acl_xml =
|
|
29
|
-
@full_acl_doc =
|
|
26
|
+
@full_acl_xml = load_test_xml('acl.xml')
|
|
27
|
+
@full_acl_doc = Policy.new(@canonical_user)
|
|
30
28
|
@full_acl_doc.grants = [@canonical_user_grant, @canonical_user_grant2, @amazon_grant,
|
|
31
|
-
@
|
|
29
|
+
@group_grant, @group_grant2]
|
|
32
30
|
|
|
33
31
|
# Grants created using convenience methods
|
|
34
32
|
@con_canonical_user_grant = Grant.for_canonical_user('a9a7b886d6fd24a52fe8ca5bef65f89a64e0193f23000e241bf9b1c61be666e9',
|
|
@@ -36,22 +34,11 @@ context 'S33r ACL functions' do
|
|
|
36
34
|
@con_canonical_user_grant2 = Grant.for_canonical_user('baa7b886d6fd24a52fe8ca5bef65f89a64e0193f23000e241bf9b1c61be666e9',
|
|
37
35
|
'mimbo', :read)
|
|
38
36
|
@con_amazon_grant = Grant.for_amazon_customer('elliot@test.org', :read)
|
|
39
|
-
@con_amazon_grant2 = Grant.for_amazon_customer('bingo@blinky.com', :write)
|
|
40
37
|
@con_group_grant1 = Grant.for_group(:all_users, :write)
|
|
41
38
|
@con_group_grant2 = Grant.for_group(:authenticated_users, :read_acl)
|
|
42
|
-
@con_full_acl_doc =
|
|
39
|
+
@con_full_acl_doc = Policy.new(@canonical_user)
|
|
43
40
|
@con_full_acl_doc.grants = [@con_canonical_user_grant, @con_canonical_user_grant2,
|
|
44
|
-
@con_amazon_grant, @
|
|
45
|
-
|
|
46
|
-
# Tests for logging ACLs
|
|
47
|
-
@logging_acl_xml = clean_xml(File.open(File.join(File.dirname(__FILE__), '../files/logging_acl.xml')).read)
|
|
48
|
-
@logging_acl_doc = ACLDoc.from_xml(@logging_acl_xml)
|
|
49
|
-
@log_delivery_write = Grant.for_group(:log_delivery, :write)
|
|
50
|
-
@log_delivery_read_acl = Grant.for_group(:log_delivery, :read_acl)
|
|
51
|
-
|
|
52
|
-
# clone without the logging grants
|
|
53
|
-
@logging_acl_doc_no_grants = ACLDoc.from_xml(@logging_acl_xml)
|
|
54
|
-
@logging_acl_doc_no_grants.grants.delete_if { |g| @log_delivery_write == g or @log_delivery_read_acl == g }
|
|
41
|
+
@con_amazon_grant, @con_group_grant1, @con_group_grant2]
|
|
55
42
|
end
|
|
56
43
|
|
|
57
44
|
specify 'should represent a grant for a CanonicalUser' do
|
|
@@ -119,28 +106,11 @@ context 'S33r ACL functions' do
|
|
|
119
106
|
end
|
|
120
107
|
|
|
121
108
|
specify 'should parse AccessControlPolicy documents' do
|
|
122
|
-
acl_doc_from_xml =
|
|
109
|
+
acl_doc_from_xml = Policy.from_xml(@full_acl_xml)
|
|
123
110
|
acl_doc_from_xml.owner.should.equal @full_acl_doc.owner
|
|
124
111
|
@full_acl_doc.grants.each do |g|
|
|
125
112
|
acl_doc_from_xml.grants = acl_doc_from_xml.grants.delete_if { |i| i == g }
|
|
126
113
|
end
|
|
127
114
|
acl_doc_from_xml.grants.should.be.empty
|
|
128
115
|
end
|
|
129
|
-
|
|
130
|
-
specify 'should represent LogDelivery grants so buckets can act as logging targets' do
|
|
131
|
-
@logging_acl_doc.grants.should.include @log_delivery_write
|
|
132
|
-
@logging_acl_doc.grants.should.include @log_delivery_read_acl
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
specify 'should be able to report whether an ACL indicates bucket is a logging target' do
|
|
136
|
-
@logging_acl_doc.log_targetable?.should.be true
|
|
137
|
-
|
|
138
|
-
# check the variant where the grants have been removed
|
|
139
|
-
@logging_acl_doc_no_grants.log_targetable?.should.be false
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
specify 'should be able to simply add logging target grants to an ACL' do
|
|
143
|
-
@logging_acl_doc_no_grants.add_log_target_grants
|
|
144
|
-
clean_xml(@logging_acl_doc_no_grants.to_xml).should.equal @logging_acl_xml
|
|
145
|
-
end
|
|
146
116
|
end
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
require base + '/../test_setup'
|
|
1
|
+
require File.dirname(__FILE__) + '/../test_setup'
|
|
3
2
|
require 'set'
|
|
4
3
|
|
|
5
4
|
context 'S33r bucket listing' do
|
|
6
5
|
setup do
|
|
7
|
-
|
|
8
|
-
@
|
|
9
|
-
|
|
10
|
-
@
|
|
11
|
-
|
|
12
|
-
@with_empty_bucket_listing_xml = File.open(xml_file3) { |f| f.read }
|
|
13
|
-
xml_file4 = File.join(base, '../files/bucket_listing_broken.xml')
|
|
14
|
-
@with_broken_bucket_listing_xml = File.open(xml_file4) { |f| f.read }
|
|
15
|
-
xml_file5 = File.join(base, '../files/suspect_bucket_listing.xml')
|
|
16
|
-
@with_suspect_bl_xml = File.open(xml_file5) { |f| f.read }
|
|
6
|
+
@with_bucket_listing_xml = load_test_xml('bucket_listing.xml')
|
|
7
|
+
@with_bucket_listing_xml2 = load_test_xml('bucket_listing2.xml')
|
|
8
|
+
@with_empty_bucket_listing_xml = load_test_xml('bucket_listing3.xml')
|
|
9
|
+
@with_broken_bucket_listing_xml = load_test_xml('bucket_listing_broken.xml')
|
|
10
|
+
@with_suspect_bl_xml = load_test_xml('suspect_bucket_listing.xml')
|
|
17
11
|
|
|
18
12
|
@bucket_listing = BucketListing.new(@with_bucket_listing_xml)
|
|
19
13
|
@bucket_properties = %w(name prefix marker max_keys is_truncated)
|
|
@@ -25,11 +19,11 @@ context 'S33r bucket listing' do
|
|
|
25
19
|
end
|
|
26
20
|
|
|
27
21
|
specify 'cannot be created from invalid XML' do
|
|
28
|
-
lambda { BucketListing.new(nil) }.should.raise
|
|
22
|
+
lambda { BucketListing.new(nil) }.should.raise S3Exception::InvalidBucketListing
|
|
29
23
|
end
|
|
30
24
|
|
|
31
25
|
specify 'should recover gracefully from broken bucket listing XML' do
|
|
32
|
-
lambda { BucketListing.new(@with_broken_bucket_listing_xml) }.should.raise
|
|
26
|
+
lambda { BucketListing.new(@with_broken_bucket_listing_xml) }.should.raise S3Exception::InvalidBucketListing
|
|
33
27
|
end
|
|
34
28
|
|
|
35
29
|
specify 'should cope if bucket is empty (i.e. no <Contents> elements)' do
|
|
@@ -69,30 +63,16 @@ context 'S33r bucket listing' do
|
|
|
69
63
|
end
|
|
70
64
|
|
|
71
65
|
specify 'should store resources (<Contents> elements) in a hash' do
|
|
72
|
-
@bucket_listing.
|
|
73
|
-
first_obj = @bucket_listing
|
|
66
|
+
@bucket_listing.size.should_be 10
|
|
67
|
+
first_obj = @bucket_listing['/home/ell/dir1/four.txt']
|
|
74
68
|
first_obj.should_be_instance_of S3Object
|
|
75
69
|
end
|
|
76
|
-
|
|
77
|
-
specify 'should enable access to metadata for a resource by its key' do
|
|
78
|
-
obj = @bucket_listing['/home/ell/dir1/four.txt']
|
|
79
|
-
obj.should_be_instance_of S3Object
|
|
80
|
-
obj.etag.should_equal '24ce59274b89287b3960c184153ac24b'
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
specify 'should build a full S3Object representation when given XML from a GET on a resource key' do
|
|
84
|
-
todo
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
specify 'should provide easy access to <CommonPrefixes> elements as a hash' do
|
|
88
|
-
todo
|
|
89
|
-
end
|
|
90
70
|
|
|
91
71
|
# attempting to fix ListBucketResult errors reported by Alex Payne
|
|
92
72
|
specify 'should handle suspect bucket listing' do
|
|
93
73
|
puts @with_suspect_bl_xml
|
|
94
74
|
bl = BucketListing.new(@with_suspect_bl_xml)
|
|
95
|
-
bl.
|
|
96
|
-
bl.
|
|
75
|
+
bl.size.should_be 1
|
|
76
|
+
bl.keys.should.include 'orly.jpg'
|
|
97
77
|
end
|
|
98
78
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
base = File.dirname(__FILE__)
|
|
2
|
+
require base + '/../test_setup'
|
|
3
|
+
|
|
4
|
+
context 'S33r logging' do
|
|
5
|
+
setup do
|
|
6
|
+
# Tests for BucketLoggingStatus generator
|
|
7
|
+
@logging_enabled_xml = load_test_xml('logging_status_enabled.xml')
|
|
8
|
+
@logging_disabled_xml = load_test_xml('logging_status_disabled.xml')
|
|
9
|
+
@logging_resource_to_enable = LoggingResource.new('mylogs', 'mybucket-access_log-')
|
|
10
|
+
@logging_resource_to_disable = LoggingResource.new
|
|
11
|
+
|
|
12
|
+
# Tests for logging ACLs
|
|
13
|
+
@logging_acl_xml = load_test_xml('logging_acl.xml')
|
|
14
|
+
@logging_acl_doc = Policy.from_xml(@logging_acl_xml)
|
|
15
|
+
@log_delivery_write = Grant.for_group(:log_delivery, :write)
|
|
16
|
+
@log_delivery_read_acl = Grant.for_group(:log_delivery, :read_acl)
|
|
17
|
+
|
|
18
|
+
# clone without the logging grants
|
|
19
|
+
@logging_acl_doc_no_grants = Policy.from_xml(@logging_acl_xml)
|
|
20
|
+
@logging_acl_doc_no_grants.grants.delete_if { |g| @log_delivery_write == g or @log_delivery_read_acl == g }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
specify 'should generate BucketLoggingStatus XML document to enable logging' do
|
|
24
|
+
clean_xml(@logging_resource_to_enable.to_xml).should.equal @logging_enabled_xml
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
specify 'should generate BucketLoggingStatus XML document to disable logging' do
|
|
28
|
+
clean_xml(@logging_resource_to_disable.to_xml).should.equal @logging_disabled_xml
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
specify 'should represent LogDelivery grants so buckets can act as logging targets' do
|
|
32
|
+
@logging_acl_doc.grants.should.include @log_delivery_write
|
|
33
|
+
@logging_acl_doc.grants.should.include @log_delivery_read_acl
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
specify 'should be able to report whether an ACL indicates bucket is a logging target' do
|
|
37
|
+
@logging_acl_doc.log_targetable?.should.be true
|
|
38
|
+
|
|
39
|
+
# check the variant where the grants have been removed
|
|
40
|
+
@logging_acl_doc_no_grants.log_targetable?.should.be false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
specify 'should be able to simply add logging target grants to an ACL' do
|
|
44
|
+
@logging_acl_doc_no_grants.add_log_target_grants
|
|
45
|
+
clean_xml(@logging_acl_doc_no_grants.to_xml).should.equal @logging_acl_xml
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -5,6 +5,21 @@ context 'S3 object' do
|
|
|
5
5
|
setup do
|
|
6
6
|
@s3_object_xml = File.open(File.join(base, '../files/s3_object.xml')).read
|
|
7
7
|
@s3obj = S3Object.from_xml_string(@s3_object_xml)
|
|
8
|
+
@s3obj.bucket = Bucket.new('test-bucket', :access => Testing::ACCESS_KEY, \
|
|
9
|
+
:secret => Testing::SECRET_ACCESS_KEY)
|
|
10
|
+
|
|
11
|
+
# Orphan s3obj (not attached to a bucket).
|
|
12
|
+
@orphan_obj = S3Object.new('my-key')
|
|
13
|
+
|
|
14
|
+
bucket = Bucket.new('quotes', :access => Testing::ACCESS_KEY, \
|
|
15
|
+
:secret => Testing::SECRET_ACCESS_KEY, :use_ssl => false)
|
|
16
|
+
@s3obj2 = S3Object.new('nelson', nil, :bucket => bucket)
|
|
17
|
+
authenticated_url_end = "s3.amazonaws.com/quotes/nelson?Signature=vjbyPxybdZaNmGa%2ByT272YEAiv4%3D&"+
|
|
18
|
+
"AWSAccessKeyId=44CF9590006BF252F707&Expires=1141889120"
|
|
19
|
+
@correct_authenticated_url = "http://" + authenticated_url_end
|
|
20
|
+
@correct_public_url = "http://s3.amazonaws.com/quotes/nelson"
|
|
21
|
+
@correct_public_url_with_ssl = "https://s3.amazonaws.com/quotes/nelson"
|
|
22
|
+
@correct_public_url_with_subdomain = "http://quotes.s3.amazonaws.com/nelson"
|
|
8
23
|
end
|
|
9
24
|
|
|
10
25
|
specify 'can be initialised from XML fragment with correct data types' do
|
|
@@ -14,6 +29,14 @@ context 'S3 object' do
|
|
|
14
29
|
@s3obj.etag.should.equal '24ce59274b89287b3960c184153ac24b'
|
|
15
30
|
@s3obj.size.should.equal 14
|
|
16
31
|
end
|
|
32
|
+
|
|
33
|
+
specify 'can be associated with bucket on creation' do
|
|
34
|
+
@s3obj2.bucket.name.should.equal 'quotes'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
specify 'can be created from a file' do
|
|
38
|
+
todo
|
|
39
|
+
end
|
|
17
40
|
|
|
18
41
|
specify 'should treat the owner as an object in his/her own right' do
|
|
19
42
|
[@s3obj.owner.user_id, @s3obj.owner.display_name].should.equal \
|
|
@@ -21,15 +44,31 @@ context 'S3 object' do
|
|
|
21
44
|
@s3obj.owner.should_be_instance_of S3ACL::CanonicalUser
|
|
22
45
|
end
|
|
23
46
|
|
|
24
|
-
specify 'can be
|
|
47
|
+
specify 'can be saved' do
|
|
25
48
|
todo
|
|
26
49
|
end
|
|
27
|
-
|
|
28
|
-
specify 'can be
|
|
50
|
+
|
|
51
|
+
specify 'can be renamed' do
|
|
29
52
|
todo
|
|
30
53
|
end
|
|
31
|
-
|
|
32
|
-
specify '
|
|
54
|
+
|
|
55
|
+
specify 'can be deleted' do
|
|
33
56
|
todo
|
|
34
57
|
end
|
|
58
|
+
|
|
59
|
+
specify 'should return authenticated URL' do
|
|
60
|
+
@s3obj2.url(:authenticated => true, :expires => 1141889120).should.equal @correct_authenticated_url
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
specify 'should return public URL' do
|
|
64
|
+
@s3obj2.url.should.equal @correct_public_url
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
specify 'should optionally return HTTPS URL' do
|
|
68
|
+
@s3obj2.url(:use_ssl => true).should.equal @correct_public_url_with_ssl
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
specify 'should optionally return URL with domain name set to bucket name' do
|
|
72
|
+
@s3obj2.url(:subdomain => true).should.equal @correct_public_url_with_subdomain
|
|
73
|
+
end
|
|
35
74
|
end
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
base = File.dirname(__FILE__)
|
|
2
|
+
require File.join(base, '../test_setup')
|
|
3
|
+
require 'time'
|
|
4
|
+
|
|
5
|
+
context 'S33r utility' do
|
|
6
|
+
|
|
7
|
+
setup do
|
|
8
|
+
@yaml_file = File.join(base, '../files/config.yaml')
|
|
9
|
+
|
|
10
|
+
@for_request_method = 'PUT'
|
|
11
|
+
@for_request_path = "/quotes/nelson"
|
|
12
|
+
@for_request_headers = {
|
|
13
|
+
"Content-Md5" => "c8fdb181845a4ca6b8fec737b3581d76",
|
|
14
|
+
"Content-Type" => "text/html",
|
|
15
|
+
"Date" => "Thu, 17 Nov 2005 18:49:58 GMT",
|
|
16
|
+
"X-Amz-Meta-Author" => "foo@bar.com",
|
|
17
|
+
"X-Amz-Magic" => "abracadabra"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# create broken request header hash
|
|
21
|
+
@for_incomplete_headers = @for_request_headers.clone.delete_if do |key,value|
|
|
22
|
+
'Content-Type' == key or 'Date' == key
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@correct_canonical_string = "PUT\nc8fdb181845a4ca6b8fec737b3581d76\n" +
|
|
26
|
+
"text/html\nThu, 17 Nov 2005 18:49:58 GMT\nx-amz-magic:abracadabra\n" +
|
|
27
|
+
"x-amz-meta-author:foo@bar.com\n/quotes/nelson"
|
|
28
|
+
@correct_signature = "jZNOcbfWmD/A/f3hSvVzXZjM2HU="
|
|
29
|
+
@correct_auth_header = "AWS #{Testing::ACCESS_KEY}:#{@correct_signature}"
|
|
30
|
+
|
|
31
|
+
@with_invalid_bucket_name = '/badbucket'
|
|
32
|
+
@with_invalid_bucket_name2 = 'badbucket/'
|
|
33
|
+
|
|
34
|
+
authenticated_url_end = "s3.amazonaws.com/quotes/nelson?Signature=vjbyPxybdZaNmGa%2ByT272YEAiv4%3D&"+
|
|
35
|
+
"AWSAccessKeyId=44CF9590006BF252F707&Expires=1141889120"
|
|
36
|
+
@correct_authenticated_url = "http://" + authenticated_url_end
|
|
37
|
+
@correct_authenticated_url_with_ssl = "https://" + authenticated_url_end
|
|
38
|
+
|
|
39
|
+
@correct_public_url = "http://s3.amazonaws.com/quotes/nelson"
|
|
40
|
+
@correct_public_ssl_url = "https://s3.amazonaws.com/quotes/nelson"
|
|
41
|
+
@correct_public_url_with_subdomain = "http://quotes.s3.amazonaws.com/nelson"
|
|
42
|
+
@correct_public_url_with_ssl_and_subdomain = "https://quotes.s3.amazonaws.com/nelson"
|
|
43
|
+
@correct_public_url_without_key = "http://s3.amazonaws.com/quotes/"
|
|
44
|
+
@correct_logging_url = "http://s3.amazonaws.com/quotes/?logging"
|
|
45
|
+
@correct_acl_url = "http://s3.amazonaws.com/quotes/?acl"
|
|
46
|
+
@correct_acl_url_with_key = "http://s3.amazonaws.com/quotes/nelson?acl"
|
|
47
|
+
@correct_public_url_with_qstring = "http://s3.amazonaws.com/quotes/?prefix=%2Fhome&max-keys=400"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
specify 'should load config files containing standard S33r settings' do
|
|
51
|
+
config, _ = S33r.load_config(@yaml_file)
|
|
52
|
+
config[:access].should.equal Testing::ACCESS_KEY
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
specify 'can parse ERb embedded in config. file' do
|
|
56
|
+
config, _ = S33r.load_config(@yaml_file)
|
|
57
|
+
config[:secret].should.equal Testing::SECRET_ACCESS_KEY
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
specify 'can parse extra application-specific settings in config. file' do
|
|
61
|
+
config, options = S33r.load_config(@yaml_file)
|
|
62
|
+
options[:email_to].should.equal Testing::EMAIL
|
|
63
|
+
# Make sure the 'options' section has not been included in the general config..
|
|
64
|
+
config.keys.should.not.include :options
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
specify 'should generate correct canonical strings' do
|
|
68
|
+
generate_canonical_string(@for_request_method, @for_request_path,
|
|
69
|
+
@for_request_headers).should.equal @correct_canonical_string
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
specify 'should generate correct signatures' do
|
|
73
|
+
generate_signature(Testing::SECRET_ACCESS_KEY,
|
|
74
|
+
@correct_canonical_string).should.equal @correct_signature
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
specify 'should generate correct auth headers' do
|
|
78
|
+
generate_auth_header_value(@for_request_method, @for_request_path, @for_request_headers,
|
|
79
|
+
Testing::ACCESS_KEY, Testing::SECRET_ACCESS_KEY).should.equal @correct_auth_header
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
specify 'should not generate auth header if bad HTTP method passed' do
|
|
83
|
+
lambda { generate_auth_header_value('duff', nil, nil, nil, nil) }.should.raise \
|
|
84
|
+
MethodNotAllowed
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
specify 'should not generate auth header if required headers missing' do
|
|
88
|
+
lambda { generate_auth_header_value('PUT', '/', @for_incomplete_headers,
|
|
89
|
+
nil, nil) }.should.raise MissingRequiredHeaders
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
specify 'when generating auth header, should allow addition of Date and Content-Type headers' do
|
|
93
|
+
now = Time.now
|
|
94
|
+
|
|
95
|
+
fixed_headers = default_headers(@for_incomplete_headers, :date => now,
|
|
96
|
+
:content_type => 'text/html')
|
|
97
|
+
|
|
98
|
+
fixed_headers['Date'].should.equal now.httpdate
|
|
99
|
+
fixed_headers['Content-Type'].should.equal 'text/html'
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
specify 'should generate correct x-amz-meta- headers from a hash' do
|
|
103
|
+
meta = {'myname' => 'elliot', 'myage' => 36}
|
|
104
|
+
headers = metadata_headers(meta)
|
|
105
|
+
headers.should.include 'x-amz-meta-myname'
|
|
106
|
+
headers.should.include 'x-amz-meta-myage'
|
|
107
|
+
headers['x-amz-meta-myname'].should.equal 'elliot'
|
|
108
|
+
headers['x-amz-meta-myage'].should.equal '36'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
specify 'should not generate canned ACL header if invalid canned ACL supplied' do
|
|
112
|
+
lambda { canned_acl_header('duff') }.should.raise \
|
|
113
|
+
UnsupportedCannedACL
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
specify 'should correctly add canned ACL headers' do
|
|
117
|
+
new_headers = canned_acl_header('private')
|
|
118
|
+
new_headers.should.have(1).keys
|
|
119
|
+
new_headers.keys.should.include 'x-amz-acl'
|
|
120
|
+
new_headers['x-amz-acl'].should.equal 'private'
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
specify 'should set sensible defaults for missing Content-Type and Date headers' do
|
|
124
|
+
fixed_headers = default_headers(@for_incomplete_headers)
|
|
125
|
+
fixed_headers['Content-Type'].should.equal ''
|
|
126
|
+
fixed_headers.include?('Date').should.not.be nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
specify 'should default to text/plain mimetype for unknown file types' do
|
|
130
|
+
guess_mime_type('hello.madeup').should.equal('text/plain')
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
specify 'should generate correct Content-Disposition, Content-Type and Content-Transfer-Encoding headers' do
|
|
134
|
+
headers = content_headers('text/plain', 'download.jpg', true)
|
|
135
|
+
headers['Content-Type'].should.equal 'text/plain'
|
|
136
|
+
headers['Content-Disposition'].should.equal "attachment; filename=download.jpg"
|
|
137
|
+
|
|
138
|
+
headers = content_headers('image/jpeg', '/home/you/me.jpg', true)
|
|
139
|
+
headers['Content-Type'].should.equal 'image/jpeg'
|
|
140
|
+
headers['Content-Transfer-Encoding'].should.equal 'binary'
|
|
141
|
+
headers['Content-Disposition'].should.equal "attachment; filename=me.jpg"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
specify 'should recognise invalid bucket names' do
|
|
145
|
+
lambda { bucket_name_valid?(@with_invalid_bucket_name) }.should.raise \
|
|
146
|
+
MalformedBucketName
|
|
147
|
+
lambda { bucket_name_valid?(@with_invalid_bucket_name2) }.should.raise \
|
|
148
|
+
MalformedBucketName
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
specify 'should return empty string if generating querystring with no key/value pairs' do
|
|
152
|
+
generate_querystring({}).should_equal ''
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
specify 'should correctly format querystring key/value pairs' do
|
|
156
|
+
generate_querystring({'message' => 'Hello world', 'id' => 1, 'page' => '[2,4]'}).should_equal \
|
|
157
|
+
'message=Hello+world&id=1&page=%5B2%2C4%5D'
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
specify 'should allow symbols as names for querystring variables when generating querystrings' do
|
|
161
|
+
generate_querystring({ :prefix => '/home/ell' }).should.equal('prefix=%2Fhome%2Fell')
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
specify 'should convert integers to strings when generating querystrings' do
|
|
165
|
+
generate_querystring({ 'max-keys' => 400 }).should.equal('max-keys=400')
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
specify 'should generate URLs with authentication parameters' do
|
|
169
|
+
s3_authenticated_url(Testing::ACCESS_KEY, Testing::SECRET_ACCESS_KEY, :bucket => 'quotes', \
|
|
170
|
+
:key => 'nelson', :expires => 1141889120).should.equal @correct_authenticated_url
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
specify 'should be able to pass credentials as an option to the basic URL generator' do
|
|
174
|
+
s3_url(:access => Testing::ACCESS_KEY, :secret => Testing::SECRET_ACCESS_KEY, \
|
|
175
|
+
:authenticated => true, :bucket => 'quotes', :key => 'nelson', \
|
|
176
|
+
:expires => 1141889120).should.equal @correct_authenticated_url
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
specify 'should generate correct public URLs' do
|
|
180
|
+
s3_public_url(:bucket => 'quotes', :key => 'nelson').should.equal @correct_public_url
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
specify 'should correctly append querystring variables' do
|
|
184
|
+
s3_url(:bucket => 'quotes', :querystring => {'max-keys' => 400, 'prefix' => '/home'}).should.equal \
|
|
185
|
+
@correct_public_url_with_qstring
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
specify 'should generate HTTPS URLs' do
|
|
189
|
+
s3_public_url(:bucket => 'quotes', :key => 'nelson', :use_ssl => true).should.equal @correct_public_ssl_url
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
specify 'should generate public URLs with bucket name as subdomain' do
|
|
193
|
+
s3_public_url(:bucket => 'quotes', :key => 'nelson', :subdomain => true).should.equal \
|
|
194
|
+
@correct_public_url_with_subdomain
|
|
195
|
+
s3_public_url(:bucket => 'quotes', :key => 'nelson', :subdomain => true, :use_ssl => true).should.equal \
|
|
196
|
+
@correct_public_url_with_ssl_and_subdomain
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
specify 'should generate URLs for buckets without keys' do
|
|
200
|
+
s3_public_url(:bucket => 'quotes').should.equal @correct_public_url_without_key
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
specify 'should generate logging URLs' do
|
|
204
|
+
s3_url(:bucket => 'quotes', :logging => true).should.equal @correct_logging_url
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
specify 'should generate ACL URLs' do
|
|
208
|
+
s3_url(:bucket => 'quotes', :acl => true).should.equal @correct_acl_url
|
|
209
|
+
s3_url(:bucket => 'quotes', :key => 'nelson', :acl => true).should.equal @correct_acl_url_with_key
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
specify 'should turn off subdomain if authenticated URL' do
|
|
213
|
+
s3_url(:access => Testing::ACCESS_KEY, :secret => Testing::SECRET_ACCESS_KEY, \
|
|
214
|
+
:authenticated => true, :bucket => 'quotes', :key => 'nelson', :subdomain => true, \
|
|
215
|
+
:expires => 1141889120).should.equal @correct_authenticated_url
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
specify 'should turn off subdomain if generating SSL URL' do
|
|
219
|
+
s3_url(:bucket => 'quotes', :key => 'nelson', :use_ssl => true, \
|
|
220
|
+
:subdomain => true).should.equal @correct_public_ssl_url
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
specify 'should produce correct default expiry time if none specified, or time 50 years \
|
|
224
|
+
from now for :far_flung_future' do
|
|
225
|
+
class Time
|
|
226
|
+
alias :old_to_i :to_i
|
|
227
|
+
# N.B. the number below is the seconds since the epoch of now, defined above
|
|
228
|
+
def to_i ; 1168349580 ; end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Default to DEFAULT_EXPIRY_SECS from now
|
|
232
|
+
expected_expiry = Time.now.to_i + DEFAULT_EXPIRY_SECS
|
|
233
|
+
expires = S33r.parse_expiry
|
|
234
|
+
expires.should.equal expected_expiry
|
|
235
|
+
expires.should_be_kind_of Integer
|
|
236
|
+
|
|
237
|
+
# If :far_flung_future is passed, default to 20 years from now.
|
|
238
|
+
expected_expiry = Time.now.to_i +
|
|
239
|
+
(60 * 60 * 24 * 365.25 * 20).to_i
|
|
240
|
+
expires = S33r.parse_expiry(:far_flung_future)
|
|
241
|
+
expires.should.equal expected_expiry
|
|
242
|
+
expires.should_be_kind_of Integer
|
|
243
|
+
|
|
244
|
+
class Time
|
|
245
|
+
remove_method(:to_i)
|
|
246
|
+
alias :to_i :old_to_i
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
specify 'should correctly set expiry from a datetime string' do
|
|
251
|
+
datetime_str = '9th January 2007 13:33'
|
|
252
|
+
expected_expiry = Time.parse(datetime_str).to_i
|
|
253
|
+
expires = S33r.parse_expiry(datetime_str)
|
|
254
|
+
expires.should.equal expected_expiry
|
|
255
|
+
expires.should_be_kind_of Integer
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
specify 'should provide a method for converting string keys of a hash into symbols' do
|
|
259
|
+
h = {'access' => Testing::ACCESS_KEY, 'secret' => Testing::SECRET_ACCESS_KEY}
|
|
260
|
+
symbolised = S33r.keys_to_symbols(h)
|
|
261
|
+
symbolised.should.equal({:access => Testing::ACCESS_KEY, :secret => Testing::SECRET_ACCESS_KEY})
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
end
|