Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added canonical_string and signature helpers to the AuthHMAC class as…

… useful tools for consumers of hte library. Added new specs. Added a simple program to generate signatures on the command line.
  • Loading branch information...
commit 401758285201a9f2a57384b534e2e1916cd67286 1 parent 6a23d94
authored January 09, 2009
38  bin/auth-hmac
... ...
@@ -0,0 +1,38 @@
  1
+#!/usr/bin/env ruby
  2
+APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
  3
+
  4
+require 'openssl'
  5
+require 'base64'
  6
+
  7
+def usage
  8
+  puts "Usage: auth-hmac COMMAND [ARGS]"
  9
+  puts "\nCommands:"
  10
+  puts "  signature [secret] [signature string]  Creates HMAC digtest using optional secret"
  11
+  exit
  12
+end
  13
+
  14
+def signature(args)
  15
+  secret = args[0]
  16
+  signature = args[1]
  17
+  digest = OpenSSL::Digest::Digest.new('sha1')
  18
+  puts Base64.encode64(OpenSSL::HMAC.digest(digest, secret, signature)).strip
  19
+end
  20
+
  21
+#
  22
+# MAIN
  23
+# 
  24
+
  25
+usage if ['--help', '-h'].include?(ARGV[0])
  26
+
  27
+command = ARGV.shift
  28
+args = []
  29
+ARGV.each { |arg| args << arg }
  30
+
  31
+usage if command.nil?
  32
+
  33
+case command
  34
+when "signature"
  35
+  signature(args)
  36
+end
  37
+
  38
+
25  lib/auth-hmac.rb
@@ -78,6 +78,24 @@ def initialize(credential_store, options = nil)
78 78
     parse_options(options)
79 79
   end
80 80
 
  81
+  # Generates canonical signing string for given request
  82
+  #
  83
+  # Supports same options as AuthHMAC.initialize for overriding service_id and
  84
+  # signature method.
  85
+  # 
  86
+  def AuthHMAC.canonical_string(request, options = nil)
  87
+    self.new(nil, options).canonical_string(request)
  88
+  end
  89
+
  90
+  # Generates signature string for a given secret
  91
+  #
  92
+  # Supports same options as AuthHMAC.initialize for overriding service_id and
  93
+  # signature method.
  94
+  # 
  95
+  def AuthHMAC.signature(request, secret, options = nil)
  96
+    self.new(nil, options).signature(request, secret)
  97
+  end
  98
+
81 99
   # Signs a request using a given access key id and secret.
82 100
   #
83 101
   # Supports same options as AuthHMAC.initialize for overriding service_id and
@@ -132,9 +150,12 @@ def authenticated?(request)
132 150
   end
133 151
 
134 152
   def signature(request, secret)
135  
-    canonical_string = @signature_method.call(request)
136 153
     digest = OpenSSL::Digest::Digest.new('sha1')
137  
-    Base64.encode64(OpenSSL::HMAC.digest(digest, secret, canonical_string)).strip
  154
+    Base64.encode64(OpenSSL::HMAC.digest(digest, secret, canonical_string(request))).strip
  155
+  end
  156
+
  157
+  def canonical_string(request)
  158
+    @signature_method.call(request)
138 159
   end
139 160
   
140 161
   private
108  spec/auth-hmac_spec.rb
@@ -17,92 +17,107 @@ def initialize(request)
17 17
   end
18 18
 end
19 19
 
  20
+def signature(value, secret)
  21
+  digest = OpenSSL::Digest::Digest.new('sha1')
  22
+  Base64.encode64(OpenSSL::HMAC.digest(digest, secret, value)).strip
  23
+end
  24
+
20 25
 describe AuthHMAC do
  26
+  before(:each) do
  27
+    @request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo", 
  28
+      'content-type' => 'text/plain', 
  29
+      'content-md5' => 'blahblah', 
  30
+      'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
  31
+  end
  32
+
  33
