omniauth 0.3.2 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of omniauth might be problematic. Click here for more details.
- data/Gemfile +11 -0
- data/Guardfile +10 -0
- data/{LICENSE.md → LICENSE} +1 -1
- data/README.md +136 -152
- data/Rakefile +6 -0
- data/lib/omniauth.rb +137 -6
- data/lib/omniauth/auth_hash.rb +51 -0
- data/lib/omniauth/builder.rb +33 -0
- data/lib/omniauth/form.rb +191 -0
- data/lib/omniauth/strategies/developer.rb +60 -0
- data/lib/omniauth/strategy.rb +429 -0
- data/lib/omniauth/test.rb +12 -0
- data/lib/omniauth/test/phony_session.rb +8 -0
- data/lib/omniauth/test/strategy_macros.rb +34 -0
- data/lib/omniauth/test/strategy_test_case.rb +49 -0
- data/lib/omniauth/version.rb +1 -17
- data/omniauth.gemspec +28 -0
- data/spec/omniauth/auth_hash_spec.rb +108 -0
- data/spec/omniauth/builder_spec.rb +20 -0
- data/spec/omniauth/strategies/developer_spec.rb +69 -0
- data/spec/omniauth/strategy_spec.rb +601 -0
- data/spec/omniauth_spec.rb +82 -0
- data/spec/spec_helper.rb +12 -0
- metadata +142 -132
@@ -0,0 +1,12 @@
|
|
1
|
+
module OmniAuth
|
2
|
+
|
3
|
+
# Support for testing OmniAuth strategies.
|
4
|
+
module Test
|
5
|
+
|
6
|
+
autoload :PhonySession, 'omniauth/test/phony_session'
|
7
|
+
autoload :StrategyMacros, 'omniauth/test/strategy_macros'
|
8
|
+
autoload :StrategyTestCase, 'omniauth/test/strategy_test_case'
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module OmniAuth
|
2
|
+
|
3
|
+
module Test
|
4
|
+
|
5
|
+
module StrategyMacros
|
6
|
+
|
7
|
+
def sets_an_auth_hash
|
8
|
+
it 'should set an auth hash' do
|
9
|
+
last_request.env['omniauth.auth'].should be_kind_of(Hash)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def sets_provider_to(provider)
|
14
|
+
it "should set the provider to #{provider}" do
|
15
|
+
(last_request.env['omniauth.auth'] || {})['provider'].should == provider
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def sets_uid_to(uid)
|
20
|
+
it "should set the UID to #{uid}" do
|
21
|
+
(last_request.env['omniauth.auth'] || {})['uid'].should == uid
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def sets_user_info_to(user_info)
|
26
|
+
it "should set the user_info to #{user_info}" do
|
27
|
+
(last_request.env['omniauth.auth'] || {})['user_info'].should == user_info
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'omniauth/test'
|
3
|
+
|
4
|
+
module OmniAuth
|
5
|
+
|
6
|
+
module Test
|
7
|
+
|
8
|
+
# Support for testing OmniAuth strategies.
|
9
|
+
#
|
10
|
+
# @example Usage
|
11
|
+
# class MyStrategyTest < Test::Unit::TestCase
|
12
|
+
# include OmniAuth::Test::StrategyTestCase
|
13
|
+
# def strategy
|
14
|
+
# # return the parameters to a Rack::Builder map call:
|
15
|
+
# [MyStrategy.new, :some, :configuration, :options => 'here']
|
16
|
+
# end
|
17
|
+
# setup do
|
18
|
+
# post '/auth/my_strategy/callback', :user => { 'name' => 'Dylan', 'id' => '445' }
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
module StrategyTestCase
|
22
|
+
|
23
|
+
def app
|
24
|
+
strat = self.strategy
|
25
|
+
resp = self.app_response
|
26
|
+
Rack::Builder.new {
|
27
|
+
use OmniAuth::Test::PhonySession
|
28
|
+
use *strat
|
29
|
+
run lambda {|env| [404, {'Content-Type' => 'text/plain'}, [resp || env.key?('omniauth.auth').to_s]] }
|
30
|
+
}.to_app
|
31
|
+
end
|
32
|
+
|
33
|
+
def app_response
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def session
|
38
|
+
last_request.env['rack.session']
|
39
|
+
end
|
40
|
+
|
41
|
+
def strategy
|
42
|
+
raise NotImplementedError.new('Including specs must define #strategy')
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/lib/omniauth/version.rb
CHANGED
@@ -1,19 +1,3 @@
|
|
1
1
|
module OmniAuth
|
2
|
-
|
3
|
-
unless defined?(::OmniAuth::Version::MAJOR)
|
4
|
-
MAJOR = 0
|
5
|
-
end
|
6
|
-
unless defined?(::OmniAuth::Version::MINOR)
|
7
|
-
MINOR = 3
|
8
|
-
end
|
9
|
-
unless defined?(::OmniAuth::Version::PATCH)
|
10
|
-
PATCH = 2
|
11
|
-
end
|
12
|
-
unless defined?(::OmniAuth::Version::PRE)
|
13
|
-
PRE = nil
|
14
|
-
end
|
15
|
-
unless defined?(::OmniAuth::Version::STRING)
|
16
|
-
STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
|
17
|
-
end
|
18
|
-
end
|
2
|
+
VERSION = "1.0.0.beta1"
|
19
3
|
end
|
data/omniauth.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path('../lib/omniauth/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'omniauth'
|
6
|
+
gem.description = %q{A generalized Rack framework for multiple-provider authentication.}
|
7
|
+
gem.authors = ['Michael Bleigh', 'Erik Michaels-Ober']
|
8
|
+
gem.email = ['michael@intridea.com', 'sferik@gmail.com']
|
9
|
+
|
10
|
+
gem.add_runtime_dependency 'rack'
|
11
|
+
gem.add_runtime_dependency 'hashie', '~> 1.2'
|
12
|
+
|
13
|
+
gem.add_development_dependency 'maruku', '~> 0.6'
|
14
|
+
gem.add_development_dependency 'simplecov', '~> 0.4'
|
15
|
+
gem.add_development_dependency 'rack-test', '~> 0.5'
|
16
|
+
gem.add_development_dependency 'rake', '~> 0.8'
|
17
|
+
gem.add_development_dependency 'rdiscount', '~> 1.6'
|
18
|
+
gem.add_development_dependency 'rspec', '~> 2.5'
|
19
|
+
gem.add_development_dependency 'yard', '~> 0.7'
|
20
|
+
|
21
|
+
gem.version = OmniAuth::VERSION
|
22
|
+
gem.files = `git ls-files`.split("\n")
|
23
|
+
gem.homepage = 'http://github.com/intridea/omniauth'
|
24
|
+
gem.require_paths = ['lib']
|
25
|
+
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if gem.respond_to? :required_rubygems_version=
|
26
|
+
gem.summary = gem.description
|
27
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
28
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OmniAuth::AuthHash do
|
4
|
+
subject{ OmniAuth::AuthHash.new }
|
5
|
+
it 'should convert a supplied info key into an InfoHash object' do
|
6
|
+
subject.info = {:first_name => 'Awesome'}
|
7
|
+
subject.info.should be_kind_of(OmniAuth::AuthHash::InfoHash)
|
8
|
+
subject.info.first_name.should == 'Awesome'
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#valid?' do
|
12
|
+
subject{ OmniAuth::AuthHash.new(:uid => '123', :provider => 'example', :info => {:name => 'Steven'}) }
|
13
|
+
|
14
|
+
it 'should be valid with the right parameters' do
|
15
|
+
subject.should be_valid
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should require a uid' do
|
19
|
+
subject.uid = nil
|
20
|
+
subject.should_not be_valid
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should require a provider' do
|
24
|
+
subject.provider = nil
|
25
|
+
subject.should_not be_valid
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should require a name in the user info hash' do
|
29
|
+
subject.info.name = nil
|
30
|
+
subject.should_not be_valid?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#name' do
|
35
|
+
subject{ OmniAuth::AuthHash.new(
|
36
|
+
:info => {
|
37
|
+
:name => 'Phillip J. Fry',
|
38
|
+
:first_name => 'Phillip',
|
39
|
+
:last_name => 'Fry',
|
40
|
+
:nickname => 'meatbag',
|
41
|
+
:email => 'fry@planetexpress.com'
|
42
|
+
})}
|
43
|
+
|
44
|
+
it 'should default to the name key' do
|
45
|
+
subject.info.name.should == 'Phillip J. Fry'
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should fall back to go to first_name last_name concatenation' do
|
49
|
+
subject.info.name = nil
|
50
|
+
subject.info.name.should == 'Phillip Fry'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should display only a first or last name if only that is available' do
|
54
|
+
subject.info.name = nil
|
55
|
+
subject.info.first_name = nil
|
56
|
+
subject.info.name.should == 'Fry'
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should display the nickname if no name, first, or last is available' do
|
60
|
+
subject.info.name = nil
|
61
|
+
%w(first_name last_name).each{|k| subject.info[k] = nil}
|
62
|
+
subject.info.name.should == 'meatbag'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should display the email if no name, first, last, or nick is available' do
|
66
|
+
subject.info.name = nil
|
67
|
+
%w(first_name last_name nickname).each{|k| subject.info[k] = nil}
|
68
|
+
subject.info.name.should == 'fry@planetexpress.com'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#to_hash' do
|
73
|
+
subject{ OmniAuth::AuthHash.new(:uid => '123', :provider => 'test', :name => 'Bob Example')}
|
74
|
+
let(:hash){ subject.to_hash }
|
75
|
+
|
76
|
+
it 'should be a plain old hash' do
|
77
|
+
hash.class.should == ::Hash
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should have string keys' do
|
81
|
+
hash.keys.should be_include('uid')
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should convert an info hash as well' do
|
85
|
+
subject.info = {:first_name => 'Bob', :last_name => 'Example'}
|
86
|
+
subject.info.class.should == OmniAuth::AuthHash::InfoHash
|
87
|
+
subject.to_hash['info'].class.should == ::Hash
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should supply the calculated name in the converted hash' do
|
91
|
+
subject.info = {:first_name => 'Bob', :last_name => 'Examplar'}
|
92
|
+
hash['info']['name'].should == 'Bob Examplar'
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should not pollute the URL hash with "name" etc' do
|
96
|
+
subject.info = {'urls' => {'Homepage' => "http://homepage.com"}}
|
97
|
+
subject.to_hash['info']['urls'].should == {'Homepage' => "http://homepage.com"}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe OmniAuth::AuthHash::InfoHash do
|
102
|
+
describe '#valid?' do
|
103
|
+
it 'should be valid if there is a name' do
|
104
|
+
OmniAuth::AuthHash::InfoHash.new(:name => 'Awesome').should be_valid
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe OmniAuth::Builder do
|
4
|
+
describe '#provider' do
|
5
|
+
it 'should translate a symbol to a constant' do
|
6
|
+
OmniAuth::Strategies.should_receive(:const_get).with('MyStrategy').and_return(Class.new)
|
7
|
+
OmniAuth::Builder.new(nil) do
|
8
|
+
provider :my_strategy
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should also just accept a class' do
|
13
|
+
class ::ExampleClass; end
|
14
|
+
|
15
|
+
lambda{ OmniAuth::Builder.new(nil) do
|
16
|
+
provider ::ExampleClass
|
17
|
+
end }.should_not raise_error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OmniAuth::Strategies::Developer do
|
4
|
+
let(:app){ Rack::Builder.new do |b|
|
5
|
+
b.use Rack::Session::Cookie
|
6
|
+
b.use OmniAuth::Strategies::Developer
|
7
|
+
b.run lambda{|env| [200, {}, ['Not Found']]}
|
8
|
+
end.to_app }
|
9
|
+
|
10
|
+
context 'request phase' do
|
11
|
+
before(:each){ get '/auth/developer' }
|
12
|
+
|
13
|
+
it 'should display a form' do
|
14
|
+
last_response.status.should == 200
|
15
|
+
last_response.body.should be_include("<form")
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should have the callback as the action for the form' do
|
19
|
+
last_response.body.should be_include("action='/auth/developer/callback'")
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should have a text field for each of the fields' do
|
23
|
+
last_response.body.scan('<input').size.should == 2
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'callback phase' do
|
28
|
+
let(:auth_hash){ last_request.env['omniauth.auth'] }
|
29
|
+
|
30
|
+
context 'with default options' do
|
31
|
+
before do
|
32
|
+
post '/auth/developer/callback', :name => 'Example User', :email => 'user@example.com'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should set the name in the auth hash' do
|
36
|
+
auth_hash.info.name.should == 'Example User'
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should set the email in the auth hash' do
|
40
|
+
auth_hash.info.email.should == 'user@example.com'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should set the uid to the email' do
|
44
|
+
auth_hash.uid.should == 'user@example.com'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with custom options' do
|
49
|
+
let(:app){ Rack::Builder.new do |b|
|
50
|
+
b.use Rack::Session::Cookie
|
51
|
+
b.use OmniAuth::Strategies::Developer, :fields => [:first_name, :last_name], :uid_field => :last_name
|
52
|
+
b.run lambda{|env| [200, {}, ['Not Found']]}
|
53
|
+
end.to_app }
|
54
|
+
|
55
|
+
before do
|
56
|
+
@options = {:uid_field => :last_name, :fields => [:first_name, :last_name]}
|
57
|
+
post '/auth/developer/callback', :first_name => 'Example', :last_name => 'User'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should set info fields properly' do
|
61
|
+
auth_hash.info.name.should == 'Example User'
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should set the uid properly' do
|
65
|
+
auth_hash.uid.should == 'User'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,601 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
class ExampleStrategy
|
4
|
+
include OmniAuth::Strategy
|
5
|
+
option :name, 'test'
|
6
|
+
def call(env); self.call!(env) end
|
7
|
+
attr_reader :last_env
|
8
|
+
def request_phase
|
9
|
+
@fail = fail!(options[:failure]) if options[:failure]
|
10
|
+
@last_env = env
|
11
|
+
return @fail if @fail
|
12
|
+
raise "Request Phase"
|
13
|
+
end
|
14
|
+
def callback_phase
|
15
|
+
@fail = fail!(options[:failure]) if options[:failure]
|
16
|
+
@last_env = env
|
17
|
+
return @fail if @fail
|
18
|
+
raise "Callback Phase"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def make_env(path = '/auth/test', props = {})
|
23
|
+
{
|
24
|
+
'REQUEST_METHOD' => 'GET',
|
25
|
+
'PATH_INFO' => path,
|
26
|
+
'rack.session' => {},
|
27
|
+
'rack.input' => StringIO.new('test=true')
|
28
|
+
}.merge(props)
|
29
|
+
end
|
30
|
+
|
31
|
+
describe OmniAuth::Strategy do
|
32
|
+
let(:app){ lambda{|env| [404, {}, ['Awesome']]}}
|
33
|
+
let(:fresh_strategy){ c = Class.new; c.send :include, OmniAuth::Strategy; c}
|
34
|
+
|
35
|
+
describe '.default_options' do
|
36
|
+
it 'should be inherited from a parent class' do
|
37
|
+
superklass = Class.new
|
38
|
+
superklass.send :include, OmniAuth::Strategy
|
39
|
+
superklass.configure do |c|
|
40
|
+
c.foo = 'bar'
|
41
|
+
end
|
42
|
+
|
43
|
+
klass = Class.new(superklass)
|
44
|
+
klass.default_options.foo.should == 'bar'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.configure' do
|
49
|
+
subject { klass = Class.new; klass.send :include, OmniAuth::Strategy; klass }
|
50
|
+
it 'should take a block and allow for default options setting' do
|
51
|
+
subject.configure do |c|
|
52
|
+
c.wakka = 'doo'
|
53
|
+
end
|
54
|
+
subject.default_options["wakka"].should == "doo"
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should take a hash and deep merge it' do
|
58
|
+
subject.configure :abc => {:def => 123}
|
59
|
+
subject.configure :abc => {:hgi => 456}
|
60
|
+
subject.default_options['abc'].should == {'def' => 123, 'hgi' => 456}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#skip_info?' do
|
65
|
+
it 'should be true if options.skip_info is true' do
|
66
|
+
ExampleStrategy.new(app, :skip_info => true).should be_skip_info
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should be false if options.skip_info is false' do
|
70
|
+
ExampleStrategy.new(app, :skip_info => false).should_not be_skip_info
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should be false by default' do
|
74
|
+
ExampleStrategy.new(app).should_not be_skip_info
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should be true if options.skip_info is a callable that evaluates to truthy' do
|
78
|
+
instance = ExampleStrategy.new(app, :skip_info => lambda{|uid| uid})
|
79
|
+
instance.should_receive(:uid).and_return(true)
|
80
|
+
instance.should be_skip_info
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '.option' do
|
85
|
+
subject { klass = Class.new; klass.send :include, OmniAuth::Strategy; klass }
|
86
|
+
it 'should set a default value' do
|
87
|
+
subject.option :abc, 123
|
88
|
+
subject.default_options.abc.should == 123
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should set the default value to nil if none is provided' do
|
92
|
+
subject.option :abc
|
93
|
+
subject.default_options.abc.should be_nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '.args' do
|
98
|
+
subject{ c = Class.new; c.send :include, OmniAuth::Strategy; c }
|
99
|
+
it 'should set args to the specified argument if there is one' do
|
100
|
+
subject.args [:abc, :def]
|
101
|
+
subject.args.should == [:abc, :def]
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should be inheritable' do
|
105
|
+
subject.args [:abc, :def]
|
106
|
+
c = Class.new(subject)
|
107
|
+
c.args.should == [:abc, :def]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'fetcher procs' do
|
112
|
+
subject{ fresh_strategy }
|
113
|
+
%w(uid info credentials extra).each do |fetcher|
|
114
|
+
it ".#{fetcher} should be able to set and retrieve a proc" do
|
115
|
+
proc = lambda{ "Hello" }
|
116
|
+
subject.send(fetcher, &proc)
|
117
|
+
subject.send(fetcher).should == proc
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'fetcher stacks' do
|
123
|
+
subject{ fresh_strategy }
|
124
|
+
%w(uid info credentials extra).each do |fetcher|
|
125
|
+
it ".#{fetcher}_stack should be an array of called ancestral procs" do
|
126
|
+
fetchy = Proc.new{ "Hello" }
|
127
|
+
subject.send(fetcher, &fetchy)
|
128
|
+
subject.send("#{fetcher}_stack", subject.new(app)).should == ["Hello"]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
%w(request_phase).each do |abstract_method|
|
134
|
+
it "#{abstract_method} should raise a NotImplementedError" do
|
135
|
+
strat = Class.new
|
136
|
+
strat.send :include, OmniAuth::Strategy
|
137
|
+
lambda{ strat.new(app).send(abstract_method) }.should raise_error(NotImplementedError)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe '#auth_hash' do
|
142
|
+
subject do
|
143
|
+
klass = Class.new
|
144
|
+
klass.send :include, OmniAuth::Strategy
|
145
|
+
klass.option :name, 'auth_hasher'
|
146
|
+
klass
|
147
|
+
end
|
148
|
+
let(:instance){ subject.new(app) }
|
149
|
+
|
150
|
+
it 'should call through to uid and info' do
|
151
|
+
instance.should_receive :uid
|
152
|
+
instance.should_receive :info
|
153
|
+
instance.auth_hash
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should return an AuthHash' do
|
157
|
+
instance.stub!(:uid).and_return('123')
|
158
|
+
instance.stub!(:info).and_return(:name => 'Hal Awesome')
|
159
|
+
hash = instance.auth_hash
|
160
|
+
hash.should be_kind_of(OmniAuth::AuthHash)
|
161
|
+
hash.uid.should == '123'
|
162
|
+
hash.info.name.should == 'Hal Awesome'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe '#initialize' do
|
167
|
+
context 'options extraction' do
|
168
|
+
it 'should be the last argument if the last argument is a Hash' do
|
169
|
+
ExampleStrategy.new(app, :abc => 123).options[:abc].should == 123
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should be the default options if any are provided' do
|
173
|
+
ExampleStrategy.stub!(:default_options).and_return(OmniAuth::Strategy::Options.new(:abc => 123))
|
174
|
+
ExampleStrategy.new(app).options.abc.should == 123
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'custom args' do
|
179
|
+
subject{ c = Class.new; c.send :include, OmniAuth::Strategy; c }
|
180
|
+
it 'should set options based on the arguments if they are supplied' do
|
181
|
+
subject.args [:abc, :def]
|
182
|
+
s = subject.new app, 123, 456
|
183
|
+
s.options[:abc].should == 123
|
184
|
+
s.options[:def].should == 456
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
it '#call should duplicate and call' do
|
190
|
+
klass = Class.new
|
191
|
+
klass.send :include, OmniAuth::Strategy
|
192
|
+
instance = klass.new(app)
|
193
|
+
instance.should_receive(:dup).and_return(instance)
|
194
|
+
instance.call({'rack.session' => {}})
|
195
|
+
end
|
196
|
+
|
197
|
+
describe '#inspect' do
|
198
|
+
it 'should just be the class name in Ruby inspect format' do
|
199
|
+
ExampleStrategy.new(app).inspect.should == '#<ExampleStrategy>'
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe '#redirect' do
|
204
|
+
it 'should use javascript if :iframe is true' do
|
205
|
+
response = ExampleStrategy.new(app, :iframe => true).redirect("http://abc.com")
|
206
|
+
response.last.body.first.should be_include("top.location.href")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '#callback_phase' do
|
211
|
+
subject{ k = Class.new; k.send :include, OmniAuth::Strategy; k.new(app) }
|
212
|
+
|
213
|
+
it 'should set the auth hash' do
|
214
|
+
env = make_env
|
215
|
+
subject.stub!(:env).and_return(env)
|
216
|
+
subject.stub!(:auth_hash).and_return("AUTH HASH")
|
217
|
+
subject.callback_phase
|
218
|
+
env['omniauth.auth'].should == "AUTH HASH"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe '#full_host' do
|
223
|
+
let(:strategy){ ExampleStrategy.new(app, {}) }
|
224
|
+
it 'should not freak out if there is a pipe in the URL' do
|
225
|
+
strategy.call!(make_env('/whatever', 'rack.url_scheme' => 'http', 'SERVER_NAME' => 'facebook.lame', 'QUERY_STRING' => 'code=asofibasf|asoidnasd', 'SCRIPT_NAME' => '', 'SERVER_PORT' => 80))
|
226
|
+
lambda{ strategy.full_host }.should_not raise_error
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe '#uid' do
|
231
|
+
subject{ fresh_strategy }
|
232
|
+
it "should be the current class's uid if one exists" do
|
233
|
+
subject.uid{ "Hi" }
|
234
|
+
subject.new(app).uid.should == "Hi"
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should inherit if it can" do
|
238
|
+
subject.uid{ "Hi" }
|
239
|
+
c = Class.new(subject)
|
240
|
+
c.new(app).uid.should == "Hi"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
%w(info credentials extra).each do |fetcher|
|
245
|
+
subject{ fresh_strategy }
|
246
|
+
it "should be the current class's proc call if one exists" do
|
247
|
+
subject.send(fetcher){ {:abc => 123} }
|
248
|
+
subject.new(app).send(fetcher).should == {:abc => 123}
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'should inherit by merging with preference for the latest class' do
|
252
|
+
subject.send(fetcher){ {:abc => 123, :def => 456} }
|
253
|
+
c = Class.new(subject)
|
254
|
+
c.send(fetcher){ {:abc => 789} }
|
255
|
+
c.new(app).send(fetcher).should == {:abc => 789, :def => 456}
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe '#call' do
|
260
|
+
let(:strategy){ ExampleStrategy.new(app, @options || {}) }
|
261
|
+
|
262
|
+
context 'omniauth.origin' do
|
263
|
+
it 'should be set on the request phase' do
|
264
|
+
lambda{ strategy.call(make_env('/auth/test', 'HTTP_REFERER' => 'http://example.com/origin')) }.should raise_error("Request Phase")
|
265
|
+
strategy.last_env['rack.session']['omniauth.origin'].should == 'http://example.com/origin'
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'should be turned into an env variable on the callback phase' do
|
269
|
+
lambda{ strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => 'http://example.com/origin'})) }.should raise_error("Callback Phase")
|
270
|
+
strategy.last_env['omniauth.origin'].should == 'http://example.com/origin'
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'should set from the params if provided' do
|
274
|
+
lambda{ strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'origin=/foo')) }.should raise_error('Request Phase')
|
275
|
+
strategy.last_env['rack.session']['omniauth.origin'].should == '/foo'
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'should be set on the failure env' do
|
279
|
+
OmniAuth.config.should_receive(:on_failure).and_return(lambda{|env| env})
|
280
|
+
@options = {:failure => :forced_fail}
|
281
|
+
strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => '/awesome'}))
|
282
|
+
end
|
283
|
+
|
284
|
+
context "with script_name" do
|
285
|
+
it 'should be set on the request phase, containing full path' do
|
286
|
+
env = {'HTTP_REFERER' => 'http://example.com/sub_uri/origin', 'SCRIPT_NAME' => '/sub_uri' }
|
287
|
+
lambda{ strategy.call(make_env('/auth/test', env)) }.should raise_error("Request Phase")
|
288
|
+
strategy.last_env['rack.session']['omniauth.origin'].should == 'http://example.com/sub_uri/origin'
|
289
|
+
end
|
290
|
+
|
291
|
+
it 'should be turned into an env variable on the callback phase, containing full path' do
|
292
|
+
env = {
|
293
|
+
'rack.session' => {'omniauth.origin' => 'http://example.com/sub_uri/origin'},
|
294
|
+
'SCRIPT_NAME' => '/sub_uri'
|
295
|
+
}
|
296
|
+
|
297
|
+
lambda{ strategy.call(make_env('/auth/test/callback', env)) }.should raise_error("Callback Phase")
|
298
|
+
strategy.last_env['omniauth.origin'].should == 'http://example.com/sub_uri/origin'
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
context 'default paths' do
|
305
|
+
it 'should use the default request path' do
|
306
|
+
lambda{ strategy.call(make_env) }.should raise_error("Request Phase")
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'should be case insensitive on request path' do
|
310
|
+
lambda{ strategy.call(make_env('/AUTH/Test'))}.should raise_error("Request Phase")
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'should be case insensitive on callback path' do
|
314
|
+
lambda{ strategy.call(make_env('/AUTH/TeSt/CaLlBAck'))}.should raise_error("Callback Phase")
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'should use the default callback path' do
|
318
|
+
lambda{ strategy.call(make_env('/auth/test/callback')) }.should raise_error("Callback Phase")
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'should strip trailing spaces on request' do
|
322
|
+
lambda{ strategy.call(make_env('/auth/test/')) }.should raise_error("Request Phase")
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'should strip trailing spaces on callback' do
|
326
|
+
lambda{ strategy.call(make_env('/auth/test/callback/')) }.should raise_error("Callback Phase")
|
327
|
+
end
|
328
|
+
|
329
|
+
context 'callback_url' do
|
330
|
+
it 'uses the default callback_path' do
|
331
|
+
strategy.should_receive(:full_host).and_return('http://example.com')
|
332
|
+
|
333
|
+
lambda{ strategy.call(make_env) }.should raise_error("Request Phase")
|
334
|
+
|
335
|
+
strategy.callback_url.should == 'http://example.com/auth/test/callback'
|
336
|
+
end
|
337
|
+
|
338
|
+
it 'preserves the query parameters' do
|
339
|
+
strategy.stub(:full_host).and_return('http://example.com')
|
340
|
+
begin
|
341
|
+
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'id=5'))
|
342
|
+
rescue RuntimeError; end
|
343
|
+
strategy.callback_url.should == 'http://example.com/auth/test/callback?id=5'
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'consider script name' do
|
347
|
+
strategy.stub(:full_host).and_return('http://example.com')
|
348
|
+
begin
|
349
|
+
strategy.call(make_env('/auth/test', 'SCRIPT_NAME' => '/sub_uri'))
|
350
|
+
rescue RuntimeError; end
|
351
|
+
strategy.callback_url.should == 'http://example.com/sub_uri/auth/test/callback'
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
context ':form option' do
|
357
|
+
it 'should call through to the supplied form option if one exists' do
|
358
|
+
strategy.options.form = lambda{|env| "Called me!"}
|
359
|
+
strategy.call(make_env('/auth/test')).should == "Called me!"
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'should call through to the app if :form => true is set as an option' do
|
363
|
+
strategy.options.form = true
|
364
|
+
strategy.call(make_env('/auth/test')).should == app.call(make_env('/auth/test'))
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context 'custom paths' do
|
369
|
+
it 'should use a custom request_path if one is provided' do
|
370
|
+
@options = {:request_path => '/awesome'}
|
371
|
+
lambda{ strategy.call(make_env('/awesome')) }.should raise_error("Request Phase")
|
372
|
+
end
|
373
|
+
|
374
|
+
it 'should use a custom callback_path if one is provided' do
|
375
|
+
@options = {:callback_path => '/radical'}
|
376
|
+
lambda{ strategy.call(make_env('/radical')) }.should raise_error("Callback Phase")
|
377
|
+
end
|
378
|
+
|
379
|
+
context 'callback_url' do
|
380
|
+
it 'uses a custom callback_path if one is provided' do
|
381
|
+
@options = {:callback_path => '/radical'}
|
382
|
+
strategy.should_receive(:full_host).and_return('http://example.com')
|
383
|
+
|
384
|
+
lambda{ strategy.call(make_env('/radical')) }.should raise_error("Callback Phase")
|
385
|
+
|
386
|
+
strategy.callback_url.should == 'http://example.com/radical'
|
387
|
+
end
|
388
|
+
|
389
|
+
it 'preserves the query parameters' do
|
390
|
+
@options = {:callback_path => '/radical'}
|
391
|
+
strategy.stub(:full_host).and_return('http://example.com')
|
392
|
+
begin
|
393
|
+
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'id=5'))
|
394
|
+
rescue RuntimeError; end
|
395
|
+
strategy.callback_url.should == 'http://example.com/radical?id=5'
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
context 'custom prefix' do
|
401
|
+
before do
|
402
|
+
@options = {:path_prefix => '/wowzers'}
|
403
|
+
end
|
404
|
+
|
405
|
+
it 'should use a custom prefix for request' do
|
406
|
+
lambda{ strategy.call(make_env('/wowzers/test')) }.should raise_error("Request Phase")
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'should use a custom prefix for callback' do
|
410
|
+
lambda{ strategy.call(make_env('/wowzers/test/callback')) }.should raise_error("Callback Phase")
|
411
|
+
end
|
412
|
+
|
413
|
+
context 'callback_url' do
|
414
|
+
it 'uses a custom prefix' do
|
415
|
+
strategy.should_receive(:full_host).and_return('http://example.com')
|
416
|
+
|
417
|
+
lambda{ strategy.call(make_env('/wowzers/test')) }.should raise_error("Request Phase")
|
418
|
+
|
419
|
+
strategy.callback_url.should == 'http://example.com/wowzers/test/callback'
|
420
|
+
end
|
421
|
+
|
422
|
+
it 'preserves the query parameters' do
|
423
|
+
strategy.stub(:full_host).and_return('http://example.com')
|
424
|
+
begin
|
425
|
+
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'id=5'))
|
426
|
+
rescue RuntimeError; end
|
427
|
+
strategy.callback_url.should == 'http://example.com/wowzers/test/callback?id=5'
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
context 'request method restriction' do
|
433
|
+
before do
|
434
|
+
OmniAuth.config.allowed_request_methods = [:post]
|
435
|
+
end
|
436
|
+
|
437
|
+
it 'should not allow a request method of the wrong type' do
|
438
|
+
lambda{ strategy.call(make_env)}.should_not raise_error
|
439
|
+
end
|
440
|
+
|
441
|
+
it 'should allow a request method of the correct type' do
|
442
|
+
lambda{ strategy.call(make_env('/auth/test', 'REQUEST_METHOD' => 'POST'))}.should raise_error("Request Phase")
|
443
|
+
end
|
444
|
+
|
445
|
+
after do
|
446
|
+
OmniAuth.config.allowed_request_methods = [:get, :post]
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
context 'receiving an OPTIONS request' do
|
451
|
+
shared_examples_for "an OPTIONS request" do
|
452
|
+
it 'should respond with 200' do
|
453
|
+
response[0].should == 200
|
454
|
+
end
|
455
|
+
|
456
|
+
it 'should set the Allow header properly' do
|
457
|
+
response[1]['Allow'].should == "GET, POST"
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
context 'to the request path' do
|
462
|
+
let(:response) { strategy.call(make_env('/auth/test', 'REQUEST_METHOD' => 'OPTIONS')) }
|
463
|
+
it_should_behave_like 'an OPTIONS request'
|
464
|
+
end
|
465
|
+
|
466
|
+
context 'to the request path' do
|
467
|
+
let(:response) { strategy.call(make_env('/auth/test/callback', 'REQUEST_METHOD' => 'OPTIONS')) }
|
468
|
+
it_should_behave_like 'an OPTIONS request'
|
469
|
+
end
|
470
|
+
|
471
|
+
context 'to some other path' do
|
472
|
+
it 'should not short-circuit the request' do
|
473
|
+
env = make_env('/other', 'REQUEST_METHOD' => 'OPTIONS')
|
474
|
+
strategy.call(env).should == app.call(env)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
context 'test mode' do
|
480
|
+
before do
|
481
|
+
OmniAuth.config.test_mode = true
|
482
|
+
end
|
483
|
+
|
484
|
+
it 'should short circuit the request phase entirely' do
|
485
|
+
response = strategy.call(make_env)
|
486
|
+
response[0].should == 302
|
487
|
+
response[1]['Location'].should == '/auth/test/callback'
|
488
|
+
end
|
489
|
+
|
490
|
+
it 'should be case insensitive on request path' do
|
491
|
+
strategy.call(make_env('/AUTH/Test'))[0].should == 302
|
492
|
+
end
|
493
|
+
|
494
|
+
it 'should respect SCRIPT_NAME (a.k.a. BaseURI)' do
|
495
|
+
response = strategy.call(make_env('/auth/test', 'SCRIPT_NAME' => '/sub_uri'))
|
496
|
+
response[1]['Location'].should == '/sub_uri/auth/test/callback'
|
497
|
+
end
|
498
|
+
|
499
|
+
it 'should be case insensitive on callback path' do
|
500
|
+
strategy.call(make_env('/AUTH/TeSt/CaLlBAck')).should == strategy.call(make_env('/auth/test/callback'))
|
501
|
+
end
|
502
|
+
|
503
|
+
it 'should maintain query string parameters' do
|
504
|
+
response = strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'cheese=stilton'))
|
505
|
+
response[1]['Location'].should == '/auth/test/callback?cheese=stilton'
|
506
|
+
end
|
507
|
+
|
508
|
+
it 'should not short circuit requests outside of authentication' do
|
509
|
+
strategy.call(make_env('/')).should == app.call(make_env('/'))
|
510
|
+
end
|
511
|
+
|
512
|
+
it 'should respond with the default hash if none is set' do
|
513
|
+
strategy.call make_env('/auth/test/callback')
|
514
|
+
strategy.env['omniauth.auth']['uid'].should == '1234'
|
515
|
+
end
|
516
|
+
|
517
|
+
it 'should respond with a provider-specific hash if one is set' do
|
518
|
+
OmniAuth.config.mock_auth[:test] = {
|
519
|
+
'uid' => 'abc'
|
520
|
+
}
|
521
|
+
|
522
|
+
strategy.call make_env('/auth/test/callback')
|
523
|
+
strategy.env['omniauth.auth']['uid'].should == 'abc'
|
524
|
+
end
|
525
|
+
|
526
|
+
it 'should simulate login failure if mocked data is set as a symbol' do
|
527
|
+
OmniAuth.config.mock_auth[:test] = :invalid_credentials
|
528
|
+
|
529
|
+
strategy.call make_env('/auth/test/callback')
|
530
|
+
strategy.env['omniauth.error.type'].should == :invalid_credentials
|
531
|
+
end
|
532
|
+
|
533
|
+
it 'should set omniauth.origin on the request phase' do
|
534
|
+
strategy.call(make_env('/auth/test', 'HTTP_REFERER' => 'http://example.com/origin'))
|
535
|
+
strategy.env['rack.session']['omniauth.origin'].should == 'http://example.com/origin'
|
536
|
+
end
|
537
|
+
|
538
|
+
it 'should set omniauth.origin from the params if provided' do
|
539
|
+
strategy.call(make_env('/auth/test', 'QUERY_STRING' => 'origin=/foo'))
|
540
|
+
strategy.env['rack.session']['omniauth.origin'].should == '/foo'
|
541
|
+
end
|
542
|
+
|
543
|
+
it 'should turn omniauth.origin into an env variable on the callback phase' do
|
544
|
+
OmniAuth.config.mock_auth[:test] = {}
|
545
|
+
|
546
|
+
strategy.call(make_env('/auth/test/callback', 'rack.session' => {'omniauth.origin' => 'http://example.com/origin'}))
|
547
|
+
strategy.env['omniauth.origin'].should == 'http://example.com/origin'
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
context 'custom full_host' do
|
552
|
+
it 'should be the string when a string is there' do
|
553
|
+
OmniAuth.config.full_host = 'my.host.com'
|
554
|
+
strategy.full_host.should == 'my.host.com'
|
555
|
+
end
|
556
|
+
|
557
|
+
it 'should run the proc with the env when it is a proc' do
|
558
|
+
OmniAuth.config.full_host = Proc.new{|env| env['HOST']}
|
559
|
+
strategy.call(make_env('/auth/test', 'HOST' => 'my.host.net'))
|
560
|
+
strategy.full_host.should == 'my.host.net'
|
561
|
+
end
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
context 'setup phase' do
|
566
|
+
context 'when options[:setup] = true' do
|
567
|
+
let(:strategy){ ExampleStrategy.new(app, :setup => true) }
|
568
|
+
let(:app){lambda{|env| env['omniauth.strategy'].options[:awesome] = 'sauce' if env['PATH_INFO'] == '/auth/test/setup'; [404, {}, 'Awesome'] }}
|
569
|
+
|
570
|
+
it 'should call through to /auth/:provider/setup' do
|
571
|
+
strategy.call(make_env('/auth/test'))
|
572
|
+
strategy.options[:awesome].should == 'sauce'
|
573
|
+
end
|
574
|
+
|
575
|
+
it 'should not call through on a non-omniauth endpoint' do
|
576
|
+
strategy.call(make_env('/somewhere/else'))
|
577
|
+
strategy.options[:awesome].should_not == 'sauce'
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
context 'when options[:setup] is an app' do
|
582
|
+
let(:setup_proc) do
|
583
|
+
Proc.new do |env|
|
584
|
+
env['omniauth.strategy'].options[:awesome] = 'sauce'
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
let(:strategy){ ExampleStrategy.new(app, :setup => setup_proc) }
|
589
|
+
|
590
|
+
it 'should not call the app on a non-omniauth endpoint' do
|
591
|
+
strategy.call(make_env('/somehwere/else'))
|
592
|
+
strategy.options[:awesome].should_not == 'sauce'
|
593
|
+
end
|
594
|
+
|
595
|
+
it 'should call the rack app' do
|
596
|
+
strategy.call(make_env('/auth/test'))
|
597
|
+
strategy.options[:awesome].should == 'sauce'
|
598
|
+
end
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|