auth-hmac 1.0.1 → 1.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/History.txt CHANGED
@@ -1,3 +1,7 @@
1
+ == 1.1.0 2009-02-26
2
+
3
+ * Added support for custom request signatures and service ids. (ascarter)
4
+
1
5
  == 1.0.1 2008-08-22
2
6
 
3
7
  * Fixed nil pointer exception when with_auth called with nil creds.
data/config/hoe.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'auth-hmac/version'
2
2
 
3
- AUTHOR = 'Sean Geoghegan' # can also be an array of Authors
3
+ AUTHOR = ['Sean Geoghegan', 'ascarter'] # can also be an array of Authors
4
4
  EMAIL = "seangeo@gmail.com"
5
5
  DESCRIPTION = "A gem providing HMAC based authentication for HTTP"
6
6
  GEM_NAME = 'auth-hmac' # what ppl will type to install your gem
@@ -51,7 +51,8 @@ end
51
51
  # Generate all the Rake tasks
52
52
  # Run 'rake -T' to see list of generated tasks (from gem root directory)
53
53
  $hoe = Hoe.new(GEM_NAME, VERS) do |p|
54
- p.developer(AUTHOR, EMAIL)
54
+ p.author = AUTHOR
55
+ p.email = EMAIL
55
56
  p.description = DESCRIPTION
56
57
  p.summary = DESCRIPTION
57
58
  p.url = HOMEPATH
data/lib/auth-hmac.rb CHANGED
@@ -17,6 +17,12 @@ require 'base64'
17
17
  # As a result of the generalization, it won't work with AWS because it doesn't support
18
18
  # the Amazon extension headers.
19
19
  #
20
+ # === References
21
+ # Cryptographic Hash functions:: http://en.wikipedia.org/wiki/Cryptographic_hash_function
22
+ # SHA-1 Hash function:: http://en.wikipedia.org/wiki/SHA-1
23
+ # HMAC algorithm:: http://en.wikipedia.org/wiki/HMAC
24
+ # RFC 2104:: http://tools.ietf.org/html/rfc2104
25
+ #
20
26
  class AuthHMAC
21
27
  module Headers # :nodoc:
22
28
  # Gets the headers for a request.
@@ -42,69 +48,7 @@ class AuthHMAC
42
48
  end
43
49
 
44
50
  include Headers
45
-
46
- # Signs a request using a given access key id and secret.
47
- #
48
- def AuthHMAC.sign!(request, access_key_id, secret)
49
- self.new(access_key_id => secret).sign!(request, access_key_id)
50
- end
51
-
52
- def AuthHMAC.authenticated?(request, access_key_id, secret)
53
- self.new(access_key_id => secret).authenticated?(request)
54
- end
55
-
56
- # Create an AuthHMAC instance using a given credential store.
57
- #
58
- # A credential store must respond to the [] method and return
59
- # the secret for the access key id passed to [].
60
- #
61
- def initialize(credential_store)
62
- @credential_store = credential_store
63
- end
64
-
65
- # Signs a request using the access_key_id and the secret associated with that id
66
- # in the credential store.
67
- #
68
- # Signing a requests adds an Authorization header to the request in the format:
69
- #
70
- # AuthHMAC <access_key_id>:<signature>
71
- #
72
- # where <signature> is the Base64 encoded HMAC-SHA1 of the CanonicalString and the secret.
73
- #
74
- def sign!(request, access_key_id)
75
- secret = @credential_store[access_key_id]
76
- raise ArgumentError, "No secret found for key id '#{access_key_id}'" if secret.nil?
77
- request['Authorization'] = build_authorization_header(request, access_key_id, secret)
78
- end
79
-
80
- # Authenticates a request using HMAC
81
- #
82
- # Returns true if the request has an AuthHMAC Authorization header and
83
- # the access id and HMAC match an id and HMAC produced for the secret
84
- # in the credential store. Otherwise returns false.
85
- #
86
- def authenticated?(request)
87
- if md = /^AuthHMAC ([^:]+):(.+)$/.match(find_header(%w(Authorization HTTP_AUTHORIZATION), headers(request)))
88
- access_key_id = md[1]
89
- hmac = md[2]
90
- secret = @credential_store[access_key_id]
91
- !secret.nil? && hmac == build_signature(request, secret)
92
- else
93
- false
94
- end
95
- end
96
-
97
- private
98
- def build_authorization_header(request, access_key_id, secret)
99
- "AuthHMAC #{access_key_id}:#{build_signature(request, secret)}"
100
- end
101
-
102
- def build_signature(request, secret)
103
- canonical_string = CanonicalString.new(request)
104
- digest = OpenSSL::Digest::Digest.new('sha1')
105
- Base64.encode64(OpenSSL::HMAC.digest(digest, secret, canonical_string)).strip
106
- end
107
-
51
+
108
52
  # Build a Canonical String for a HTTP request.
