dnclabs-auth-hmac 1.1.1.2010061601

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,314 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require "net/http"
3
+ require 'time'
4
+ require 'yaml'
5
+ require 'rubygems'
6
+ gem 'actionpack'
7
+ gem 'activeresource'
8
+ require 'action_controller'
9
+ require 'action_controller/test_process'
10
+ require 'active_resource'
11
+ require 'active_resource/http_mock'
12
+ require 'ruby-debug'
13
+
14
+ # Class for doing a custom signature
15
+ class CustomSignature < String
16
+ def initialize(request)
17
+ self << "Custom signature string: #{request.method}"
18
+ end
19
+ end
20
+
21
+ def signature(value, secret)
22
+ digest = OpenSSL::Digest::Digest.new('sha1')
23
+ Base64.encode64(OpenSSL::HMAC.digest(digest, secret, value)).strip
24
+ end
25
+
26
+ describe AuthHMAC do
27
+ before(:each) do
28
+ @request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo",
29
+ 'content-type' => 'text/plain',
30
+ 'content-md5' => 'blahblah',
31
+ 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
32
+ end
33
+
34
+ describe ".canonical_string" do
35
+ it "should generate a canonical string using default method" do
36
+ # debugger
37
+ AuthHMAC.canonical_string(@request).should == "PUT\ntext/plain\nblahblah\nThu, 10 Jul 2008 03:29:56 GMT\n/path/to/put"
38
+ end
39
+ end
40
+
41
+ describe ".signature" do
42
+ it "should generate a valid signature string for a secret" do
43
+ AuthHMAC.signature(@request, 'secret').should == "71wAJM4IIu/3o6lcqx/tw7XnAJs="
44
+ end
45
+ end
46
+
47
+ describe ".sign!" do
48
+ before(:each) do
49
+ @request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo",
50
+ 'content-type' => 'text/plain',
51
+ 'content-md5' => 'blahblah',
52
+ 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
53
+ end
54
+
55
+ it "should sign using the key passed in as a parameter" do
56
+ AuthHMAC.sign!(@request, "my-key-id", "secret")
57
+ @request['Authorization'].should == "AuthHMAC my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
58
+ end
59
+
60
+ it "should sign using custom service id" do
61
+ AuthHMAC.sign!(@request, "my-key-id", "secret", { :service_id => 'MyService' })
62
+ @request['Authorization'].should == "MyService my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
63
+ end
64
+
65
+ it "should sign using custom signature method" do
66
+ options = {
67
+ :service_id => 'MyService',
68
+ :signature => CustomSignature
69
+ }
70
+ # debugger
71
+ AuthHMAC.sign!(@request, "my-key-id", "secret", options)
72
+ @request['Authorization'].should == "MyService my-key-id:/L4N1v1BZSHfAYkQjsvZn696D9c="
73
+ end
74
+ end
75
+
76
+ describe "#sign!" do
77
+ before(:each) do
78
+ @get_request = Net::HTTP::Get.new("/")
79
+ @put_request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo",
80
+ 'content-type' => 'text/plain',
81
+ 'content-md5' => 'blahblah',
82
+ 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
83
+ @store = mock('store')
84
+ @store.stub!(:[]).and_return("")
85
+ @authhmac = AuthHMAC.new(@store)
86
+ end
87
+
88
+ describe "default AuthHMAC with CanonicalString signature" do
89
+ it "should add an Authorization header" do
90
+ @authhmac.sign!(@get_request, 'key-id')
91
+ @get_request.key?("Authorization").should be_true
92
+ end
93
+
94
+ it "should fetch the secret from the store" do
95
+ @store.should_receive(:[]).with('key-id').and_return('secret')
96
+ @authhmac.sign!(@get_request, 'key-id')
97
+ end
98
+
99
+ it "should prefix the Authorization Header with AuthHMAC" do
100
+ @authhmac.sign!(@get_request, 'key-id')
101
+ @get_request['Authorization'].should match(/^AuthHMAC /)
102
+ end
103
+
104
+ it "should include the key id as the first part of the Authorization header value" do
105
+ @authhmac.sign!(@get_request, 'key-id')
106
+ @get_request['Authorization'].should match(/^AuthHMAC key-id:/)
107
+ end
108
+
109
+ it "should include the base64 encoded HMAC signature as the last part of the header value" do
110
+ @authhmac.sign!(@get_request, 'key-id')
111
+ @get_request['Authorization'].should match(/:[A-Za-z0-9+\/]{26,28}[=]{0,2}$/)
112
+ end
113
+
114
+ it "should create a complete signature" do
115
+ @store.should_receive(:[]).with('my-key-id').and_return('secret')
116
+ @authhmac.sign!(@put_request, "my-key-id")
117
+ @put_request['Authorization'].should == "AuthHMAC my-key-id:71wAJM4IIu/3o6lcqx/tw7XnAJs="
118
+ end
119
+ end
120
+
121
+ describe "custom signatures" do
122
+ before(:each) do
123
+ @options = {
124
+ :service_id => 'MyService',
125
+ :signature => CustomSignature
126
+ }
127
+ @authhmac = AuthHMAC.new(@store, @options)
128
+ end
129
+
130
+ it "should prefix the Authorization header with custom service id" do
131
+ @authhmac.sign!(@get_request, 'key-id')
132
+ @get_request['Authorization'].should match(/^MyService /)
133
+ end
134
+
135
+ it "should create a complete signature using options" do
136
+ @store.should_receive(:[]).with('my-key-id').and_return('secret')
137
+ @authhmac.sign!(@put_request, "my-key-id")
138
+ @put_request['Authorization'].should == "MyService my-key-id:/L4N1v1BZSHfAYkQjsvZn696D9c="
139
+ end
140
+ end
141
+ end
142
+
143
+ describe "authenticated?" do
144
+ before(:each) do
145
+ @credentials = YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml')))
146
+ @authhmac = AuthHMAC.new(@credentials)
147
+ @request = Net::HTTP::Get.new("/path/to/get?foo=bar&bar=foo", 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
148
+ end
149
+
150
+ it "should return false when there is no Authorization Header" do
151
+ @authhmac.authenticated?(@request).should be_false
152
+ end
153
+
154
+ it "should return false when the Authorization value isn't prefixed with HMAC" do
155
+ @request['Authorization'] = "id:secret"
156
+ @authhmac.authenticated?(@request).should be_false
157
+ end
158
+
159
+ it "should return false when the access key id can't be found" do
160
+ @request['Authorization'] = 'AuthHMAC missing-key:blah'
161
+ @authhmac.authenticated?(@request).should be_false
162
+ end
163
+
164
+ it "should return false when there is no hmac" do
165
+ @request['Authorization'] = 'AuthHMAC missing-key:'
166
+ @authhmac.authenticated?(@request).should be_false
167
+ end
168
+
169
+ it "should return false when the hmac doesn't match" do
170
+ @request['Authorization'] = 'AuthHMAC access key 1:blah'
171
+ @authhmac.authenticated?(@request).should be_false
172
+ end
173
+
174
+ it "should return false if the request was modified after signing" do
175
+ @authhmac.sign!(@request, 'access key 1')
176
+ @request.content_type = 'text/plain'
177
+ @authhmac.authenticated?(@request).should be_false
178
+ end
179
+
180
+ it "should return true when the hmac does match" do
181
+ @authhmac.sign!(@request, 'access key 1')
182
+ @authhmac.authenticated?(@request).should be_true
183
+ end
184
+
185
+ describe "custom signatures" do
186
+ before(:each) do
187
+ @options = {
188
+ :service_id => 'MyService',
189
+ :signature => CustomSignature
190
+ }
191
+ end
192
+
193
+ it "should return false for invalid service id" do
194
+ @authhmac.sign!(@request, 'access key 1')
195
+ AuthHMAC.new(@credentials, @options.except(:signature)).authenticated?(@request).should be_false
196
+ end
197
+
198
+ it "should return false for request using default CanonicalString signature" do
199
+ @authhmac.sign!(@request, 'access key 1')
200
+ AuthHMAC.new(@credentials, @options.except(:service_id)).authenticated?(@request).should be_false
201
+ end
202
+
203
+ it "should return true when valid" do
204
+ @authhmac = AuthHMAC.new(@credentials, @options)
205
+ @authhmac.sign!(@request, 'access key 1')
206
+ @authhmac.authenticated?(@request).should be_true
207
+ end
208
+ end
209
+ end
210
+
211
+ describe "#sign! with YAML credentials" do
212
+ before(:each) do
213
+ @authhmac = AuthHMAC.new(YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml'))))
214
+ @request = Net::HTTP::Get.new("/path/to/get?foo=bar&bar=foo", 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
215
+ end
216
+
217
+ it "should raise an argument error if credentials are missing" do
218
+ lambda { @authhmac.sign!(@request, 'missing') }.should raise_error(ArgumentError)
219
+ end
220
+
221
+ it "should sign with the secret" do
222
+ @authhmac.sign!(@request, "access key 1")
223
+ @request['Authorization'].should == "AuthHMAC access key 1:ovwO0OBERuF3/uR3aowaUCkFMiE="
224
+ end
225
+
226
+ it "should sign with the other secret" do
227
+ @authhmac.sign!(@request, "access key 2")
228
+ @request['Authorization'].should == "AuthHMAC access key 2:vT010RQm4IZ6+UCVpK2/N0FLpLw="
229
+ end
230
+ end
231
+
232
+ describe AuthHMAC::Headers do
233
+ before(:each) do
234
+ @authhmac = AuthHMAC.new(YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml'))))
235
+ end
236
+
237
+ it "should support Rack::Request objects" do
238
+ rack_req = mock("MockRackRequest")
239
+ headers = {
240
+ 'HTTP_AUTHORIZATION' => ["AuthHMAC access key 1:ovwO0OBERuF3/uR3aowaUCkFMiE="],
241
+ 'HTTP_DATE' => ['Thu, 10 Jul 2008 03:29:56 GMT']
242
+ }
243
+ rack_req.stub!(:env).and_return(headers)
244
+ rack_req.stub!(:request_method).and_return('GET')
245
+ rack_req.stub!(:path).and_return("/path/to/get?foo=bar&bar=foo")
246
+ rack_req.stub!(:[]).and_return({'foo' => 'bar', 'bar' => 'foo'})
247
+ @authhmac.authenticated?(rack_req).should be_true
248
+ end
249
+ end
250
+
251
+ describe AuthHMAC::CanonicalString do
252
+ it "should include the http verb when it is GET" do
253
+ request = Net::HTTP::Get.new("/")
254
+ AuthHMAC::CanonicalString.new(request).should match(/GET/)
255
+ end
256
+
257
+ it "should include the http verb when it is POST" do
258
+ request = Net::HTTP::Post.new("/")
259
+ AuthHMAC::CanonicalString.new(request).should match(/POST/)
260
+ end
261
+
262
+ it "should include the content-type" do
263
+ request = Net::HTTP::Put.new("/", {'Content-Type' => 'application/xml'})
264
+ AuthHMAC::CanonicalString.new(request).should match(/application\/xml/)
265
+ end
266
+
267
+ it "should include the content-type even if the case is messed up" do
268
+ request = Net::HTTP::Put.new("/", {'cOntent-type' => 'text/html'})
269
+ AuthHMAC::CanonicalString.new(request).should match(/text\/html/)
270
+ end
271
+
272
+ it "should include the content-md5" do
273
+ request = Net::HTTP::Put.new("/", {'Content-MD5' => 'skwkend'})
274
+ AuthHMAC::CanonicalString.new(request).should match(/skwkend/)
275
+ end
276
+
277
+ it "should include the content-md5 even if the case is messed up" do
278
+ request = Net::HTTP::Put.new("/", {'content-md5' => 'adsada'})
279
+ AuthHMAC::CanonicalString.new(request).should match(/adsada/)
280
+ end
281
+
282
+ it "should include the date" do
283
+ date = Time.now.httpdate
284
+ request = Net::HTTP::Put.new("/", {'Date' => date})
285
+ AuthHMAC::CanonicalString.new(request).should match(/#{date}/)
286
+ end
287
+
288
+ it "should include the request path" do
289
+ request = Net::HTTP::Get.new("/path/to/file")
290
+ AuthHMAC::CanonicalString.new(request).should match(/\/path\/to\/file[^?]?/)
291
+ end
292
+
293
+ it "should ignore the query string of the request path" do
294
+ request = Net::HTTP::Get.new("/other/path/to/file?query=foo")
295
+ AuthHMAC::CanonicalString.new(request).should match(/\/other\/path\/to\/file[^?]?/)
296
+ end
297
+
298
+ it "should build the correct string" do
299
+ date = Time.now.httpdate
300
+ request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo",
301
+ 'content-type' => 'text/plain',
302
+ 'content-md5' => 'blahblah',
303
+ 'date' => date)
304
+ AuthHMAC::CanonicalString.new(request).should == "PUT\ntext/plain\nblahblah\n#{date}\n/path/to/put"
305
+ end
306
+
307
+ it "should build the correct string when some elements are missing" do
308
+ date = Time.now.httpdate
309
+ request = Net::HTTP::Get.new("/path/to/get?foo=bar&bar=foo",
310
+ 'date' => date)
311
+ AuthHMAC::CanonicalString.new(request).should == "GET\n\n\n#{date}\n/path/to/get"
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,2 @@
1
+ "access key 1": "secret1"
2
+ "access key 2": "secret2"
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'auth-hmac'
@@ -0,0 +1,7 @@
1
+ task :ruby_env do
2
+ RUBY_APP = if RUBY_PLATFORM =~ /java/
3
+ "jruby"
4
+ else
5
+ "ruby"
6
+ end unless defined? RUBY_APP
7
+ end
@@ -0,0 +1,17 @@
1
+ require 'auth-hmac/version'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gemspec|
6
+ gemspec.name = "dnclabs-auth-hmac"
7
+ gemspec.summary = "A gem providing HMAC based authentication for HTTP"
8
+ gemspec.description = "A gem providing HMAC based authentication for HTTP. This version includes DNC Innovation Lab changes (improvements?). See History.txt for details."
9
+ gemspec.email = "innovationlab@dnc.org"
10
+ gemspec.homepage = "http://github.com/dnclabs/auth-hmac"
11
+ gemspec.authors = ['Sean Geoghegan', 'ascarter', 'Wes Morgan']
12
+ gemspec.version = AuthHMAC::VERSION::STRING
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler not available. Install it with: gem install jeweler"
17
+ end
data/tasks/rspec.rake ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'spec'
6
+ end
7
+ begin
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ desc "Run the specs under spec/models"
18
+ Spec::Rake::SpecTask.new do |t|
19
+ t.spec_opts = ['--options', "spec/spec.opts"]
20
+ t.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
@@ -0,0 +1,9 @@
1
+ # stubs for the website generation
2
+ # To install the website framework:
3
+ # script/generate website
4
+
5
+ task :website_generate
6
+
7
+ task :website_upload
8
+
9
+ task :website => :publish_docs
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dnclabs-auth-hmac
3
+ version: !ruby/object:Gem::Version
4
+ hash: 4020123153
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 1
9
+ - 1
10
+ - 2010061601
11
+ version: 1.1.1.2010061601
12
+ platform: ruby
13
+ authors:
14
+ - Sean Geoghegan
15
+ - ascarter
16
+ - Wes Morgan
17
+ autorequire:
18
+ bindir: bin
19
+ cert_chain: []
20
+
21
+ date: 2010-06-17 00:00:00 -04:00
22
+ default_executable: auth-hmac
23
+ dependencies: []
24
+
25
+ description: A gem providing HMAC based authentication for HTTP. This version includes DNC Innovation Lab changes (improvements?). See History.txt for details.
26
+ email: innovationlab@dnc.org
27
+ executables:
28
+ - auth-hmac
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.rdoc
33
+ - README.txt
34
+ files:
35
+ - .gitignore
36
+ - History.txt
37
+ - License.txt
38
+ - Manifest.txt
39
+ - PostInstall.txt
40
+ - README.rdoc
41
+ - README.txt
42
+ - Rakefile
43
+ - bin/auth-hmac
44
+ - config/requirements.rb
45
+ - lib/auth-hmac.rb
46
+ - lib/auth-hmac/version.rb
47
+ - script/console
48
+ - script/destroy
49
+ - script/generate
50
+ - setup.rb
51
+ - spec/auth-hmac_spec.rb
52
+ - spec/fixtures/credentials.yml
53
+ - spec/spec.opts
54
+ - spec/spec_helper.rb
55
+ - tasks/environment.rake
56
+ - tasks/jeweler.rake
57
+ - tasks/rspec.rake
58
+ - tasks/website.rake
59
+ has_rdoc: true
60
+ homepage: http://github.com/dnclabs/auth-hmac
61
+ licenses: []
62
+
63
+ post_install_message:
64
+ rdoc_options:
65
+ - --charset=UTF-8
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ hash: 3
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ requirements: []
87
+
88
+ rubyforge_project:
89
+ rubygems_version: 1.3.7
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: A gem providing HMAC based authentication for HTTP
93
+ test_files:
94
+ - spec/auth-hmac_spec.rb
95
+ - spec/spec_helper.rb