s3lib 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/Rakefile +24 -0
  2. data/VERSION.yml +5 -0
  3. data/bin/s3lib +15 -0
  4. data/bin/s3sh_as +15 -0
  5. data/github-test.rb +22 -0
  6. data/lib/acl.rb +134 -0
  7. data/lib/acl_access.rb +20 -0
  8. data/lib/acl_creating_a_grant_recipe.rb +95 -0
  9. data/lib/acl_reading_acl_recipe.rb +59 -0
  10. data/lib/acl_refreshing_cached_grants_recipe.rb +54 -0
  11. data/lib/bucket.rb +116 -0
  12. data/lib/bucket_before_refactoring.rb +120 -0
  13. data/lib/bucket_create.rb +39 -0
  14. data/lib/bucket_find.rb +41 -0
  15. data/lib/bucket_with_acl_mixin.rb +103 -0
  16. data/lib/error_handling.rb +12 -0
  17. data/lib/grant.rb +107 -0
  18. data/lib/grant_creating_a_grant_recipe.rb +103 -0
  19. data/lib/grant_reading_acl_recipe.rb +51 -0
  20. data/lib/object.rb +144 -0
  21. data/lib/object_from_bucket_test.rb +18 -0
  22. data/lib/object_take1.rb +150 -0
  23. data/lib/object_with_acl_mixin.rb +131 -0
  24. data/lib/put_with_curl_test.rb +39 -0
  25. data/lib/s3_authenticator.rb +155 -0
  26. data/lib/s3_authenticator_dev.rb +117 -0
  27. data/lib/s3_authenticator_dev_private.rb +40 -0
  28. data/lib/s3_errors.rb +58 -0
  29. data/lib/s3lib.rb +10 -0
  30. data/lib/s3lib_with_mixin.rb +11 -0
  31. data/lib/service.rb +24 -0
  32. data/lib/service_dev.rb +36 -0
  33. data/s3lib.gemspec +74 -0
  34. data/sample_usage.rb +45 -0
  35. data/test/acl_test.rb +89 -0
  36. data/test/amazon_headers_test.rb +87 -0
  37. data/test/canonical_resource_test.rb +53 -0
  38. data/test/canonical_string_tests.rb +73 -0
  39. data/test/first_test.rb +34 -0
  40. data/test/first_test_private.rb +55 -0
  41. data/test/full_test.rb +84 -0
  42. data/test/s3_authenticator_test.rb +291 -0
  43. 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
@@ -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
@@ -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