mirror of
https://github.com/omniauth/omniauth.git
synced 2022-11-09 12:31:49 -05:00
445 lines
16 KiB
Ruby
445 lines
16 KiB
Ruby
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 '.option' do
|
|
subject { klass = Class.new; klass.send :include, OmniAuth::Strategy; klass }
|
|
it 'should set a default value' do
|
|
subject.option :abc, 123
|
|
subject.default_options.abc.should == 123
|
|
end
|
|
|
|
it 'should set the default value to nil if none is provided' do
|
|
subject.option :abc
|
|
subject.default_options.abc.should be_nil
|
|
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
|