bixby-auth 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,356 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "ApiAuth::Headers" do
4
+
5
+ CANONICAL_STRING = "text/plain,e59ff97941044f85df5297e1c302d260,/resource.xml?foo=bar&bar=foo,Mon, 23 Jan 1984 03:29:56 GMT"
6
+
7
+ describe "with Net::HTTP::Put::Multipart" do
8
+
9
+ before(:each) do
10
+ request = Net::HTTP::Put::Multipart.new("/resource.xml?foo=bar&bar=foo",
11
+ 'file' => UploadIO.new(File.new('spec/fixtures/upload.png'), 'image/png', 'upload.png'))
12
+ ApiAuth.sign!(request, "some access id", "some secret key")
13
+ @headers = ApiAuth::Headers.new(request)
14
+ end
15
+
16
+ it "should set the content-type" do
17
+ @headers.canonical_string.split(',')[0].should match 'multipart/form-data; boundary='
18
+ end
19
+
20
+ it "should generate the proper content-md5" do
21
+ @headers.canonical_string.split(',')[1].should match 'zap0d6zuh6wRBSrsvO2bcw=='
22
+ end
23
+
24
+ end
25
+
26
+ describe "with Net::HTTP" do
27
+
28
+ before(:each) do
29
+ @request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
30
+ 'content-type' => 'text/plain',
31
+ 'content-md5' => 'e59ff97941044f85df5297e1c302d260',
32
+ 'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
33
+ @headers = ApiAuth::Headers.new(@request)
34
+ end
35
+
36
+ it "should generate the proper canonical string" do
37
+ @headers.canonical_string.should == CANONICAL_STRING
38
+ end
39
+
40
+ it "should set the authorization header" do
41
+ @headers.sign_header("alpha")
42
+ @headers.authorization_header.should == "alpha"
43
+ end
44
+
45
+ it "should set the DATE header if one is not already present" do
46
+ @request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
47
+ 'content-type' => 'text/plain',
48
+ 'content-md5' => 'e59ff97941044f85df5297e1c302d260')
49
+ ApiAuth.sign!(@request, "some access id", "some secret key")
50
+ @request['DATE'].should_not be_nil
51
+ end
52
+
53
+ it "should not set the DATE header just by asking for the canonical_string" do
54
+ request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
55
+ 'content-type' => 'text/plain',
56
+ 'content-md5' => 'e59ff97941044f85df5297e1c302d260')
57
+ headers = ApiAuth::Headers.new(request)
58
+ headers.canonical_string
59
+ request['DATE'].should be_nil
60
+ end
61
+
62
+ context "md5_mismatch?" do
63
+ it "is false if no md5 header is present" do
64
+ request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
65
+ 'content-type' => 'text/plain')
66
+ headers = ApiAuth::Headers.new(request)
67
+ headers.md5_mismatch?.should be_false
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "with RestClient" do
73
+
74
+ before(:each) do
75
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
76
+ 'Content-Type' => "text/plain",
77
+ 'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
78
+ @request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
79
+ :headers => headers,
80
+ :method => :put)
81
+ @headers = ApiAuth::Headers.new(@request)
82
+ end
83
+
84
+ it "should generate the proper canonical string" do
85
+ @headers.canonical_string.should == CANONICAL_STRING
86
+ end
87
+
88
+ it "should set the authorization header" do
89
+ @headers.sign_header("alpha")
90
+ @headers.authorization_header.should == "alpha"
91
+ end
92
+
93
+ it "should set the DATE header if one is not already present" do
94
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
95
+ 'Content-Type' => "text/plain" }
96
+ @request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
97
+ :headers => headers,
98
+ :method => :put)
99
+ ApiAuth.sign!(@request, "some access id", "some secret key")
100
+ @request.headers['DATE'].should_not be_nil
101
+ end
102
+
103
+ it "should not set the DATE header just by asking for the canonical_string" do
104
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
105
+ 'Content-Type' => "text/plain" }
106
+ request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
107
+ :headers => headers,
108
+ :method => :put)
109
+ headers = ApiAuth::Headers.new(request)
110
+ headers.canonical_string
111
+ request.headers['DATE'].should be_nil
112
+ end
113
+
114
+ it "doesn't mess up symbol based headers" do
115
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
116
+ :content_type => "text/plain",
117
+ 'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
118
+ @request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
119
+ :headers => headers,
120
+ :method => :put)
121
+ @headers = ApiAuth::Headers.new(@request)
122
+ ApiAuth.sign!(@request, "some access id", "some secret key")
123
+ @request.processed_headers.should have_key('Content-Type')
124
+ end
125
+ end
126
+
127
+ describe "with Curb" do
128
+
129
+ before(:each) do
130
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
131
+ 'Content-Type' => "text/plain",
132
+ 'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
133
+ @request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
134
+ curl.headers = headers
135
+ end
136
+ @headers = ApiAuth::Headers.new(@request)
137
+ end
138
+
139
+ it "should generate the proper canonical string" do
140
+ @headers.canonical_string.should == CANONICAL_STRING
141
+ end
142
+
143
+ it "should set the authorization header" do
144
+ @headers.sign_header("alpha")
145
+ @headers.authorization_header.should == "alpha"
146
+ end
147
+
148
+ it "should set the DATE header if one is not already present" do
149
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
150
+ 'Content-Type' => "text/plain" }
151
+ @request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
152
+ curl.headers = headers
153
+ end
154
+ ApiAuth.sign!(@request, "some access id", "some secret key")
155
+ @request.headers['DATE'].should_not be_nil
156
+ end
157
+
158
+ it "should not set the DATE header just by asking for the canonical_string" do
159
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
160
+ 'Content-Type' => "text/plain" }
161
+ request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
162
+ curl.headers = headers
163
+ end
164
+ headers = ApiAuth::Headers.new(request)
165
+ headers.canonical_string
166
+ request.headers['DATE'].should be_nil
167
+ end
168
+ end
169
+
170
+ describe "with ActionController" do
171
+
172
+ let(:request_klass){ ActionDispatch::Request rescue ActionController::Request }
173
+
174
+ before(:each) do
175
+ @request = request_klass.new(
176
+ 'PATH_INFO' => '/resource.xml',
177
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
178
+ 'REQUEST_METHOD' => 'PUT',
179
+ 'CONTENT_MD5' => 'e59ff97941044f85df5297e1c302d260',
180
+ 'CONTENT_TYPE' => 'text/plain',
181
+ 'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT')
182
+ @headers = ApiAuth::Headers.new(@request)
183
+ end
184
+
185
+ it "should generate the proper canonical string" do
186
+ @headers.canonical_string.should == CANONICAL_STRING
187
+ end
188
+
189
+ it "should set the authorization header" do
190
+ @headers.sign_header("alpha")
191
+ @headers.authorization_header.should == "alpha"
192
+ end
193
+
194
+ it "should set the DATE header if one is not already present" do
195
+ @request = request_klass.new(
196
+ 'PATH_INFO' => '/resource.xml',
197
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
198
+ 'REQUEST_METHOD' => 'PUT',
199
+ 'CONTENT_MD5' => 'e59ff97941044f85df5297e1c302d260',
200
+ 'CONTENT_TYPE' => 'text/plain')
201
+ ApiAuth.sign!(@request, "some access id", "some secret key")
202
+ @request.headers['DATE'].should_not be_nil
203
+ end
204
+
205
+ it "should not set the DATE header just by asking for the canonical_string" do
206
+ request = request_klass.new(
207
+ 'PATH_INFO' => '/resource.xml',
208
+ 'QUERY_STRING' => 'foo=bar&bar=foo',
209
+ 'REQUEST_METHOD' => 'PUT',
210
+ 'CONTENT_MD5' => 'e59ff97941044f85df5297e1c302d260',
211
+ 'CONTENT_TYPE' => 'text/plain')
212
+ headers = ApiAuth::Headers.new(request)
213
+ headers.canonical_string
214
+ request.headers['DATE'].should be_nil
215
+ end
216
+ end
217
+
218
+ describe "with Rack::Request" do
219
+
220
+ before(:each) do
221
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
222
+ 'Content-Type' => "text/plain",
223
+ 'Date' => "Mon, 23 Jan 1984 03:29:56 GMT"
224
+ }
225
+ @request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
226
+ @headers = ApiAuth::Headers.new(@request)
227
+ end
228
+
229
+ it "should generate the proper canonical string" do
230
+ @headers.canonical_string.should == CANONICAL_STRING
231
+ end
232
+
233
+ it "should set the authorization header" do
234
+ @headers.sign_header("alpha")
235
+ @headers.authorization_header.should == "alpha"
236
+ end
237
+
238
+ it "should set the DATE header if one is not already present" do
239
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
240
+ 'Content-Type' => "text/plain" }
241
+ @request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
242
+ ApiAuth.sign!(@request, "some access id", "some secret key")
243
+ @request.env['DATE'].should_not be_nil
244
+ end
245
+
246
+ it "should not set the DATE header just by asking for the canonical_string" do
247
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
248
+ 'Content-Type' => "text/plain" }
249
+ request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
250
+ headers = ApiAuth::Headers.new(request)
251
+ headers.canonical_string
252
+ request.env['DATE'].should be_nil
253
+ end
254
+ end
255
+
256
+ describe "with HTTPI" do
257
+ before(:each) do
258
+ @request = HTTPI::Request.new("http://localhost/resource.xml?foo=bar&bar=foo")
259
+ @request.headers.merge!({
260
+ 'content-type' => 'text/plain',
261
+ 'content-md5' => 'e59ff97941044f85df5297e1c302d260',
262
+ 'date' => "Mon, 23 Jan 1984 03:29:56 GMT"
263
+ })
264
+ @headers = ApiAuth::Headers.new(@request)
265
+ end
266
+
267
+ it "should generate the proper canonical string" do
268
+ @headers.canonical_string.should == CANONICAL_STRING
269
+ end
270
+
271
+ it "should set the authorization header" do
272
+ @headers.sign_header("alpha")
273
+ @headers.authorization_header.should == "alpha"
274
+ end
275
+
276
+ it "should set the DATE header if one is not already present" do
277
+ @request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
278
+ 'content-type' => 'text/plain',
279
+ 'content-md5' => 'e59ff97941044f85df5297e1c302d260')
280
+ ApiAuth.sign!(@request, "some access id", "some secret key")
281
+ @request['DATE'].should_not be_nil
282
+ end
283
+
284
+ it "should not set the DATE header just by asking for the canonical_string" do
285
+ request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
286
+ 'content-type' => 'text/plain',
287
+ 'content-md5' => 'e59ff97941044f85df5297e1c302d260')
288
+ headers = ApiAuth::Headers.new(request)
289
+ headers.canonical_string
290
+ request['DATE'].should be_nil
291
+ end
292
+
293
+ context "md5_mismatch?" do
294
+ it "is false if no md5 header is present" do
295
+ request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
296
+ 'content-type' => 'text/plain')
297
+ headers = ApiAuth::Headers.new(request)
298
+ headers.md5_mismatch?.should be_false
299
+ end
300
+ end
301
+ end
302
+
303
+ describe "with Bixby::SignedJsonRequest" do
304
+
305
+ before(:each) do
306
+ @json_req = Bixby::JsonRequest.new("foo", "bar")
307
+ @request = Bixby::SignedJsonRequest.new(@json_req)
308
+ @request.headers.merge!({
309
+ 'Content-Type' => 'text/plain',
310
+ 'Content-MD5' => 'e59ff97941044f85df5297e1c302d260',
311
+ 'Date' => "Mon, 23 Jan 1984 03:29:56 GMT"
312
+ })
313
+ @headers = ApiAuth::Headers.new(@request)
314
+ end
315
+
316
+ it "should generate the proper canonical string" do
317
+ @headers.canonical_string.should == CANONICAL_STRING.gsub("/resource.xml?foo=bar&bar=foo", "/api")
318
+ end
319
+
320
+ it "should set the authorization header" do
321
+ @headers.sign_header("alpha")
322
+ @headers.authorization_header.should == "alpha"
323
+ end
324
+
325
+ it "should set the DATE header if one is not already present" do
326
+ @request = Bixby::SignedJsonRequest.new(@json_req)
327
+ @request.headers.merge!({
328
+ 'Content-Type' => 'text/plain',
329
+ 'Content-MD5' => 'e59ff97941044f85df5297e1c302d260'
330
+ })
331
+ ApiAuth.sign!(@request, "some access id", "some secret key")
332
+ @request.headers['Date'].should_not be_nil
333
+ end
334
+
335
+ it "should not set the DATE header just by asking for the canonical_string" do
336
+ request = Bixby::SignedJsonRequest.new(@json_req)
337
+ @request.headers.merge!({
338
+ 'Content-Type' => 'text/plain',
339
+ 'Content-MD5' => 'e59ff97941044f85df5297e1c302d260'
340
+ })
341
+ headers = ApiAuth::Headers.new(request)
342
+ headers.canonical_string
343
+ request.headers['date'].should be_nil
344
+ end
345
+
346
+ context "md5_mismatch?" do
347
+ it "is false if no md5 header is present" do
348
+ request = Bixby::SignedJsonRequest.new(@json_req)
349
+ request.headers.merge!({'Content-Type' => 'text/plain'})
350
+ headers = ApiAuth::Headers.new(request)
351
+ headers.md5_mismatch?.should be_false
352
+ end
353
+ end
354
+ end
355
+
356
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "ApiAuth::Helpers" do
4
+
5
+ it "should strip the new line character on a Base64 encoding" do
6
+ ApiAuth.b64_encode("some string").should_not match(/\n/)
7
+ end
8
+
9
+ it "should properly upcase a hash's keys" do
10
+ hsh = { "JoE" => "rOOLz" }
11
+ ApiAuth.capitalize_keys(hsh)["JOE"].should == "rOOLz"
12
+ end
13
+
14
+ end
@@ -0,0 +1,134 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Rails integration" do
4
+
5
+ API_KEY_STORE = { "1044" => "l16imAXie1sRMcJODpOG7UwC1VyoqvO13jejkfpKWX4Z09W8DC9IrU23DvCwMry7pgSFW6c5S1GIfV0OY6F/vUA==" }
6
+
7
+ describe "Rails controller integration" do
8
+
9
+ class ApplicationController < ActionController::Base
10
+
11
+ private
12
+
13
+ def require_api_auth
14
+ if (access_id = get_api_access_id_from_request)
15
+ begin
16
+ if api_authenticated?(API_KEY_STORE[access_id]) then
17
+ return true
18
+ end
19
+ rescue ApiAuth::RequestTooOld
20
+ end
21
+ end
22
+
23
+ respond_to do |format|
24
+ format.xml { render :xml => "You are unauthorized to perform this action.", :status => 401 }
25
+ format.json { render :json => "You are unauthorized to perform this action.", :status => 401 }
26
+ format.html { render :text => "You are unauthorized to perform this action", :status => 401 }
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ class TestController < ApplicationController
33
+ before_filter :require_api_auth, :only => [:index]
34
+
35
+ if defined?(ActionDispatch)
36
+ def self._routes
37
+ ActionDispatch::Routing::RouteSet.new
38
+ end
39
+ end
40
+
41
+ def index
42
+ render :text => "OK"
43
+ end
44
+
45
+ def public
46
+ render :text => "OK"
47
+ end
48
+
49
+ def rescue_action(e); raise(e); end
50
+ end
51
+
52
+ unless defined?(ActionDispatch)
53
+ ActionController::Routing::Routes.draw {|map| map.resources :test }
54
+ end
55
+
56
+ def generated_response(request, action = :index)
57
+ if defined?(ActionDispatch)
58
+ TestController.action(action).call(request.env).last
59
+ else
60
+ request.action = action.to_s
61
+ request.path = "/#{action.to_s}"
62
+ TestController.new.process(request, ActionController::TestResponse.new)
63
+ end
64
+ end
65
+
66
+ it "should permit a request with properly signed headers" do
67
+ request = ActionController::TestRequest.new
68
+ request.env['DATE'] = Time.now.utc.httpdate
69
+ ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
70
+ response = generated_response(request, :index)
71
+ response.code.should == "200"
72
+ end
73
+
74
+ it "should forbid a request with properly signed headers but timestamp > 15 minutes" do
75
+ request = ActionController::TestRequest.new
76
+ request.env['DATE'] = "Mon, 23 Jan 1984 03:29:56 GMT"
77
+ ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
78
+ response = generated_response(request, :index)
79
+ response.code.should == "401"
80
+ end
81
+
82
+ it "should insert a DATE header in the request when one hasn't been specified" do
83
+ request = ActionController::TestRequest.new
84
+ ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
85
+ request.headers['DATE'].should_not be_nil
86
+ end
87
+
88
+ it "should forbid an unsigned request to a protected controller action" do
89
+ request = ActionController::TestRequest.new
90
+ response = generated_response(request, :index)
91
+ response.code.should == "401"
92
+ end
93
+
94
+ it "should forbid a request with a bogus signature" do
95
+ request = ActionController::TestRequest.new
96
+ request.env['Authorization'] = "APIAuth bogus:bogus"
97
+ response = generated_response(request, :index)
98
+ response.code.should == "401"
99
+ end
100
+
101
+ it "should allow non-protected controller actions to function as before" do
102
+ request = ActionController::TestRequest.new
103
+ response = generated_response(request, :public)
104
+ response.code.should == "200"
105
+ end
106
+
107
+ end
108
+
109
+ describe "Rails ActiveResource integration" do
110
+
111
+ class TestResource < ActiveResource::Base
112
+ with_api_auth "1044", API_KEY_STORE["1044"]
113
+ self.site = "http://localhost/"
114
+ self.format = :xml
115
+ end
116
+
117
+ it "should send signed requests automagically" do
118
+ timestamp = Time.parse("Mon, 23 Jan 1984 03:29:56 GMT")
119
+ Time.should_receive(:now).at_least(1).times.and_return(timestamp)
120
+ ActiveResource::HttpMock.respond_to do |mock|
121
+ mock.get "/test_resources/1.xml",
122
+ {
123
+ 'Authorization' => 'APIAuth 1044:IbTx7VzSOGU55HNbV4y2jZDnVis=',
124
+ 'Accept' => 'application/xml',
125
+ 'DATE' => "Mon, 23 Jan 1984 03:29:56 GMT"
126
+ },
127
+ { :id => "1" }.to_xml(:root => 'test_resource')
128
+ end
129
+ TestResource.find(1)
130
+ end
131
+
132
+ end
133
+
134
+ end
@@ -0,0 +1,27 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'api_auth'
5
+ require 'amatch'
6
+ require 'rest_client'
7
+ require 'curb'
8
+ require 'httpi'
9
+ require 'net/http/post/multipart'
10
+
11
+ require 'active_support'
12
+ require 'active_support/test_case'
13
+ require 'action_controller'
14
+ require 'action_controller/test_case'
15
+ require 'active_resource'
16
+ require 'active_resource/http_mock'
17
+
18
+ require 'bixby-auth'
19
+ require 'bixby-common'
20
+
21
+ # Requires supporting files with custom matchers and macros, etc,
22
+ # in ./support/ and its subdirectories.
23
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
24
+
25
+ RSpec.configure do |config|
26
+
27
+ end
@@ -0,0 +1,2 @@
1
+ module TestHelper
2
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'simplecov'
2
+
3
+ module SimpleCov::Configuration
4
+ def clean_filters
5
+ @filters = []
6
+ end
7
+ end
8
+
9
+ SimpleCov.configure do
10
+ clean_filters
11
+ load_adapter 'test_frameworks'
12
+ end
13
+
14
+ ENV["COVERAGE"] && SimpleCov.start do
15
+ add_filter "/.rvm/"
16
+ end
17
+ require 'rubygems'
18
+ require 'bundler'
19
+ begin
20
+ Bundler.setup(:default, :development)
21
+ rescue Bundler::BundlerError => e
22
+ $stderr.puts e.message
23
+ $stderr.puts "Run `bundle install` to install missing gems"
24
+ exit e.status_code
25
+ end
26
+ require 'minitest/unit'
27
+
28
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
29
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
30
+ require 'bixby-auth'
31
+
32
+ class MiniTest::Unit::TestCase
33
+ end
34
+
35
+ MiniTest::Unit.autorun
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestBixbyAuth < MiniTest::Unit::TestCase
4
+ def test_something_for_real
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end