auth-hmac 1.0.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.
@@ -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
+