+  describe ".canonical_string" do
  34
+    it "should generate a canonical string using default method" do
  35
+      AuthHMAC.canonical_string(@request).should == "PUT\ntext/plain\nblahblah\nThu, 10 Jul 2008 03:29:56 GMT\n/path/to/put"
  36
+    end
  37
+  end
  38
+  
  39
+  describe ".signature" do
  40
+    it "should generate a valid signature string for a secret" do
  41
+      AuthHMAC.signature(@request, 'secret').should == "71wAJM4IIu/3o6lcqx/tw7XnAJs="
  42
+    end
  43
+  end
  44
+
21 45
   describe ".sign!" do
  46
+    before(:each) do
  47
+      @request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo", 
  48
+        'content-type' => 'text/plain', 
  49
+        'content-md5' => 'blahblah', 
  50
+        'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
  51
+    end
  52
+
22 53
     it "should sign using the key passed in as a parameter" do
23  
-      request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo", 
24  
-                                    'content-type' => 'text/plain', 
25  
-                                    'content-md5' => 'blahblah', 
26  
-                                    'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
27  
-      AuthHMAC.sign!(request, "my-key-id", "secret")
28  
-      request['Authorization'].should == "AuthHMAC my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
  54
+     AuthHMAC.sign!(@request, "my-key-id", "secret")
  55
+     @request['Authorization'].should == "AuthHMAC my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
29 56
     end
30 57
 
31 58
     it "should sign using custom service id" do
32  
-      request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo", 
33  
-                                    'content-type' => 'text/plain', 
34  
-                                    'content-md5' => 'blahblah', 
35  
-                                    'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
36  
-      AuthHMAC.sign!(request, "my-key-id", "secret", { :service_id => 'MyService' })
37  
-      request['Authorization'].should == "MyService my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
  59
+      AuthHMAC.sign!(@request, "my-key-id", "secret", { :service_id => 'MyService' })
  60
+      @request['Authorization'].should == "MyService my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
38 61
     end
39 62
 
40 63
     it "should sign using custom signature method" do
41  
-      request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo", 
42  
-                                    'content-type' => 'text/plain', 
43  
-                                    'content-md5' => 'blahblah', 
44  
-                                    'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
45 64
       options = {
46 65
         :service_id => 'MyService',
47 66
         :signature_method => lambda { |r| CustomSignature.new(r) }
48 67
       }
49  
-      AuthHMAC.sign!(request, "my-key-id", "secret", options)
50  
-      request['Authorization'].should == "MyService my-key-id:/L4N1v1BZSHfAYkQjsvZn696D9c="
  68
+      AuthHMAC.sign!(@request, "my-key-id", "secret", options)
  69
+      @request['Authorization'].should == "MyService my-key-id:/L4N1v1BZSHfAYkQjsvZn696D9c="
51 70
     end
52 71
   end
53 72
   
54 73
   describe "#sign!" do
55 74
     before(:each) do
56  
-      @store = mock('store')
  75
+      @get_request = Net::HTTP::Get.new("/")
  76
+      @put_request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo", 
  77
+        'content-type' => 'text/plain', 
  78
+        'content-md5' => 'blahblah', 
  79
+        'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
  80
+       @store = mock('store')
57 81
       @store.stub!(:[]).and_return("")
58 82
       @authhmac = AuthHMAC.new(@store)
59 83
     end
60 84
 
61 85
     describe "default AuthHMAC with CanonicalString signature" do
62 86
       it "should add an Authorization header" do
63  
-        request = Net::HTTP::Get.new("/")
64  
-        @authhmac.sign!(request, 'key-id')
65  
-        request.key?("Authorization").should be_true
  87
+        @authhmac.sign!(@get_request, 'key-id')
  88
+        @get_request.key?("Authorization").should be_true
66 89
       end
67 90
       
68 91
       it "should fetch the secret from the store" do
