s3lib 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +24 -0
- data/VERSION.yml +5 -0
- data/bin/s3lib +15 -0
- data/bin/s3sh_as +15 -0
- data/github-test.rb +22 -0
- data/lib/acl.rb +134 -0
- data/lib/acl_access.rb +20 -0
- data/lib/acl_creating_a_grant_recipe.rb +95 -0
- data/lib/acl_reading_acl_recipe.rb +59 -0
- data/lib/acl_refreshing_cached_grants_recipe.rb +54 -0
- data/lib/bucket.rb +116 -0
- data/lib/bucket_before_refactoring.rb +120 -0
- data/lib/bucket_create.rb +39 -0
- data/lib/bucket_find.rb +41 -0
- data/lib/bucket_with_acl_mixin.rb +103 -0
- data/lib/error_handling.rb +12 -0
- data/lib/grant.rb +107 -0
- data/lib/grant_creating_a_grant_recipe.rb +103 -0
- data/lib/grant_reading_acl_recipe.rb +51 -0
- data/lib/object.rb +144 -0
- data/lib/object_from_bucket_test.rb +18 -0
- data/lib/object_take1.rb +150 -0
- data/lib/object_with_acl_mixin.rb +131 -0
- data/lib/put_with_curl_test.rb +39 -0
- data/lib/s3_authenticator.rb +155 -0
- data/lib/s3_authenticator_dev.rb +117 -0
- data/lib/s3_authenticator_dev_private.rb +40 -0
- data/lib/s3_errors.rb +58 -0
- data/lib/s3lib.rb +10 -0
- data/lib/s3lib_with_mixin.rb +11 -0
- data/lib/service.rb +24 -0
- data/lib/service_dev.rb +36 -0
- data/s3lib.gemspec +74 -0
- data/sample_usage.rb +45 -0
- data/test/acl_test.rb +89 -0
- data/test/amazon_headers_test.rb +87 -0
- data/test/canonical_resource_test.rb +53 -0
- data/test/canonical_string_tests.rb +73 -0
- data/test/first_test.rb +34 -0
- data/test/first_test_private.rb +55 -0
- data/test/full_test.rb +84 -0
- data/test/s3_authenticator_test.rb +291 -0
- metadata +109 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 's3_authenticator')
|
2
|
+
require File.join(File.dirname(__FILE__), 's3_errors')
|
3
|
+
require File.join(File.dirname(__FILE__), 'acl_access')
|
4
|
+
require File.join(File.dirname(__FILE__), 'object_with_acl_mixin')
|
5
|
+
require File.join(File.dirname(__FILE__), 'bucket_with_acl_mixin')
|
6
|
+
require File.join(File.dirname(__FILE__), 'acl')
|
7
|
+
require File.join(File.dirname(__FILE__), 'grant')
|
8
|
+
|
9
|
+
require 'rexml/document'
|
10
|
+
require 'rubygems'
|
11
|
+
require 'builder'
|
data/lib/service.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# service.rb
|
2
|
+
require File.join(File.dirname(__FILE__), 's3_authenticator')
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
module S3Lib
|
6
|
+
|
7
|
+
class Service
|
8
|
+
|
9
|
+
def self.buckets
|
10
|
+
response = S3Lib.request(:get, '')
|
11
|
+
doc = REXML::Document.new(response)
|
12
|
+
xml = doc.root
|
13
|
+
REXML::XPath.match(xml, '//Buckets/Bucket').collect do |bucket_xml|
|
14
|
+
Bucket.new(bucket_xml)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
if __FILE__ == $0
|
23
|
+
S3Lib::Service.buckets
|
24
|
+
end
|
data/lib/service_dev.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# service.rb
|
2
|
+
require File.join(File.dirname(__FILE__), 's3_authenticator')
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
module S3Lib
|
6
|
+
|
7
|
+
class Service
|
8
|
+
|
9
|
+
def self.buckets
|
10
|
+
response = S3Lib.request(:get, '')
|
11
|
+
xml = REXML::Document.new(response).root
|
12
|
+
|
13
|
+
REXML::XPath.match(xml, '//Buckets/Bucket').collect do |bucket_xml|
|
14
|
+
Bucket.new(bucket_xml)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
# This is a stub of the Bucket class that will be replaced with
|
21
|
+
# a full-blown class in the following recipes.
|
22
|
+
class Bucket
|
23
|
+
|
24
|
+
attr_reader :name
|
25
|
+
|
26
|
+
def initialize(doc)
|
27
|
+
@name = doc.elements['Name'].text
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
if __FILE__ == $0
|
35
|
+
S3Lib::Service.buckets
|
36
|
+
end
|
data/s3lib.gemspec
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{s3lib}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Scott Patten"]
|
12
|
+
s.date = %q{2011-11-11}
|
13
|
+
s.description = %q{This library forms the basis for building a library to talk to Amazon S3 using Ruby. It is used as an example of how to build an Amazon S3 interface in The S3 Cookbook (http://thes3cookbook.com)}
|
14
|
+
s.email = %q{scott@scottpatten.ca}
|
15
|
+
s.executables = ["s3lib", "s3sh_as"]
|
16
|
+
s.files = [
|
17
|
+
"Rakefile",
|
18
|
+
"VERSION.yml",
|
19
|
+
"bin/s3lib",
|
20
|
+
"bin/s3sh_as",
|
21
|
+
"github-test.rb",
|
22
|
+
"lib/acl.rb",
|
23
|
+
"lib/acl_access.rb",
|
24
|
+
"lib/acl_creating_a_grant_recipe.rb",
|
25
|
+
"lib/acl_reading_acl_recipe.rb",
|
26
|
+
"lib/acl_refreshing_cached_grants_recipe.rb",
|
27
|
+
"lib/bucket.rb",
|
28
|
+
"lib/bucket_before_refactoring.rb",
|
29
|
+
"lib/bucket_create.rb",
|
30
|
+
"lib/bucket_find.rb",
|
31
|
+
"lib/bucket_with_acl_mixin.rb",
|
32
|
+
"lib/error_handling.rb",
|
33
|
+
"lib/grant.rb",
|
34
|
+
"lib/grant_creating_a_grant_recipe.rb",
|
35
|
+
"lib/grant_reading_acl_recipe.rb",
|
36
|
+
"lib/object.rb",
|
37
|
+
"lib/object_from_bucket_test.rb",
|
38
|
+
"lib/object_take1.rb",
|
39
|
+
"lib/object_with_acl_mixin.rb",
|
40
|
+
"lib/put_with_curl_test.rb",
|
41
|
+
"lib/s3_authenticator.rb",
|
42
|
+
"lib/s3_authenticator_dev.rb",
|
43
|
+
"lib/s3_authenticator_dev_private.rb",
|
44
|
+
"lib/s3_errors.rb",
|
45
|
+
"lib/s3lib.rb",
|
46
|
+
"lib/s3lib_with_mixin.rb",
|
47
|
+
"lib/service.rb",
|
48
|
+
"lib/service_dev.rb",
|
49
|
+
"s3lib.gemspec",
|
50
|
+
"sample_usage.rb",
|
51
|
+
"test/acl_test.rb",
|
52
|
+
"test/amazon_headers_test.rb",
|
53
|
+
"test/canonical_resource_test.rb",
|
54
|
+
"test/canonical_string_tests.rb",
|
55
|
+
"test/first_test.rb",
|
56
|
+
"test/first_test_private.rb",
|
57
|
+
"test/full_test.rb",
|
58
|
+
"test/s3_authenticator_test.rb"
|
59
|
+
]
|
60
|
+
s.homepage = %q{http://thes3cookbook.com}
|
61
|
+
s.require_paths = ["lib"]
|
62
|
+
s.rubygems_version = %q{1.6.2}
|
63
|
+
s.summary = %q{An Amazon S3 interface library used as an example in The S3 Cookbook (http://thes3cookbook.com)}
|
64
|
+
|
65
|
+
if s.respond_to? :specification_version then
|
66
|
+
s.specification_version = 3
|
67
|
+
|
68
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
69
|
+
else
|
70
|
+
end
|
71
|
+
else
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
data/sample_usage.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# require 'rubygems'
|
4
|
+
# require 's3lib'
|
5
|
+
require File.join(File.dirname(__FILE__), 'lib/s3lib')
|
6
|
+
include S3Lib
|
7
|
+
|
8
|
+
bucket_name = 'spattens_first_bucket'
|
9
|
+
|
10
|
+
# Delete the bucket if it already exists. We want to start with an empty bucket.
|
11
|
+
Bucket.delete(bucket_name, :force => true) if Bucket.find(bucket_name)
|
12
|
+
|
13
|
+
# Create the bucket and store it in b
|
14
|
+
Bucket.create(bucket_name)
|
15
|
+
bucket = Bucket.find(bucket_name)
|
16
|
+
puts "Objects in bucket: #{bucket.objects.length}"
|
17
|
+
|
18
|
+
# Create some objects in the bucket
|
19
|
+
S3Object.create(bucket_name, 'first_object.txt', "this is the content")
|
20
|
+
S3Object.create(bucket_name, 'second_object.txt', "This is the second object")
|
21
|
+
|
22
|
+
# Create an object from a file
|
23
|
+
File.open('powers.txt', 'w') do |f|
|
24
|
+
10.times do |n|
|
25
|
+
f.puts "#{n},#{n**2},#{n**3},#{n**4}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
S3Object.create(bucket_name, 'powers.txt', File.read('powers.txt'))
|
29
|
+
|
30
|
+
# Look at the objects in the bucket, using refresh to make sure we see the new objects.
|
31
|
+
puts "Objects in buckets: #{bucket.objects(:refresh => true).length}"
|
32
|
+
first_obj = bucket.objects.first
|
33
|
+
puts "contents of the first object (#{first_obj.key}): #{first_obj.value}"
|
34
|
+
|
35
|
+
# Accessing objects by their name
|
36
|
+
puts "contents of the 'powers.txt' object:\n#{bucket['powers.txt'].value}"
|
37
|
+
|
38
|
+
# Show the permissions on 'powers.txt'
|
39
|
+
puts "Grants on 'powers.txt':\n#{bucket['powers.txt'].acl.inspect}"
|
40
|
+
|
41
|
+
# Grant world-read permission on 'powers.txt'
|
42
|
+
bucket['powers.txt'].acl.add_grant!(:read, :type => :public)
|
43
|
+
|
44
|
+
# Show the new permissions on 'powers.txt'
|
45
|
+
puts "Grants on 'powers.txt':\n#{bucket['powers.txt'].acl.inspect}"
|
data/test/acl_test.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 's3lib')
|
3
|
+
include S3Lib
|
4
|
+
|
5
|
+
|
6
|
+
class S3AclTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
SPATTEN_CANONICAL_ID = '9d92623ba6dd9d7cc06a7b8bcc46381e7c646f72d769214012f7e91b50c0de0f'
|
9
|
+
SPATTEN_DESIGN_CANONICAL_ID = '2f29caa19cd40477cf8a840b6dc473463cbda95b7dc81a8d72118a42733a7661'
|
10
|
+
SPATTEN_EMAIL = 'scott@spattendesign.com'
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@bucket = Bucket.find('spatten_test_bucket')
|
14
|
+
@object = @bucket['copy_test']
|
15
|
+
@bucket_acl = Acl.new('spatten_test_bucket')
|
16
|
+
end
|
17
|
+
|
18
|
+
def teardown
|
19
|
+
@bucket_acl.clear_grants
|
20
|
+
@bucket_acl.add_grant(:full_control, {:type => :canonical, :grantee => @bucket_acl.owner})
|
21
|
+
@bucket_acl.set_grants
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def test_acl_creation_with_bucket_object
|
26
|
+
assert_nothing_raised{@bucket_acl = Acl.new(@bucket)}
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_acl_can_instantiate_grants
|
30
|
+
assert_nothing_raised {@bucket_acl.grants.inspect}
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_add_grant_works
|
34
|
+
old_length = @bucket_acl.grants.length
|
35
|
+
assert_nothing_raised do
|
36
|
+
@bucket_acl.add_grant(:read, {:type => :all_s3})
|
37
|
+
assert_equal old_length + 1, @bucket_acl.grants.length
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_add_grant_by_canonical_id
|
42
|
+
old_length = @bucket_acl.grants.length
|
43
|
+
assert_nothing_raised do
|
44
|
+
@bucket_acl.add_grant(:read, {:type => :canonical, :grantee => SPATTEN_DESIGN_CANONICAL_ID})
|
45
|
+
assert_equal old_length + 1, @bucket_acl.grants.length
|
46
|
+
end
|
47
|
+
@bucket_acl.set_grants
|
48
|
+
assert_equal old_length + 1, @bucket_acl.grants.length
|
49
|
+
assert_equal :canonical, @bucket_acl.grants.last.type
|
50
|
+
assert_equal SPATTEN_DESIGN_CANONICAL_ID, @bucket_acl.grants.last.grantee
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_add_grant_by_email
|
54
|
+
old_length = @bucket_acl.grants.length
|
55
|
+
assert_nothing_raised do
|
56
|
+
@bucket_acl.add_grant(:read, {:type => :email, :grantee => SPATTEN_EMAIL})
|
57
|
+
assert_equal old_length + 1, @bucket_acl.grants.length
|
58
|
+
end
|
59
|
+
@bucket_acl.set_grants
|
60
|
+
assert_equal old_length + 1, @bucket_acl.grants.length
|
61
|
+
assert_equal :canonical, @bucket_acl.grants.last.type
|
62
|
+
assert_equal SPATTEN_DESIGN_CANONICAL_ID, @bucket_acl.grants.last.grantee
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_add_grant_to_all_s3
|
66
|
+
old_length = @bucket_acl.grants.length
|
67
|
+
assert_nothing_raised do
|
68
|
+
@bucket_acl.add_grant(:read, {:type => :all_s3})
|
69
|
+
assert_equal old_length + 1, @bucket_acl.grants.length
|
70
|
+
end
|
71
|
+
@bucket_acl.set_grants
|
72
|
+
assert_equal old_length + 1, @bucket_acl.grants.length
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_to_xml_includes_grant_xml
|
76
|
+
assert_match "<Grant>", @bucket_acl.to_xml
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_set_grants_works
|
80
|
+
old_length = @bucket_acl.grants.length
|
81
|
+
@bucket_acl.add_grant(:read, {:type => :all_s3})
|
82
|
+
assert_nothing_raised {@bucket_acl.set_grants}
|
83
|
+
assert_equal old_length + 1, @bucket_acl.grants.length
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_owner
|
87
|
+
assert_equal SPATTEN_CANONICAL_ID, @bucket_acl.owner
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.join(File.dirname(__FILE__), '../lib/s3_authenticator')
|
3
|
+
|
4
|
+
class S3AuthenticatorAmazonHeadersTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
S3Lib::AuthenticatedRequest.test_mode
|
8
|
+
@s3_test = S3Lib::AuthenticatedRequest.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_amazon_headers_should_remove_non_amazon_headers
|
12
|
+
@s3_test.make_authenticated_request(:get, '', {'content-type' => 'content',
|
13
|
+
'some-other-header' => 'other',
|
14
|
+
'x-amz-meta-one' => 'one',
|
15
|
+
'x-amz-meta-two' => 'two'})
|
16
|
+
headers = @s3_test.canonicalized_amazon_headers
|
17
|
+
assert_no_match /other/, headers
|
18
|
+
assert_no_match /content/, headers
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_amazon_headers_should_keep_amazon_headers
|
22
|
+
@s3_test.make_authenticated_request(:get, '', {'content-type' => 'content',
|
23
|
+
'some-other-header' => 'other',
|
24
|
+
'x-amz-meta-one' => 'one',
|
25
|
+
'x-amz-meta-two' => 'two'})
|
26
|
+
headers = @s3_test.canonicalized_amazon_headers
|
27
|
+
assert_match /x-amz-meta-one/, headers
|
28
|
+
assert_match /x-amz-meta-two/, headers
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_amazon_headers_should_be_lowercase
|
32
|
+
@s3_test.make_authenticated_request(:get, '', {'content-type' => 'content',
|
33
|
+
'some-other-header' => 'other',
|
34
|
+
'X-amz-meta-one' => 'one',
|
35
|
+
'x-Amz-meta-two' => 'two'})
|
36
|
+
headers = @s3_test.canonicalized_amazon_headers
|
37
|
+
assert_match /x-amz-meta-one/, headers
|
38
|
+
assert_match /x-amz-meta-two/, headers
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_amazon_headers_should_be_alphabetized
|
42
|
+
@s3_test.make_authenticated_request(:get, '', {'content-type' => 'content',
|
43
|
+
'some-other-header' => 'other',
|
44
|
+
'X-amz-meta-one' => 'one',
|
45
|
+
'x-Amz-meta-two' => 'two',
|
46
|
+
'x-amz-meta-zed' => 'zed',
|
47
|
+
'x-amz-meta-alpha' => 'alpha'})
|
48
|
+
headers = @s3_test.canonicalized_amazon_headers
|
49
|
+
assert_match /alpha.*one.*two.*zed/m, headers # /m on the reg-exp makes .* include newlines
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_xamzdate_should_override_date_header
|
53
|
+
@s3_test.make_authenticated_request(:get, '', {'date' => 'December 15, 2005', 'x-amz-date' => 'Tue, 27 Mar 2007 21:20:26 +0000'})
|
54
|
+
headers = @s3_test.canonicalized_headers
|
55
|
+
assert_match /2007/, headers
|
56
|
+
assert_no_match /2005/, headers
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_xamzdate_should_override_capitalized_date_header
|
60
|
+
@s3_test.make_authenticated_request(:get, '', {'date' => 'December 15, 2005', 'X-amz-date' => 'Tue, 27 Mar 2007 21:20:26 +0000'})
|
61
|
+
headers = @s3_test.canonicalized_headers
|
62
|
+
assert_match /2007/, headers
|
63
|
+
assert_no_match /2005/, headers
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_leading_spaces_get_stripped_from_header_values
|
67
|
+
@s3_test.make_authenticated_request(:get, '', {'x-amz-meta-one' => ' one with a leading space',
|
68
|
+
'x-Amz-meta-two' => ' two with a leading and trailing space '})
|
69
|
+
headers = @s3_test.canonicalized_amazon_headers
|
70
|
+
assert_match /x-amz-meta-one:one with a leading space/, headers
|
71
|
+
assert_match /x-amz-meta-two:two with a leading and trailing space /, headers
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_long_amazon_headers_should_get_unfolded
|
75
|
+
@s3_test.make_authenticated_request(:get, '', {'x-amz-meta-one' => "A really long header\nwith multiple lines\nshould be unfolded."})
|
76
|
+
headers = @s3_test.canonicalized_amazon_headers
|
77
|
+
assert_match /x-amz-meta-one:A really long header with multiple lines should be unfolded./, headers
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_values_as_arrays_should_be_joined_as_commas
|
81
|
+
@s3_test.make_authenticated_request(:get, '', {'x-amz-mult' => ['a', 'b', 'c']})
|
82
|
+
|
83
|
+
headers = @s3_test.canonicalized_amazon_headers
|
84
|
+
assert_match /a,b,c/, headers
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.join(File.dirname(__FILE__), '../lib/s3_authenticator')
|
3
|
+
|
4
|
+
class S3AuthenticatorCanonicalResourceTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
S3Lib::AuthenticatedRequest.test_mode
|
8
|
+
@s3_test = S3Lib::AuthenticatedRequest.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_forward_slash_is_always_added
|
12
|
+
@s3_test.make_authenticated_request(:get, '')
|
13
|
+
assert_match /^\//, @s3_test.canonicalized_resource
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_bucket_name_in_uri_should_get_passed_through
|
17
|
+
@s3_test.make_authenticated_request(:get, 'my_bucket')
|
18
|
+
assert_match /^\/my_bucket/, @s3_test.canonicalized_resource
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_canonicalized_resource_should_include_uri
|
22
|
+
@s3_test.make_authenticated_request(:get, 'my_bucket/vampire.jpg')
|
23
|
+
assert_match /vampire.jpg$/, @s3_test.canonicalized_resource
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_canonicalized_resource_should_include_sub_resource
|
27
|
+
@s3_test.make_authenticated_request(:get, 'my_bucket/vampire.jpg?torrent')
|
28
|
+
assert_match /vampire.jpg\?torrent$/, @s3_test.canonicalized_resource
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_bucket_name_with_virtual_hosting
|
32
|
+
@s3_test.make_authenticated_request(:get, '/', {'host' => 'some_bucket.s3.amazonaws.com'})
|
33
|
+
assert_match /some_bucket\//, @s3_test.canonicalized_resource
|
34
|
+
assert_no_match /s3.amazonaws.com/, @s3_test.canonicalized_resource
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_bucket_name_with_cname_virtual_hosting
|
38
|
+
@s3_test.make_authenticated_request(:get, '/', {'host' => 'some_bucket.example.com'})
|
39
|
+
assert_match /^\/some_bucket.example.com/, @s3_test.canonicalized_resource
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_bucket_name_is_lowercase_with_virtual_hosting
|
43
|
+
@s3_test.make_authenticated_request(:get, '/', {'host' => 'Some_Bucket.s3.amazonaws.com'})
|
44
|
+
assert_match /some_bucket/, @s3_test.canonicalized_resource
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_bucket_name_is_lowercase_with_cname_virtual_hosting
|
48
|
+
@s3_test.make_authenticated_request(:get, '/', {'host' => 'Some_Bucket.example.com'})
|
49
|
+
assert_match /some_bucket.example.com/, @s3_test.canonicalized_resource
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.join(File.dirname(__FILE__), '../lib/s3_authenticator')
|
3
|
+
|
4
|
+
class S3AuthenticatorTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
S3Lib::AuthenticatedRequest.test_mode
|
8
|
+
@s3_test = S3Lib::AuthenticatedRequest.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# See http://developer.amazonwebservices.com/connect/entry.jspa?externalID=123&categoryID=48
|
12
|
+
def test_dg_sample_one
|
13
|
+
@s3_test.make_authenticated_request(:get, '/photos/puppy.jpg', {'Host' => 'johnsmith.s3.amazonaws.com',
|
14
|
+
'Date' => 'Tue, 27 Mar 2007 19:36:42 +0000'})
|
15
|
+
expected_canonical_string = "GET\n\n\nTue, 27 Mar 2007 19:36:42 +0000\n/johnsmith/photos/puppy.jpg"
|
16
|
+
assert_equal expected_canonical_string, @s3_test.canonical_string
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_dg_sample_two
|
20
|
+
@s3_test.make_authenticated_request(:put, '/photos/puppy.jpg', {'Content-Type' => 'image/jpeg',
|
21
|
+
'Content-Length' => '94328',
|
22
|
+
'Host' => 'johnsmith.s3.amazonaws.com',
|
23
|
+
'Date' => 'Tue, 27 Mar 2007 21:15:45 +0000'})
|
24
|
+
expected_canonical_string = "PUT\n\nimage/jpeg\nTue, 27 Mar 2007 21:15:45 +0000\n/johnsmith/photos/puppy.jpg"
|
25
|
+
assert_equal expected_canonical_string, @s3_test.canonical_string
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_dg_sample_three
|
29
|
+
@s3_test.make_authenticated_request(:get, '', {'prefix' => 'photos',
|
30
|
+
'max-keys' => '50',
|
31
|
+
'marker' => 'puppy',
|
32
|
+
'host' => 'johnsmith.s3.amazonaws.com',
|
33
|
+
'date' => 'Tue, 27 Mar 2007 19:42:41 +0000'})
|
34
|
+
assert_equal "GET\n\n\nTue, 27 Mar 2007 19:42:41 +0000\n/johnsmith/", @s3_test.canonical_string
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_dg_sample_four
|
38
|
+
@s3_test.make_authenticated_request(:get, '?acl', {'host' => 'johnsmith.s3.amazonaws.com',
|
39
|
+
'date' => 'Tue, 27 Mar 2007 19:44:46 +0000'})
|
40
|
+
|
41
|
+
assert_equal "GET\n\n\nTue, 27 Mar 2007 19:44:46 +0000\n/johnsmith/?acl", @s3_test.canonical_string
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_dg_sample_five
|
45
|
+
@s3_test.make_authenticated_request(:delete, '/johnsmith/photos/puppy.jpg',
|
46
|
+
{'User-Agent' => 'dotnet',
|
47
|
+
'host' => 's3.amazonaws.com',
|
48
|
+
'date' => 'Tue, 27 Mar 2007 21:20:27 +0000',
|
49
|
+
'x-amz-date' => 'Tue, 27 Mar 2007 21:20:26 +0000' })
|
50
|
+
assert_equal "DELETE\n\n\n\nx-amz-date:Tue, 27 Mar 2007 21:20:26 +0000\n/johnsmith/photos/puppy.jpg", @s3_test.canonical_string
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_dg_sample_six
|
54
|
+
@s3_test.make_authenticated_request(:put, '/db-backup.dat.gz',
|
55
|
+
{'User-Agent' => 'curl/7.15.5',
|
56
|
+
'host' => 'static.johnsmith.net:8080',
|
57
|
+
'date' => 'Tue, 27 Mar 2007 21:06:08 +0000',
|
58
|
+
'x-amz-acl' => 'public-read',
|
59
|
+
'content-type' => 'application/x-download',
|
60
|
+
'Content-MD5' => '4gJE4saaMU4BqNR0kLY+lw==',
|
61
|
+
'X-Amz-Meta-ReviewedBy' => ['joe@johnsmith.net', 'jane@johnsmith.net'],
|
62
|
+
'X-Amz-Meta-FileChecksum' => '0x02661779',
|
63
|
+
'X-Amz-Meta-ChecksumAlgorithm' => 'crc32',
|
64
|
+
'Content-Disposition' => 'attachment; filename=database.dat',
|
65
|
+
'Content-Encoding' => 'gzip',
|
66
|
+
'Content-Length' => '5913339' })
|
67
|
+
expected_canonical_string = "PUT\n4gJE4saaMU4BqNR0kLY+lw==\napplication/x-download\nTue, 27 Mar 2007 21:06:08 +0000\n" +
|
68
|
+
"x-amz-acl:public-read\nx-amz-meta-checksumalgorithm:crc32\nx-amz-meta-filechecksum:0x02661779\n" +
|
69
|
+
"x-amz-meta-reviewedby:joe@johnsmith.net,jane@johnsmith.net\n/static.johnsmith.net/db-backup.dat.gz"
|
70
|
+
assert_equal expected_canonical_string, @s3_test.canonical_string
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/test/first_test.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.join(File.dirname(__FILE__), '../lib/s3_authenticator')
|
3
|
+
|
4
|
+
class S3AuthenticatorTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@s3_test = S3Lib::AuthenticatedRequest.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_http_verb_is_uppercase
|
11
|
+
@s3_test.make_authenticated_request(:get, '/', {'host' => 's3.amazonaws.com'})
|
12
|
+
assert_match /^GET\n/, @s3_test.canonical_string
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_canonical_string_contains_positional_headers
|
16
|
+
@s3_test.make_authenticated_request(:get, '', {'content-type' => 'some crazy content type',
|
17
|
+
'date' => 'December 25th, 2007',
|
18
|
+
'content-md5' => 'whee'})
|
19
|
+
assert_match /^GET\n#{@s3_test.canonicalized_positional_headers}/, @s3_test.canonical_string
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_positional_headers_with_all_headers
|
23
|
+
@s3_test.make_authenticated_request(:get, '', {'content-type' => 'some crazy content type',
|
24
|
+
'date' => 'December 25th, 2007',
|
25
|
+
'content-md5' => 'whee'})
|
26
|
+
assert_equal "whee\nsome crazy content type\nDecember 25th, 2007\n", @s3_test.canonicalized_positional_headers
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_positional_headers_with_only_date_header
|
30
|
+
@s3_test.make_authenticated_request(:get, '', {'date' => 'December 25th, 2007'})
|
31
|
+
assert_equal "\n\nDecember 25th, 2007\n", @s3_test.canonicalized_positional_headers
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Make private methods and attributes public so that you can test them
|
2
|
+
module S3Lib
|
3
|
+
class AuthenticatedRequest
|
4
|
+
|
5
|
+
attr_reader :headers
|
6
|
+
|
7
|
+
def public_canonicalized_headers
|
8
|
+
canonicalized_headers
|
9
|
+
end
|
10
|
+
|
11
|
+
def public_canonicalized_positional_headers
|
12
|
+
canonicalized_positional_headers
|
13
|
+
end
|
14
|
+
|
15
|
+
def public_canonical_string
|
16
|
+
canonical_string
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'test/unit'
|
23
|
+
require File.join(File.dirname(__FILE__), '../lib/s3_authenticator')
|
24
|
+
|
25
|
+
class S3AuthenticatorTest < Test::Unit::TestCase
|
26
|
+
|
27
|
+
def setup
|
28
|
+
@s3_test = S3Lib::AuthenticatedRequest.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_http_verb_is_uppercase
|
32
|
+
@s3_test.make_authenticated_request(:get, '/', {'host' => 's3.amazonaws.com'})
|
33
|
+
assert_match /^GET\n/, @s3_test.public_canonical_string
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_canonical_string_contains_positional_headers
|
37
|
+
@s3_test.make_authenticated_request(:get, '', {'content-type' => 'some crazy content type',
|
38
|
+
'date' => 'December 25th, 2007',
|
39
|
+
'content-md5' => 'whee'})
|
40
|
+
assert_match /^GET\n#{@s3_test.public_canonicalized_positional_headers}/, @s3_test.public_canonical_string
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_positional_headers_with_all_headers
|
44
|
+
@s3_test.make_authenticated_request(:get, '', {'content-type' => 'some crazy content type',
|
45
|
+
'date' => 'December 25th, 2007',
|
46
|
+
'content-md5' => 'whee'})
|
47
|
+
assert_equal "whee\nsome crazy content type\nDecember 25th, 2007\n", @s3_test.public_canonicalized_positional_headers
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_positional_headers_with_only_date_header
|
51
|
+
@s3_test.make_authenticated_request(:get, '', {'date' => 'December 25th, 2007'})
|
52
|
+
assert_equal "\n\nDecember 25th, 2007\n", @s3_test.public_canonicalized_positional_headers
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|