109
53
  #
110
54
  # A Canonical String has the following format:
@@ -145,10 +89,10 @@ class AuthHMAC
145
89
  def header_values(headers)
146
90
  [ content_type(headers),
147
91
  content_md5(headers),
148
- (date(headers) or headers['Date'] = Time.now.getutc.httpdate)
92
+ (date(headers) or headers['Date'] = Time.now.utc.httpdate)
149
93
  ].join("\n")
150
94
  end
151
-
95
+
152
96
  def content_type(headers)
153
97
  find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE), headers)
154
98
  end
@@ -172,7 +116,129 @@ class AuthHMAC
172
116
  path[/^[^?]*/]
173
117
  end
174
118
  end
119
+
120
+ @@default_signature_class = CanonicalString
121
+
122
+ # Create an AuthHMAC instance using the given credential store
123
+ #
124
+ # Credential Store:
125
+ # * Credential store must respond to the [] method and return a secret for access key id
126
+ #
127
+ # Options:
128
+ # Override default options
129
+ # * <tt>:service_id</tt> - Service ID used in the AUTHORIZATION header string. Default is AuthHMAC.
130
+ # * <tt>:signature_method</tt> - Proc object that takes request and produces the signature string
131
+ # used for authentication. Default is CanonicalString.
132
+ # Examples:
133
+ # my_hmac = AuthHMAC.new('access_id1' => 'secret1', 'access_id2' => 'secret2')
134
+ #
135
+ # cred_store = { 'access_id1' => 'secret1', 'access_id2' => 'secret2' }
136
+ # options = { :service_id => 'MyApp', :signature_method => lambda { |r| MyRequestString.new(r) } }
137
+ # my_hmac = AuthHMAC.new(cred_store, options)
138
+ #
139
+ def initialize(credential_store, options = nil)
140
+ @credential_store = credential_store
141
+
142
+ # Defaults
143
+ @service_id = self.class.name
144
+ @signature_class = @@default_signature_class
145
+
146
+ unless options.nil?
147
+ @service_id = options[:service_id] if options.key?(:service_id)
148
+ @signature_class = options[:signature] if options.key?(:signature) && options[:signature].is_a?(Class)
149
+ end
175
150
 
