bixby-auth 0.1.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,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