forked from omniauth/omniauth
/
strategy_spec.rb
433 lines (352 loc) · 15.8 KB
/
strategy_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
require File.expand_path('../../spec_helper', __FILE__)
class ExampleStrategy
include OmniAuth::Strategy
def call(env); self.call!(env) end
attr_reader :last_env
def request_phase
@fail = fail!(options[:failure]) if options[:failure]
@last_env = env
return @fail if @fail
raise "Request Phase"
end
def callback_phase
@fail = fail!(options[:failure]) if options[:failure]
@last_env = env
return @fail if @fail
raise "Callback Phase"
end
end
def make_env(path = '/auth/test', props = {})
{
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => path,
'rack.session' => {},
'rack.input' => StringIO.new('test=true')
}.merge(props)
end
describe OmniAuth::Strategy do
let(:app){ lambda{|env| [404, {}, ['Awesome']]}}
describe '.default_options' do
it 'should be inherited from a parent class' do
superklass = Class.new
superklass.send :include, OmniAuth::Strategy
superklass.configure do |c|
c.foo = 'bar'
end
klass = Class.new(superklass)
klass.default_options.foo.should == 'bar'
end
end
describe '.configure' do
subject { klass = Class.new; klass.send :include, OmniAuth::Strategy; klass }
it 'should take a block and allow for default options setting' do
subject.configure do |c|
c.wakka = 'doo'
end
subject.default_options.should == {"wakka" => "doo"}
end
it 'should take a hash and deep merge it' do
subject.configure :abc => {:def => 123}
subject.configure :abc => {:hgi => 456}
subject.default_options.should == {'abc' => {'def' => 123, 'hgi' => 456}}
end
end
describe '#initialize' do
context 'options extraction' do
it 'should be the last argument if the last argument is a Hash' do
ExampleStrategy.new(app, 'test', :abc => 123).options[:abc].should == 123
end
it 'should be a blank hash if none are provided' do
ExampleStrategy.new(app, 'test').options.should == {}
end
it 'should be the default options if any are provided' do
ExampleStrategy.stub!(:default_options).and_return(OmniAuth::Strategy::Options.new(:abc => 123))
ExampleStrategy.new(app, 'test').options.abc.should == 123
end
end
end
describe '#full_host' do
let(:strategy){ ExampleStrategy.new(app, 'test', {}) }
it 'should not freak out if there is a pipe in the URL' do
strategy.call!(make_env('/whatever', 'rack.url_scheme' => 'http', 'SERVER_NAME' => 'facebook.lame', 'QUERY_STRING' => 'code=asofibasf|asoidnasd', 'SCRIPT_NAME' => '', 'SERVER_PORT' => 80))
lambda{ strategy.full_host }.should_not raise_error
end
end
describe '#call' do
let(:strategy){ ExampleStrategy.new(app, 'test', @options) }
context 'omniauth.origin' do
it 'should be set on the request phase' do
lambda{ strategy.call(make_env('/auth/test', 'HTTP_REFERER' => 'http://example.com/origin')) }.should raise_error("Request Phase")
strategy.last_env['rack.session']['omniauth.origin'].should == 'http://example.com/origin'
end
it 'should be turned into an env variable on the callback phase' do
lambda{ strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => 'http://example.com/origin'})) }.should raise_error("Callback Phase")
strategy.last_env['omniauth.origin'].should == 'http://example.com/origin'
end
it 'should set from the params if provided' do
lambda{ strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'origin=/foo')) }.should raise_error('Request Phase')
strategy.last_env['rack.session']['omniauth.origin'].should == '/foo'
end
it 'should be set on the failure env' do
OmniAuth.config.should_receive(:on_failure).and_return(lambda{|env| env})
@options = {:failure => :forced_fail}
strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => '/awesome'}))
end
context "with script_name" do
it 'should be set on the request phase, containing full path' do
env = {'HTTP_REFERER' => 'http://example.com/sub_uri/origin', 'SCRIPT_NAME' => '/sub_uri' }
lambda{ strategy.call(make_env('/auth/test', env)) }.should raise_error("Request Phase")
strategy.last_env['rack.session']['omniauth.origin'].should == 'http://example.com/sub_uri/origin'
end
it 'should be turned into an env variable on the callback phase, containing full path' do
env = {
'rack.session' => {'omniauth.origin' => 'http://example.com/sub_uri/origin'},
'SCRIPT_NAME' => '/sub_uri'
}
lambda{ strategy.call(make_env('/auth/test/callback', env)) }.should raise_error("Callback Phase")
strategy.last_env['omniauth.origin'].should == 'http://example.com/sub_uri/origin'
end
end
end
context 'default paths' do
it 'should use the default request path' do
lambda{ strategy.call(make_env) }.should raise_error("Request Phase")
end
it 'should be case insensitive on request path' do
lambda{ strategy.call(make_env('/AUTH/Test'))}.should raise_error("Request Phase")
end
it 'should be case insensitive on callback path' do
lambda{ strategy.call(make_env('/AUTH/TeSt/CaLlBAck'))}.should raise_error("Callback Phase")
end
it 'should use the default callback path' do
lambda{ strategy.call(make_env('/auth/test/callback')) }.should raise_error("Callback Phase")
end
it 'should strip trailing spaces on request' do
lambda{ strategy.call(make_env('/auth/test/')) }.should raise_error("Request Phase")
end
it 'should strip trailing spaces on callback' do
lambda{ strategy.call(make_env('/auth/test/callback/')) }.should raise_error("Callback Phase")
end
context 'callback_url' do
it 'uses the default callback_path' do
strategy.should_receive(:full_host).and_return('http://example.com')
lambda{ strategy.call(make_env) }.should raise_error("Request Phase")
strategy.callback_url.should == 'http://example.com/auth/test/callback'
end
it 'preserves the query parameters' do
strategy.stub(:full_host).and_return('http://example.com')
begin
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'id=5'))
rescue RuntimeError; end
strategy.callback_url.should == 'http://example.com/auth/test/callback?id=5'
end
it 'consider script name' do
strategy.stub(:full_host).and_return('http://example.com')
begin
strategy.call(make_env('/auth/test', 'SCRIPT_NAME' => '/sub_uri'))
rescue RuntimeError; end
strategy.callback_url.should == 'http://example.com/sub_uri/auth/test/callback'
end
end
end
context 'pre-request call through' do
subject { ExampleStrategy.new(app, 'test') }
let(:app){ lambda{|env| env['omniauth.boom'] = true; [env['test.status'] || 404, {}, ['Whatev']] } }
it 'should be able to modify the env on the fly before the request_phase' do
lambda{ subject.call(make_env) }.should raise_error("Request Phase")
subject.response.status.should == 404
subject.last_env.should be_key('omniauth.boom')
end
it 'should call through to the app instead if a non-404 response is received' do
lambda{ subject.call(make_env('/auth/test', 'test.status' => 200)) }.should_not raise_error
subject.response.body.should == ['Whatev']
end
end
context 'custom paths' do
it 'should use a custom request_path if one is provided' do
@options = {:request_path => '/awesome'}
lambda{ strategy.call(make_env('/awesome')) }.should raise_error("Request Phase")
end
it 'should use a custom callback_path if one is provided' do
@options = {:callback_path => '/radical'}
lambda{ strategy.call(make_env('/radical')) }.should raise_error("Callback Phase")
end
context 'callback_url' do
it 'uses a custom callback_path if one is provided' do
@options = {:callback_path => '/radical'}
strategy.should_receive(:full_host).and_return('http://example.com')
lambda{ strategy.call(make_env('/radical')) }.should raise_error("Callback Phase")
strategy.callback_url.should == 'http://example.com/radical'
end
it 'preserves the query parameters' do
@options = {:callback_path => '/radical'}
strategy.stub(:full_host).and_return('http://example.com')
begin
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'id=5'))
rescue RuntimeError; end
strategy.callback_url.should == 'http://example.com/radical?id=5'
end
end
end
context 'custom prefix' do
before do
@options = {:path_prefix => '/wowzers'}
end
it 'should use a custom prefix for request' do
lambda{ strategy.call(make_env('/wowzers/test')) }.should raise_error("Request Phase")
end
it 'should use a custom prefix for callback' do
lambda{ strategy.call(make_env('/wowzers/test/callback')) }.should raise_error("Callback Phase")
end
context 'callback_url' do
it 'uses a custom prefix' do
strategy.should_receive(:full_host).and_return('http://example.com')
lambda{ strategy.call(make_env('/wowzers/test')) }.should raise_error("Request Phase")
strategy.callback_url.should == 'http://example.com/wowzers/test/callback'
end
it 'preserves the query parameters' do
strategy.stub(:full_host).and_return('http://example.com')
begin
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'id=5'))
rescue RuntimeError; end
strategy.callback_url.should == 'http://example.com/wowzers/test/callback?id=5'
end
end
end
context 'request method restriction' do
before do
OmniAuth.config.allowed_request_methods = [:post]
end
it 'should not allow a request method of the wrong type' do
lambda{ strategy.call(make_env)}.should_not raise_error
end
it 'should allow a request method of the correct type' do
lambda{ strategy.call(make_env('/auth/test', 'REQUEST_METHOD' => 'POST'))}.should raise_error("Request Phase")
end
after do
OmniAuth.config.allowed_request_methods = [:get, :post]
end
end
context 'receiving an OPTIONS request' do
shared_examples_for "an OPTIONS request" do
it 'should respond with 200' do
response[0].should == 200
end
it 'should set the Allow header properly' do
response[1]['Allow'].should == "GET, POST"
end
end
context 'to the request path' do
let(:response) { strategy.call(make_env('/auth/test', 'REQUEST_METHOD' => 'OPTIONS')) }
it_should_behave_like 'an OPTIONS request'
end
context 'to the request path' do
let(:response) { strategy.call(make_env('/auth/test/callback', 'REQUEST_METHOD' => 'OPTIONS')) }
it_should_behave_like 'an OPTIONS request'
end
context 'to some other path' do
it 'should not short-circuit the request' do
env = make_env('/other', 'REQUEST_METHOD' => 'OPTIONS')
strategy.call(env).should == app.call(env)
end
end
end
context 'test mode' do
before do
OmniAuth.config.test_mode = true
end
it 'should short circuit the request phase entirely' do
response = strategy.call(make_env)
response[0].should == 302
response[1]['Location'].should == '/auth/test/callback'
end
it 'should be case insensitive on request path' do
strategy.call(make_env('/AUTH/Test'))[0].should == 302
end
it 'should respect SCRIPT_NAME (a.k.a. BaseURI)' do
response = strategy.call(make_env('/auth/test', 'SCRIPT_NAME' => '/sub_uri'))
response[1]['Location'].should == '/sub_uri/auth/test/callback'
end
it 'should be case insensitive on callback path' do
strategy.call(make_env('/AUTH/TeSt/CaLlBAck')).should == strategy.call(make_env('/auth/test/callback'))
end
it 'should maintain query string parameters' do
response = strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'cheese=stilton'))
response[1]['Location'].should == '/auth/test/callback?cheese=stilton'
end
it 'should not short circuit requests outside of authentication' do
strategy.call(make_env('/')).should == app.call(make_env('/'))
end
it 'should respond with the default hash if none is set' do
strategy.call make_env('/auth/test/callback')
strategy.env['omniauth.auth']['uid'].should == '1234'
end
it 'should respond with a provider-specific hash if one is set' do
OmniAuth.config.mock_auth[:test] = {
'uid' => 'abc'
}
strategy.call make_env('/auth/test/callback')
strategy.env['omniauth.auth']['uid'].should == 'abc'
end
it 'should simulate login failure if mocked data is set as a symbol' do
OmniAuth.config.mock_auth[:test] = :invalid_credentials
strategy.call make_env('/auth/test/callback')
strategy.env['omniauth.error.type'].should == :invalid_credentials
end
it 'should set omniauth.origin on the request phase' do
strategy.call(make_env('/auth/test', 'HTTP_REFERER' => 'http://example.com/origin'))
strategy.env['rack.session']['omniauth.origin'].should == 'http://example.com/origin'
end
it 'should set omniauth.origin from the params if provided' do
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'origin=/foo'))
strategy.env['rack.session']['omniauth.origin'].should == '/foo'
end
it 'should turn omniauth.origin into an env variable on the callback phase' do
OmniAuth.config.mock_auth[:test] = {}
strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => 'http://example.com/origin'}))
strategy.env['omniauth.origin'].should == 'http://example.com/origin'
end
end
context 'custom full_host' do
it 'should be the string when a string is there' do
OmniAuth.config.full_host = 'my.host.com'
strategy.full_host.should == 'my.host.com'
end
it 'should run the proc with the env when it is a proc' do
OmniAuth.config.full_host = Proc.new{|env| env['HOST']}
strategy.call(make_env('/auth/test', 'HOST' => 'my.host.net'))
strategy.full_host.should == 'my.host.net'
end
end
end
context 'setup phase' do
context 'when options[:setup] = true' do
let(:strategy){ ExampleStrategy.new(app, 'test', :setup => true) }
let(:app){lambda{|env| env['omniauth.strategy'].options[:awesome] = 'sauce' if env['PATH_INFO'] == '/auth/test/setup'; [404, {}, 'Awesome'] }}
it 'should call through to /auth/:provider/setup' do
strategy.call(make_env('/auth/test'))
strategy.options[:awesome].should == 'sauce'
end
it 'should not call through on a non-omniauth endpoint' do
strategy.call(make_env('/somewhere/else'))
strategy.options[:awesome].should_not == 'sauce'
end
end
context 'when options[:setup] is an app' do
let(:setup_proc) do
Proc.new do |env|
env['omniauth.strategy'].options[:awesome] = 'sauce'
end
end
let(:strategy){ ExampleStrategy.new(app, 'test', :setup => setup_proc) }
it 'should not call the app on a non-omniauth endpoint' do
strategy.call(make_env('/somehwere/else'))
strategy.options[:awesome].should_not == 'sauce'
end
it 'should call the rack app' do
strategy.call(make_env('/auth/test'))
strategy.options[:awesome].should == 'sauce'
end
end
end
end