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.
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