dnclabs-auth-hmac 1.1.1.2010061601

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