auth-hmac 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,305 @@
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
+
13
+ describe AuthHMAC do
14
+ describe ".sign!" do
15
+ 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="
22
+ end
23
+ end
24
+
25
+ describe "#sign!" do
26
+ before(:each) do
27
+ @store = mock('store')
28
+ @store.stub!(:[]).and_return("")
29
+ @authhmac = AuthHMAC.new(@store)
30
+ 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}$/)
60
+ 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="
70
+ end
71
+ end
72
+
73
+ describe "authenticated?" do
74
+ before(:each) do
75
+ @authhmac = AuthHMAC.new(YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml'))))
76
+ @request = Net::HTTP::Get.new("/path/to/get?foo=bar&bar=foo", 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
77
+ end
78
+
79
+ it "should return false when there is no Authoization Header" do
80
+ @authhmac.authenticated?(@request).should be_false
81
+ end
82
+
83
+ it "should return false when the Authorization value isn't prefixed with HMAC" do
84
+ @request['Authorization'] = "id:secret"
85
+ @authhmac.authenticated?(@request).should be_false
86
+ end
87
+
88
+ it "should return false when the access key id can't be found" do
89
+ @request['Authorization'] = 'AuthHMAC missing-key:blah'
90
+ @authhmac.authenticated?(@request).should be_false
91
+ end
92
+
93
+ it "should return false when there is no hmac" do
94
+ @request['Authorization'] = 'AuthHMAC missing-key:'
95
+ @authhmac.authenticated?(@request).should be_false
96
+ end
97
+
98
+ it "should return false when the hmac doesn't match" do
99
+ @request['Authorization'] = 'AuthHMAC access key 1:blah'
100
+ @authhmac.authenticated?(@request).should be_false
101
+ end
102
+
103
+ it "should return false if the request was modified after signing" do
104
+ @authhmac.sign!(@request, 'access key 1')
105
+ @request.content_type = 'text/plain'
106
+ @authhmac.authenticated?(@request).should be_false
107
+ end
108
+
109
+ it "should return true when the hmac does match" do
110
+ @authhmac.sign!(@request, 'access key 1')
111
+ @authhmac.authenticated?(@request).should be_true
112
+ end
113
+ end
114
+
115
+ describe "#sign! with YAML credentials" do
116
+ before(:each) do
117
+ @authhmac = AuthHMAC.new(YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml'))))
118
+ @request = Net::HTTP::Get.new("/path/to/get?foo=bar&bar=foo", 'date' => "Thu, 10 Jul 2008 03:29:56 GMT")
119
+ end
120
+
121
+ it "should raise an argument error if credentials are missing" do
122
+ lambda { @authhmac.sign!(@request, 'missing') }.should raise_error(ArgumentError)
123
+ end
124
+
125
+ it "should sign with the secret" do
126
+ @authhmac.sign!(@request, "access key 1")
127
+ @request['Authorization'].should == "AuthHMAC access key 1:ovwO0OBERuF3/uR3aowaUCkFMiE="
128
+ end
129
+
130
+ it "should sign with the other secret" do
131
+ @authhmac.sign!(@request, "access key 2")
132
+ @request['Authorization'].should == "AuthHMAC access key 2:vT010RQm4IZ6+UCVpK2/N0FLpLw="
133
+ end
134
+ end
135
+
136
+ describe AuthHMAC::CanonicalString do
137
+ it "should include the http verb when it is GET" do
138
+ request = Net::HTTP::Get.new("/")
139
+ AuthHMAC::CanonicalString.new(request).should match(/GET/)
140
+ end
141
+
142
+ it "should include the http verb when it is POST" do
143
+ request = Net::HTTP::Post.new("/")
144
+ AuthHMAC::CanonicalString.new(request).should match(/POST/)
145
+ end
146
+
147
+ it "should include the content-type" do
148
+ request = Net::HTTP::Put.new("/", {'Content-Type' => 'application/xml'})
149
+ AuthHMAC::CanonicalString.new(request).should match(/application\/xml/)
150
+ end
151
+
152
+ it "should include the content-type even if the case is messed up" do
153
+ request = Net::HTTP::Put.new("/", {'cOntent-type' => 'text/html'})
154
+ AuthHMAC::CanonicalString.new(request).should match(/text\/html/)
155
+ end
156
+
157
+ it "should include the content-md5" do
158
+ request = Net::HTTP::Put.new("/", {'Content-MD5' => 'skwkend'})
159
+ AuthHMAC::CanonicalString.new(request).should match(/skwkend/)
160
+ end
161
+
162
+ it "should include the content-md5 even if the case is messed up" do
163
+ request = Net::HTTP::Put.new("/", {'content-md5' => 'adsada'})
164
+ AuthHMAC::CanonicalString.new(request).should match(/adsada/)
165
+ end
166
+
167
+ it "should include the date" do
168
+ date = Time.now.httpdate
169
+ request = Net::HTTP::Put.new("/", {'Date' => date})
170
+ AuthHMAC::CanonicalString.new(request).should match(/#{date}/)
171
+ end
172
+
173
+ it "should include the request path" do
174
+ request = Net::HTTP::Get.new("/path/to/file")
175
+ AuthHMAC::CanonicalString.new(request).should match(/\/path\/to\/file[^?]?/)
176
+ end
177
+
178
+ it "should ignore the query string of the request path" do
179
+ request = Net::HTTP::Get.new("/other/path/to/file?query=foo")
180
+ AuthHMAC::CanonicalString.new(request).should match(/\/other\/path\/to\/file[^?]?/)
181
+ end
182
+
183
+ it "should build the correct string" do
184
+ date = Time.now.httpdate
185
+ request = Net::HTTP::Put.new("/path/to/put?foo=bar&bar=foo",
186
+ 'content-type' => 'text/plain',
187
+ 'content-md5' => 'blahblah',
188
+ 'date' => date)
189
+ AuthHMAC::CanonicalString.new(request).should == "PUT\ntext/plain\nblahblah\n#{date}\n/path/to/put"
190
+ end
191
+
192
+ it "should build the correct string when some elements are missing" do
193
+ date = Time.now.httpdate
194
+ request = Net::HTTP::Get.new("/path/to/get?foo=bar&bar=foo",
195
+ 'date' => date)
196
+ AuthHMAC::CanonicalString.new(request).should == "GET\n\n\n#{date}\n/path/to/get"
197
+ end
198
+ end
199
+
200
+ describe AuthHMAC::Rails::ControllerFilter do
201
+ class TestController < ActionController::Base
202
+ with_auth_hmac YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml'))),
203
+ :only => [:index]
204
+
205
+ def index
206
+ render :nothing => true, :status => :ok
207
+ end
208
+
209
+ def public
210
+ render :nothing => true, :status => :ok
211
+ end
212
+
213
+ def rescue_action(e) raise(e) end
214
+ end
215
+
216
+ class MessageTestController < ActionController::Base
217
+ with_auth_hmac YAML.load(File.read(File.join(File.dirname(__FILE__), 'fixtures', 'credentials.yml'))),
218
+ :failure_message => "Stay away!", :except => :public
219
+
220
+ def index
221
+ render :nothing => true, :status => :ok
222
+ end
223
+
224
+ def public
225
+ render :nothing => true, :status => :ok
226
+ end
227
+
228
+ def rescue_action(e) raise(e) end
229
+ end
230
+
231
+ it "should allow a request with the proper hmac" do
232
+ request = ActionController::TestRequest.new
233
+ request.env['Authorization'] = "AuthHMAC access key 1:6BVEVfAyIDoI3K+WallRMnDxROQ="
234
+ request.env['DATE'] = "Thu, 10 Jul 2008 03:29:56 GMT"
235
+ request.action = 'index'
236
+ request.path = "/index"
237
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
238
+ end
239
+
240
+ it "should reject a request with no hmac" do
241
+ request = ActionController::TestRequest.new
242
+ request.action = 'index'
243
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
244
+ end
245
+
246
+ it "should reject a request with the wrong hmac" do
247
+ request = ActionController::TestRequest.new
248
+ request.action = 'index'
249
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
250
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
251
+ end
252
+
253
+ it "should include a WWW-Authenticate header with the schema AuthHMAC" do
254
+ request = ActionController::TestRequest.new
255
+ request.action = 'index'
256
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
257
+ TestController.new.process(request, ActionController::TestResponse.new).headers['WWW-Authenticate'].should == "AuthHMAC"
258
+ end
259
+
260
+ it "should include a default error message" do
261
+ request = ActionController::TestRequest.new
262
+ request.action = 'index'
263
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
264
+ TestController.new.process(request, ActionController::TestResponse.new).body.should == "HMAC Authentication failed"
265
+ end
266
+
267
+ it "should reject a request with a given message" do
268
+ request = ActionController::TestRequest.new
269
+ request.action = 'index'
270
+ request.env['Authorization'] = "AuthHMAC bogus:bogus"
271
+ MessageTestController.new.process(request, ActionController::TestResponse.new).body.should == "Stay away!"
272
+ end
273
+
274
+ it "should allow anything to access the public action (using only)" do
275
+ request = ActionController::TestRequest.new
276
+ request.action = 'public'
277
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
278
+ end
279
+
280
+ it "should allow anything to access the public action (using except)" do
281
+ request = ActionController::TestRequest.new
282
+ request.action = 'public'
283
+ MessageTestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
284
+ end
285
+ end
286
+
287
+ describe AuthHMAC::Rails::ActiveResourceExtension do
288
+ class TestResource < ActiveResource::Base
289
+ with_auth_hmac("access_id", "secret")
290
+ self.site = "http://localhost/"
291
+ end
292
+
293
+ it "should send requests using HMAC authentication" do
294
+ now = Time.parse("Thu, 10 Jul 2008 03:29:56 GMT")
295
+ Time.should_receive(:now).at_least(1).and_return(now)
296
+ ActiveResource::HttpMock.respond_to do |mock|
297
+ mock.get "/test_resources/1.xml",
298
+ {'Authorization' => 'AuthHMAC access_id:n8UlshMa8ve66U36XD3ZCbAIctg=', 'Content-Type' => 'application/xml', 'Date' => "Thu, 10 Jul 2008 03:29:56 GMT" },
299
+ {:id => "1"}.to_xml(:root => 'test_resource')
300
+ end
301
+
302
+ TestResource.find(1)
303
+ end
304
+ end
305
+ end
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,34 @@
1
+ desc 'Release the website and new gem version'
2
+ task :deploy => [:check_version, :website, :release] do
3
+ puts "Remember to create SVN tag:"
4
+ puts "svn copy svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/trunk " +
5
+ "svn+ssh://#{rubyforge_username}@rubyforge.org/var/svn/#{PATH}/tags/REL-#{VERS} "
6
+ puts "Suggested comment:"
7
+ puts "Tagging release #{CHANGES}"
8
+ end
9
+
10
+ desc 'Runs tasks website_generate and install_gem as a local deployment of the gem'
11
+ task :local_deploy => [:website_generate, :install_gem]
12
+
13
+ task :check_version do
14
+ unless ENV['VERSION']
15
+ puts 'Must pass a VERSION=x.y.z release version'
16
+ exit
17
+ end
18
+ unless ENV['VERSION'] == VERS
19
+ puts "Please update your version.rb to match the release version, currently #{VERS}"
20
+ exit
21
+ end
22
+ end
23
+
24
+ desc 'Install the package as a gem, without generating documentation(ri/rdoc)'
25
+ task :install_gem_no_doc => [:clean, :package] do
26
+ sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri"
27
+ end
28
+
29
+ namespace :manifest do
30
+ desc 'Recreate Manifest.txt to include ALL files'
31
+ task :refresh do
32
+ `rake check_manifest | patch -p0 > Manifest.txt`
33
+ end
34
+ end
@@ -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
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: auth-hmac
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Geoghegan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-08-11 00:00:00 +09:30
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.7.0
24
+ version:
25
+ description: A gem providing HMAC based authentication for HTTP
26
+ email:
27
+ - seangeo@gmail.com
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - History.txt
34
+ - License.txt
35
+ - Manifest.txt
36
+ - PostInstall.txt
37
+ - README.txt
38
+ files:
39
+ - History.txt
40
+ - License.txt
41
+ - Manifest.txt
42
+ - PostInstall.txt
43
+ - README.txt
44
+ - Rakefile
45
+ - config/hoe.rb
46
+ - config/requirements.rb
47
+ - lib/auth-hmac.rb
48
+ - lib/auth-hmac/version.rb
49
+ - script/console
50
+ - script/destroy
51
+ - script/generate
52
+ - setup.rb
53
+ - spec/auth-hmac_spec.rb
54
+ - spec/spec.opts
55
+ - spec/spec_helper.rb
56
+ - tasks/deployment.rake
57
+ - tasks/environment.rake
58
+ - tasks/rspec.rake
59
+ - tasks/website.rake
60
+ has_rdoc: true
61
+ homepage: http://auth-hmac.rubyforge.org
62
+ post_install_message: |+
63
+
64
+ For more information on auth-hmac, see http://auth-hmac.rubyforge.org
65
+
66
+ NOTE: Change this information in PostInstall.txt
67
+ You can also delete it if you don't want it.
68
+
69
+
70
+ rdoc_options:
71
+ - --main
72
+ - README.txt
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ requirements: []
88
+
89
+ rubyforge_project: auth-hmac
90
+ rubygems_version: 1.2.0
91
+ signing_key:
92
+ specification_version: 2
93
+ summary: A gem providing HMAC based authentication for HTTP
94
+ test_files: []
95
+