69  
-        request = Net::HTTP::Get.new("/")
70 92
         @store.should_receive(:[]).with('key-id').and_return('secret')
71  
-        @authhmac.sign!(request, 'key-id')
  93
+        @authhmac.sign!(@get_request, 'key-id')
72 94
       end
73 95
       
74 96
       it "should prefix the Authorization Header with AuthHMAC" do
75  
-        request = Net::HTTP::Get.new("/")
76  
-        @authhmac.sign!(request, 'key-id')
77  
-        request['Authorization'].should match(/^AuthHMAC /)
  97
+        @authhmac.sign!(@get_request, 'key-id')
  98
+        @get_request['Authorization'].should match(/^AuthHMAC /)
78 99
       end
79 100
 
80 101
       it "should include the key id as the first part of the Authorization header value" do
81  
-        request = Net::HTTP::Get.new("/")
82  
-        @authhmac.sign!(request, 'key-id')
83  
-        request['Authorization'].should match(/^AuthHMAC key-id:/)
  102
+        @authhmac.sign!(@get_request, 'key-id')
  103
+        @get_request['Authorization'].should match(/^AuthHMAC key-id:/)
84 104
       end
85 105
       
86 106
       it "should include the base64 encoded HMAC signature as the last part of the header value" do
87  
-        request = Net::HTTP::Get.new("/path")
88  
-        @authhmac.sign!(request, 'key-id')
89  
-        request['Authorization'].should match(/:[A-Za-z0-9+\/]{26,28}[=]{0,2}$/)
  107
+        @authhmac.sign!(@get_request, 'key-id')
  108
+        @get_request['Authorization'].should match(/:[A-Za-z0-9+\/]{26,28}[=]{0,2}$/)
90 109
       end
91 110
       
92 111
       it "should create a complete signature" do
93 112
         @store.should_receive(:[]).with('my-key-id').and_return('secret')
94  
-        request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo", 
95  
-                                      'content-type' => 'text/plain', 
96  
-                                      'content-md5' => 'blahblah', 
97  
-                                      'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
98  
-        @authhmac.sign!(request, "my-key-id")
99  
-        request['Authorization'].should == "AuthHMAC my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
  113
+        @authhmac.sign!(@put_request, "my-key-id")
  114
+        @put_request['Authorization'].should == "AuthHMAC my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
100 115
       end
101 116
     end
102 117
 
103 118
     describe "custom signatures" do
104 119
       before(:each) do
105  
-        @options = {
  120
+         @options = {
106 121
           :service_id => 'MyService',
107 122
           :signature_method => lambda { |r| CustomSignature.new(r) }
108 123
         }
@@ -110,19 +125,14 @@ def initialize(request)
110 125
       end
111 126
 
112 127
       it "should prefix the Authorization header with custom service id" do
113  
-        request = Net::HTTP::Get.new("/")
114  
-        @authhmac.sign!(request, 'key-id')
115  
-        request['Authorization'].should match(/^MyService /)
  128
+        @authhmac.sign!(@get_request, 'key-id')
  129
+        @get_request['Authorization'].should match(/^MyService /)
116 130
       end
117 131
       
118 132
       it "should create a complete signature using options" do
119 133
         @store.should_receive(:[]).with('my-key-id').and_return('secret')
120  
-        request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo", 
121  
-                                      'content-type' => 'text/plain', 
122  
-                                      'content-md5' => 'blahblah', 
123  
-                                      'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
124  
-        @authhmac.sign!(request, "my-key-id")
125  
-        request['Authorization'].should == "MyService my-key-id:/L4N1v1BZSHfAYkQjsvZn696D9c="
  134
+        @authhmac.sign!(@put_request, "my-key-id")
  135
+        @put_request['Authorization'].should == "MyService my-key-id:/L4N1v1BZSHfAYkQjsvZn696D9c="
126 136
       end
127 137
     end
128 138
   end

0 notes on commit 4017582

Please sign in to comment.
Something went wrong with that request. Please try again.