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