151
+ @signature_method = lambda { |r| @signature_class.send(:new, r) }
152
+ end
153
+
154
+ # Generates canonical signing string for given request
155
+ #
156
+ # Supports same options as AuthHMAC.initialize for overriding service_id and
157
+ # signature method.
158
+ #
159
+ def AuthHMAC.canonical_string(request, options = nil)
160
+ self.new(nil, options).canonical_string(request)
161
+ end
162
+
163
+ # Generates signature string for a given secret
164
+ #
165
+ # Supports same options as AuthHMAC.initialize for overriding service_id and
166
+ # signature method.
167
+ #
168
+ def AuthHMAC.signature(request, secret, options = nil)
169
+ self.new(nil, options).signature(request, secret)
170
+ end
171
+
172
+ # Signs a request using a given access key id and secret.
173
+ #
174
+ # Supports same options as AuthHMAC.initialize for overriding service_id and
175
+ # signature method.
176
+ #
177
+ def AuthHMAC.sign!(request, access_key_id, secret, options = nil)
178
+ credentials = { access_key_id => secret }
179
+ self.new(credentials, options).sign!(request, access_key_id)
180
+ end
181
+
182
+ # Authenticates a request using HMAC
183
+ #
184
+ # Supports same options as AuthHMAC.initialize for overriding service_id and
185
+ # signature method.
186
+ #
187
+ def AuthHMAC.authenticated?(request, access_key_id, secret, options)
188
+ credentials = { access_key_id => secret }
189
+ self.new(credentials, options).authenticated?(request)
190
+ end
191
+
192
+ # Signs a request using the access_key_id and the secret associated with that id
193
+ # in the credential store.
194
+ #
195
+ # Signing a requests adds an Authorization header to the request in the format:
196
+ #
197
+ # <service_id> <access_key_id>:<signature>
198
+ #
199
+ # where <signature> is the Base64 encoded HMAC-SHA1 of the CanonicalString and the secret.
200
+ #
201
+ def sign!(request, access_key_id)
202
+ secret = @credential_store[access_key_id]
203
+ raise ArgumentError, "No secret found for key id '#{access_key_id}'" if secret.nil?
204
+ request['Authorization'] = authorization(request, access_key_id, secret)
205
+ end
206
+
207
+ # Authenticates a request using HMAC
208
+ #
209
+ # Returns true if the request has an AuthHMAC Authorization header and
210
+ # the access id and HMAC match an id and HMAC produced for the secret
211
+ # in the credential store. Otherwise returns false.
212
+ #
213
+ def authenticated?(request)
214
+ rx = Regexp.new("#{@service_id} ([^:]+):(.+)$")
215
+ if md = rx.match(authorization_header(request))
216
+ access_key_id = md[1]
217
+ hmac = md[2]
218
+ secret = @credential_store[access_key_id]
219
+ !secret.nil? && hmac == signature(request, secret)
220
+ else
221
+ false
222
+ end
223
+ end
224
+
225
+ def signature(request, secret)
226
+ digest = OpenSSL::Digest::Digest.new('sha1')
227
+ Base64.encode64(OpenSSL::HMAC.digest(digest, secret, canonical_string(request))).strip
228
+ end
229
+
230
+ def canonical_string(request)
231
+ @signature_method.call(request)
232
+ end
233
+
234
+ def authorization_header(request)
235
+ find_header(%w(Authorization HTTP_AUTHORIZATION), headers(request))
236
+ end
237
+
238
+ def authorization(request, access_key_id, secret)
239
+ "#{@service_id} #{access_key_id}:#{signature(request, secret)}"
240
+ end
241
+
176
242
  # Integration with Rails
177
243
  #
178
244
  class Rails # :nodoc:
@@ -185,21 +251,22 @@ class AuthHMAC
185
251
  # * +failure_message+: The text to use when authentication fails.
186
252
  # * +only+: A list off actions to protect.
187
253
  # * +except+: A list of actions to not protect.
254
+ # * +hmac+: Options for HMAC creation. See AuthHMAC#initialize for options.
188
255
  #
189
256
  def with_auth_hmac(credentials, options = {})
190
257
  unless credentials.nil?
191
258
  self.credentials = credentials
192
- self.authhmac = AuthHMAC.new(self.credentials)
193
259
  self.authhmac_failure_message = (options.delete(:failure_message) or "HMAC Authentication failed")
260
+ self.authhmac = AuthHMAC.new(self.credentials, options.delete(:hmac))
194
261
  before_filter(:hmac_login_required, options)
195
262
  else
196
- $stderr << "with_auth_hmac called with nil credentials - authentication will be skipped\n"
263
+ $stderr.puts("with_auth_hmac called with nil credentials - authentication will be skipped")
197
264
  end
198
265
  end
199
266
  end
200
267
 
201
268
  module InstanceMethods # :nodoc:
202
- def hmac_login_required
269
+ def hmac_login_required
203
270
  unless hmac_authenticated?
204
271
  response.headers['WWW-Authenticate'] = 'AuthHMAC'
205
272
  render :text => self.class.authhmac_failure_message, :status => :unauthorized
@@ -243,6 +310,7 @@ class AuthHMAC
243
310
  base.class_inheritable_accessor :hmac_access_id
244
311
  base.class_inheritable_accessor :hmac_secret
245
312
  base.class_inheritable_accessor :use_hmac
313
+ base.class_inheritable_accessor :hmac_options
246
314
  end
247
315
 
248
316
  module ClassMethods
@@ -268,7 +336,7 @@ class AuthHMAC
268
336
  # patch of the internals of ActiveResource it might not work with past or
269
337
  # future versions.
270
338
  #
271
- def with_auth_hmac(access_id, secret = nil)
339
+ def with_auth_hmac(access_id, secret = nil, options = nil)
272
340
  if access_id.is_a?(Hash)
273
341
  self.hmac_access_id = access_id.keys.first
274
342
  self.hmac_secret = access_id[self.hmac_access_id]
@@ -277,6 +345,7 @@ class AuthHMAC
277
345
  self.hmac_secret = secret
278
346
  end
279
347
  self.use_hmac = true
348
+ self.hmac_options = options
280
349
 
281
350
  class << self
282
351
  alias_method_chain :connection, :hmac
@@ -288,6 +357,7 @@ class AuthHMAC
288
357
  c.hmac_access_id = self.hmac_access_id
289
358
  c.hmac_secret = self.hmac_secret
290
359
  c.use_hmac = self.use_hmac
360
+ c.hmac_options = self.hmac_options
291
361
  c
292
362
  end
293
363
  end
@@ -300,7 +370,7 @@ class AuthHMAC
300
370
  def self.included(base)
301
371
  base.send :alias_method_chain, :request, :hmac
302
372
  base.class_eval do
303
- attr_accessor :hmac_secret, :hmac_access_id, :use_hmac
373
+ attr_accessor :hmac_secret, :hmac_access_id, :use_hmac, :hmac_options
304
374
  end
305
375
  end
306
376
 
@@ -308,7 +378,7 @@ class AuthHMAC
308
378
  if use_hmac && hmac_access_id && hmac_secret
309
379
  arguments.last['Date'] = Time.now.httpdate if arguments.last['Date'].nil?
310
380
  temp = "Net::HTTP::#{method.to_s.capitalize}".constantize.new(path, arguments.last)
311
- AuthHMAC.sign!(temp, hmac_access_id, hmac_secret)
381
+ AuthHMAC.sign!(temp, hmac_access_id, hmac_secret, hmac_options)
312
382
  arguments.last['Authorization'] = temp['Authorization']
313
383
  end
314
384
 
@@ -1,8 +1,8 @@
1
1
  class AuthHMAC
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 1
4
- MINOR = 0
5
- TINY = 1
4
+ MINOR = 1
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -10,73 +10,141 @@ require 'action_controller/test_process'
10
10
  require 'active_resource'
11
11
  require 'active_resource/http_mock'
12
12
 
13
+ # Class for doing a custom signature
14
+ class CustomSignature < String
15
+ def initialize(request)
16
+ self << "Custom signature string: #{request.method}"
17
+ end
18
+ end
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
+
13
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
+
14
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
+
15
53
  it "should sign using the key passed in as a parameter" do
16
- request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo",
17
- 'content-type' => 'text/plain',
18
- 'content-md5' => 'blahblah',
19
- 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
20
- AuthHMAC.sign!(request, "my-key-id", "secret")
21
- 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="
56
+ end
57
+
58
+ it "should sign using custom service id" do
59
+ AuthHMAC.sign!(@request, "my-key-id", "secret", { :service_id => 'MyService' })
60
+ @request['Authorization'].should == "MyService my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
61
+ end
62
+
63
+ it "should sign using custom signature method" do
64
+ options = {
65
+ :service_id => 'MyService',
66
+ :signature => CustomSignature
67
+ }
68
+ AuthHMAC.sign!(@request, "my-key-id", "secret", options)
69
+ @request['Authorization'].should == "MyService my-key-id:/L4N1v1BZSHfAYkQjsvZn696D9c="
22
70
  end
23
71
  end
24
72
 
25
73
  describe "#sign!" do
26
74
  before(:each) do
27
- @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')
28
81
  @store.stub!(:[]).and_return("")
29
82
  @authhmac = AuthHMAC.new(@store)
30
83
  end
31
-
32
- it "should add an Authorization header" do
33
- request = Net::HTTP::Get.new("/")
34
- @authhmac.sign!(request, 'key-id')
35
- request.key?("Authorization").should be_true
36
- end
37
-
38
- it "should fetch the secret from the store" do
39
- request = Net::HTTP::Get.new("/")
40
- @store.should_receive(:[]).with('key-id').and_return('secret')
41
- @authhmac.sign!(request, 'key-id')
42
- end
43
-
44
- it "should prefix the Authorization Header with AuthHMAC" do
45
- request = Net::HTTP::Get.new("/")
46
- @authhmac.sign!(request, 'key-id')
47
- request['Authorization'].should match(/^AuthHMAC /)
48
- end
49
-
50
- it "should include the key id as the first part of the Authorization header value" do
51
- request = Net::HTTP::Get.new("/")
52
- @authhmac.sign!(request, 'key-id')
53
- request['Authorization'].should match(/^AuthHMAC key-id:/)
54
- end
55
-
56
- it "should include the base64 encoded HMAC signature as the last part of the header value" do
57
- request = Net::HTTP::Get.new("/path")
58
- @authhmac.sign!(request, 'key-id')
59
- request['Authorization'].should match(/:[A-Za-z0-9+\/]{26,28}[=]{0,2}$/)
84
+
85
+ describe "default AuthHMAC with CanonicalString signature" do
86
+ it "should add an Authorization header" do
87
+ @authhmac.sign!(@get_request, 'key-id')
88
+ @get_request.key?("Authorization").should be_true
89
+ end
90
+
91
+ it "should fetch the secret from the store" do
92
+ @store.should_receive(:[]).with('key-id').and_return('secret')
93
+ @authhmac.sign!(@get_request, 'key-id')
94
+ end
95
+
96
+ it "should prefix the Authorization Header with AuthHMAC" do
97
+ @authhmac.sign!(@get_request, 'key-id')
98
+ @get_request['Authorization'].should match(/^AuthHMAC /)
99
+ end
100
+
101
+ it "should include the key id as the first part of the Authorization header value" do
102
+ @authhmac.sign!(@get_request, 'key-id')
103
+ @get_request['Authorization'].should match(/^AuthHMAC key-id:/)
104
+ end
105
+
106
+ it "should include the base64 encoded HMAC signature as the last part of the header value" do
107
+ @authhmac.sign!(@get_request, 'key-id')
108
+ @get_request['Authorization'].should match(/:[A-Za-z0-9+\/]{26,28}[=]{0,2}$/)
109
+ end
110
+
111
+ it "should create a complete signature" do
112
+ @store.should_receive(:[]).with('my-key-id').and_return('secret')
113
+ @authhmac.sign!(@put_request, "my-key-id")
114
+ @put_request['Authorization'].should == "AuthHMAC my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
115
+ end
60
116
  end
61
-
62
- it "should create a complete signature" do
63
- @store.should_receive(:[]).with('my-key-id').and_return('secret')
64
- request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo",
65
- 'content-type' => 'text/plain',
66
- 'content-md5' => 'blahblah',
67
- 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
68
- @authhmac.sign!(request, "my-key-id")
69
- request['Authorization'].should == "AuthHMAC my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
117
+
118
+ describe "custom signatures" do
119
+ before(:each) do
120
+ @options = {
121
+ :service_id => 'MyService',
122
+ :signature => CustomSignature
123
+ }
124
+ @authhmac = AuthHMAC.new(@store, @options)
125
+ end
126
+
127
+ it "should prefix the Authorization header with custom service id" do
128
+ @authhmac.sign!(@get_request, 'key-id')
129
+ @get_request['Authorization'].should match(/^MyService /)
130
+ end
131
+
132
+ it "should create a complete signature using options" do
133
+ @store.should_receive(:[]).with('my-key-id').and_return('secret')
134
+ @authhmac.sign!(@put_request, "my-key-id")
135
+ @put_request['Authorization'].should == "MyService my-key-id:/L4N1v1BZSHfAYkQjsvZn696D9c="
136
+ end
70
137
  end
71
138
  end
72
139
 
73
140
  describe "authenticated?" do
74
141
  before(:each) do
75
- @authhmac = AuthHMAC.new(YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml'))))
142
+ @credentials = YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml')))
143
+ @authhmac = AuthHMAC.new(@credentials)
76
144
  @request = Net::HTTP::Get.new("/path/to/get?foo=bar&bar=foo", 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
77
145
  end
78
146
 
79
- it "should return false when there is no Authoization Header" do
147
+ it "should return false when there is no Authorization Header" do
80
148
  @authhmac.authenticated?(@request).should be_false
81
149
  end
82
150
 
@@ -110,6 +178,31 @@ describe AuthHMAC do
110
178
  @authhmac.sign!(@request, 'access key 1')
111
179
  @authhmac.authenticated?(@request).should be_true
112
180
  end
181
+
182
+ describe "custom signatures" do
183
+ before(:each) do
184
+ @options = {
185
+ :service_id => 'MyService',
186
+ :signature => CustomSignature
187
+ }
188
+ end
189
+
190
+ it "should return false for invalid service id" do
191
+ @authhmac.sign!(@request, 'access key 1')
192
+ AuthHMAC.new(@credentials, @options.except(:signature)).authenticated?(@request).should be_false
193
+ end
194
+
195
+ it "should return false for request using default CanonicalString signature" do
196
+ @authhmac.sign!(@request, 'access key 1')
197
+ AuthHMAC.new(@credentials, @options.except(:service_id)).authenticated?(@request).should be_false
198
+ end
199
+
200
+ it "should return true when valid" do
201
+ @authhmac = AuthHMAC.new(@credentials, @options)
202
+ @authhmac.sign!(@request, 'access key 1')
203
+ @authhmac.authenticated?(@request).should be_true
204
+ end
205
+ end
113
206
  end
114
207
 
115
208
  describe "#sign! with YAML credentials" do
@@ -247,69 +340,129 @@ describe AuthHMAC do
247
340
  hmac_authenticated?
248
341
  end
249
342
  end
250
-
251
- it "should not raise an error when credentials are nil" do
252
- request = ActionController::TestRequest.new
253
- request.action = 'index'
254
- request.path = "/index"
255
- lambda do
256
- NilCredentialsController.new.process(request, ActionController::TestResponse.new).code.should == "200"
257
- end.should_not raise_error
258
- end
259
-
260
- it "should allow a request with the proper hmac" do
261
- request = ActionController::TestRequest.new
262
- request.env['Authorization'] = "AuthHMAC access key 1:6BVEVfAyIDoI3K+WallRMnDxROQ="
263
- request.env['DATE'] = "Thu, 10 Jul 2008 03:29:56 GMT"
264
- request.action = 'index'
265
- request.path = "/index"
266
- TestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
267
- end
268
-
269
- it "should reject a request with no hmac" do
270
- request = ActionController::TestRequest.new
271
- request.action = 'index'
272
- TestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
273
- end
274
-
275
- it "should reject a request with the wrong hmac" do
276
- request = ActionController::TestRequest.new
277
- request.action = 'index'
278
- request.env['Authorization'] = "AuthHMAC bogus:bogus"
279
- TestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
280
- end
281
-
282
- it "should include a WWW-Authenticate header with the schema AuthHMAC" do
283
- request = ActionController::TestRequest.new
284
- request.action = 'index'
285
- request.env['Authorization'] = "AuthHMAC bogus:bogus"
286
- TestController.new.process(request, ActionController::TestResponse.new).headers['WWW-Authenticate'].should == "AuthHMAC"
343
+
344
+ class CustomTestController < ActionController::Base
345
+ with_auth_hmac YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml'))),
346
+ :failure_message => "Stay away!",
347
+ :except => :public,
348
+ :hmac => { :service_id => 'MyService', :signature => CustomSignature }
349
+
350
+ def index
351
+ render :nothing => true, :status => :ok
352
+ end
353
+
354
+ def public
355
+ render :nothing => true, :status => :ok
356
+ end
357
+
358
+ def rescue_action(e) raise(e) end
287
359
  end
288
360
 
289
- it "should include a default error message" do
290
- request = ActionController::TestRequest.new
291
- request.action = 'index'
292
- request.env['Authorization'] = "AuthHMAC bogus:bogus"
293
- TestController.new.process(request, ActionController::TestResponse.new).body.should == "HMAC Authentication failed"
361
+ describe NilCredentialsController do
362
+ it "should not raise an error when credentials are nil" do
363
+ request = ActionController::TestRequest.new
364
+ request.action = 'index'
365
+ request.path = "/index"
366
+ lambda do
367
+ NilCredentialsController.new.process(request, ActionController::TestResponse.new).code.should == "200"
368
+ end.should_not raise_error
369
+ end
294
370
  end
295
-
296
- it "should reject a request with a given message" do
297
- request = ActionController::TestRequest.new
298
- request.action = 'index'
299
- request.env['Authorization'] = "AuthHMAC bogus:bogus"
300
- MessageTestController.new.process(request, ActionController::TestResponse.new).body.should == "Stay away!"
371
+
372
+ describe TestController do
373
+ it "should allow a request with the proper hmac" do
374
+ request = ActionController::TestRequest.new
375
+ request.env['Authorization'] = "AuthHMAC access key 1:6BVEVfAyIDoI3K+WallRMnDxROQ="
376
+ request.env['DATE'] = "Thu, 10 Jul 2008 03:29:56 GMT"
377
+ request.action = 'index'
378
+ request.path = "/index"
379
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
380
+ end
381
+
382
+ it "should reject a request with no hmac" do
383
+ request = ActionController::TestRequest.new
384
+ request.action = 'index'
385
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
386
+ end
387
+
388
+ it "should reject a request with the wrong hmac" do
389
+ request = ActionController::TestRequest.new
390
+ request.action = 'index'
391
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
392
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
393
+ end
394
+
395
+ it "should include a WWW-Authenticate header with the schema AuthHMAC" do
396
+ request = ActionController::TestRequest.new
397
+ request.action = 'index'
398
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
399
+ TestController.new.process(request, ActionController::TestResponse.new).headers['WWW-Authenticate'].should == "AuthHMAC"
400
+ end
401
+
402
+ it "should include a default error message" do
403
+ request = ActionController::TestRequest.new
404
+ request.action = 'index'
405
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
406
+ TestController.new.process(request, ActionController::TestResponse.new).body.should == "HMAC Authentication failed"
407
+ end
408
+
409
+ it "should allow anything to access the public action (using only)" do
410
+ request = ActionController::TestRequest.new
411
+ request.action = 'public'
412
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
413
+ end
301
414
  end
302
-
303
- it "should allow anything to access the public action (using only)" do
304
- request = ActionController::TestRequest.new
305
- request.action = 'public'
306
- TestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
415
+
416
+ describe MessageTestController do
417
+ it "should reject a request with a given message" do
418
+ request = ActionController::TestRequest.new
419
+ request.action = 'index'
420
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
421
+ MessageTestController.new.process(request, ActionController::TestResponse.new).body.should == "Stay away!"
422
+ end
423
+
424
+ it "should allow anything to access the public action (using except)" do
425
+ request = ActionController::TestRequest.new
426
+ request.action = 'public'
427
+ MessageTestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
428
+ end
307
429
  end
308
-
309
- it "should allow anything to access the public action (using except)" do
310
- request = ActionController::TestRequest.new
311
- request.action = 'public'
312
- MessageTestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
430
+
431
+ describe CustomTestController do
432
+ it "should allow a request with the proper hmac" do
433
+ request = ActionController::TestRequest.new
434
+ request.env['Authorization'] = "MyService access key 1:J2W4dOrv/sGsL0C5adnZYiQ3d70="
435
+ request.env['DATE'] = "Thu, 10 Jul 2008 03:29:56 GMT"
436
+ request.action = 'index'
437
+ request.path = "/index"
438
+ CustomTestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
439
+ end
440
+
441
+ it "should reject a request with no hmac" do
442
+ request = ActionController::TestRequest.new
443
+ request.action = 'index'
444
+ CustomTestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
445
+ end
446
+
447
+ it "should reject a request with the wrong hmac" do
448
+ request = ActionController::TestRequest.new
449
+ request.action = 'index'
450
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
451
+ CustomTestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
452
+ end
453
+
454
+ it "should reject a request with a given message" do
455
+ request = ActionController::TestRequest.new
456
+ request.action = 'index'
457
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
458
+ CustomTestController.new.process(request, ActionController::TestResponse.new).body.should == "Stay away!"
459
+ end
460
+
461
+ it "should allow anything to access the public action (using except)" do
462
+ request = ActionController::TestRequest.new
463
+ request.action = 'public'
464
+ CustomTestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
465
+ end
313
466
  end
314
467
  end
315
468
 
@@ -318,17 +471,44 @@ describe AuthHMAC do
318
471
  with_auth_hmac("access_id", "secret")
319
472
  self.site = "http://localhost/"
320
473
  end
321
-
322
- it "should send requests using HMAC authentication" do
323
- now = Time.parse("Thu, 10 Jul 2008 03:29:56 GMT")
324
- Time.should_receive(:now).at_least(1).and_return(now)
325
- ActiveResource::HttpMock.respond_to do |mock|
326
- mock.get "/test_resources/1.xml",
327
- {'Authorization' => 'AuthHMAC access_id:n8UlshMa8ve66U36XD3ZCbAIctg=', 'Content-Type' => 'application/xml', 'Date' => "Thu, 10 Jul 2008 03:29:56 GMT" },
328
- {:id => "1"}.to_xml(:root => 'test_resource')
474
+
475
+ class CustomTestResource < ActiveResource::Base
476
+ with_auth_hmac("access_id", "secret", { :service_id => 'MyService', :signature => CustomSignature })
477
+ self.site = "http://localhost/"
478
+ end
479
+
480
+ describe TestResource do
481
+ it "should send requests using HMAC authentication" do
482
+ now = Time.parse("Thu, 10 Jul 2008 03:29:56 GMT")
483
+ Time.should_receive(:now).at_least(1).and_return(now)
484
+ ActiveResource::HttpMock.respond_to do |mock|
485
+ mock.get "/test_resources/1.xml",
486
+ {
487
+ 'Authorization' => 'AuthHMAC access_id:44dvKATf4xanDtypqEA0EFYvOgI=',
488
+ 'Accept' => 'application/xml',
489
+ 'Date' => "Thu, 10 Jul 2008 03:29:56 GMT"
490
+ },
491
+ { :id => "1" }.to_xml(:root => 'test_resource')
492
+ end
493
+ TestResource.find(1)
494
+ end
495
+ end
496
+
497
+ describe CustomTestResource do
498
+ it "should send requests using HMAC authentication" do
499
+ now = Time.parse("Thu, 10 Jul 2008 03:29:56 GMT")
500
+ Time.should_receive(:now).at_least(1).and_return(now)
501
+ ActiveResource::HttpMock.respond_to do |mock|
502
+ mock.get "/custom_test_resources/1.xml",
503
+ {
504
+ 'Authorization' => 'MyService access_id:ZwCBL2rWLOMnwRrdF7wWEdJn7yA=',
505
+ 'Accept' => 'application/xml',
506
+ 'Date' => "Thu, 10 Jul 2008 03:29:56 GMT"
507
+ },
508
+ { :id => "1" }.to_xml(:root => 'custom_test_resource')
509
+ end
510
+ CustomTestResource.find(1)
329
511
  end
330
-
331
- TestResource.find(1)
332
512
  end
333
513
  end
334
514
  end
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: auth-hmac
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Geoghegan
8
+ - ascarter
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
12
 
12
- date: 2008-08-22 00:00:00 +09:30
13
+ date: 2009-02-26 00:00:00 +10:30
13
14
  default_executable:
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
@@ -20,11 +21,10 @@ dependencies:
20
21
  requirements:
21
22
  - - ">="
22
23
  - !ruby/object:Gem::Version
23
- version: 1.7.0
24
+ version: 1.8.2
24
25
  version:
25
26
  description: A gem providing HMAC based authentication for HTTP
26
- email:
27
- - seangeo@gmail.com
27
+ email: seangeo@gmail.com
28
28
  executables: []
29
29
 
30
30
  extensions: []
@@ -87,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
87
  requirements: []
88
88
 
89
89
  rubyforge_project: auth-hmac
90
- rubygems_version: 1.2.0
90
+ rubygems_version: 1.3.1
91
91
  signing_key:
92
92
  specification_version: 2
93
93
  summary: A gem providing HMAC based authentication for HTTP