omniauth-identity 2.0.0 → 3.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -1
- data/CODE_OF_CONDUCT.md +133 -0
- data/{README.markdown → README.md} +62 -29
- data/lib/omniauth-identity/version.rb +1 -1
- data/lib/omniauth/identity.rb +0 -1
- data/lib/omniauth/identity/model.rb +9 -9
- data/lib/omniauth/identity/models/active_record.rb +2 -1
- data/lib/omniauth/identity/models/couch_potato.rb +1 -4
- data/lib/omniauth/identity/models/mongoid.rb +1 -7
- data/lib/omniauth/identity/secure_password.rb +2 -4
- data/lib/omniauth/strategies/identity.rb +39 -22
- data/spec/omniauth/identity/model_spec.rb +34 -33
- data/spec/omniauth/identity/models/active_record_spec.rb +22 -10
- data/spec/omniauth/identity/models/couch_potato_spec.rb +4 -3
- data/spec/omniauth/identity/models/mongoid_spec.rb +6 -5
- data/spec/omniauth/identity/secure_password_spec.rb +7 -7
- data/spec/omniauth/strategies/identity_spec.rb +173 -59
- data/spec/spec_helper.rb +16 -8
- metadata +111 -48
- data/.gitignore +0 -6
- data/.rspec +0 -3
- data/Gemfile +0 -13
- data/Guardfile +0 -10
- data/Rakefile +0 -9
- data/lib/omniauth/identity/models/data_mapper.rb +0 -32
- data/omniauth-identity.gemspec +0 -32
- data/spec/omniauth/identity/models/data_mapper_spec.rb +0 -24
@@ -4,11 +4,8 @@ module OmniAuth
|
|
4
4
|
module Identity
|
5
5
|
module Models
|
6
6
|
module Mongoid
|
7
|
-
|
8
7
|
def self.included(base)
|
9
|
-
|
10
8
|
base.class_eval do
|
11
|
-
|
12
9
|
include ::OmniAuth::Identity::Model
|
13
10
|
include ::OmniAuth::Identity::SecurePassword
|
14
11
|
|
@@ -16,17 +13,14 @@ module OmniAuth
|
|
16
13
|
|
17
14
|
def self.auth_key=(key)
|
18
15
|
super
|
19
|
-
validates_uniqueness_of key, :
|
16
|
+
validates_uniqueness_of key, case_sensitive: false
|
20
17
|
end
|
21
18
|
|
22
19
|
def self.locate(search_hash)
|
23
20
|
where(search_hash).first
|
24
21
|
end
|
25
|
-
|
26
22
|
end
|
27
|
-
|
28
23
|
end
|
29
|
-
|
30
24
|
end
|
31
25
|
end
|
32
26
|
end
|
@@ -9,9 +9,7 @@ module OmniAuth
|
|
9
9
|
# a has_secure_password method.
|
10
10
|
module SecurePassword
|
11
11
|
def self.included(base)
|
12
|
-
unless base.respond_to?(:has_secure_password)
|
13
|
-
base.extend ClassMethods
|
14
|
-
end
|
12
|
+
base.extend ClassMethods unless base.respond_to?(:has_secure_password)
|
15
13
|
end
|
16
14
|
|
17
15
|
module ClassMethods
|
@@ -40,7 +38,7 @@ module OmniAuth
|
|
40
38
|
# User.find_by_name("david").try(:authenticate, "notright") # => nil
|
41
39
|
# User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
|
42
40
|
def has_secure_password
|
43
|
-
attr_reader
|
41
|
+
attr_reader :password
|
44
42
|
|
45
43
|
validates_confirmation_of :password
|
46
44
|
validates_presence_of :password_digest
|
@@ -6,39 +6,53 @@ module OmniAuth
|
|
6
6
|
class Identity
|
7
7
|
include OmniAuth::Strategy
|
8
8
|
|
9
|
-
option :fields, [
|
9
|
+
option :fields, %i[name email]
|
10
|
+
option :enable_login, true # See #other_phase documentation
|
10
11
|
option :on_login, nil
|
11
12
|
option :on_registration, nil
|
12
13
|
option :on_failed_registration, nil
|
13
|
-
option :
|
14
|
+
option :enable_registration, true
|
15
|
+
option :locate_conditions, ->(req) { { model.auth_key => req['auth_key'] } }
|
14
16
|
|
15
17
|
def request_phase
|
16
18
|
if options[:on_login]
|
17
|
-
options[:on_login].call(
|
19
|
+
options[:on_login].call(env)
|
18
20
|
else
|
19
21
|
OmniAuth::Form.build(
|
20
|
-
:
|
21
|
-
:
|
22
|
+
title: (options[:title] || 'Identity Verification'),
|
23
|
+
url: callback_path
|
22
24
|
) do |f|
|
23
25
|
f.text_field 'Login', 'auth_key'
|
24
26
|
f.password_field 'Password', 'password'
|
25
|
-
|
27
|
+
if options[:enable_registration]
|
28
|
+
f.html "<p align='center'><a href='#{registration_path}'>Create an Identity</a></p>"
|
29
|
+
end
|
26
30
|
end.to_response
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
30
34
|
def callback_phase
|
31
35
|
return fail!(:invalid_credentials) unless identity
|
36
|
+
|
32
37
|
super
|
33
38
|
end
|
34
39
|
|
35
40
|
def other_phase
|
36
|
-
if on_registration_path?
|
41
|
+
if options[:enable_registration] && on_registration_path?
|
37
42
|
if request.get?
|
38
43
|
registration_form
|
39
44
|
elsif request.post?
|
40
45
|
registration_phase
|
46
|
+
else
|
47
|
+
call_app!
|
41
48
|
end
|
49
|
+
elsif options[:enable_login] && on_request_path?
|
50
|
+
# OmniAuth, by default, disables "GET" requests for security reasons.
|
51
|
+
# This effectively disables omniauth-identity tool's login form feature.
|
52
|
+
# Because it is disabled by default, and because enabling it would desecuritize all the other
|
53
|
+
# OmniAuth strategies that may be implemented, we do not ask users to modify that setting.
|
54
|
+
# Instead we hook in here in the "other_phase", with a config setting of our own: `enable_login`
|
55
|
+
request_phase
|
42
56
|
else
|
43
57
|
call_app!
|
44
58
|
end
|
@@ -46,9 +60,9 @@ module OmniAuth
|
|
46
60
|
|
47
61
|
def registration_form
|
48
62
|
if options[:on_registration]
|
49
|
-
options[:on_registration].call(
|
63
|
+
options[:on_registration].call(env)
|
50
64
|
else
|
51
|
-
OmniAuth::Form.build(:
|
65
|
+
OmniAuth::Form.build(title: 'Register Identity') do |f|
|
52
66
|
options[:fields].each do |field|
|
53
67
|
f.text_field field.to_s.capitalize, field.to_s
|
54
68
|
end
|
@@ -59,23 +73,26 @@ module OmniAuth
|
|
59
73
|
end
|
60
74
|
|
61
75
|
def registration_phase
|
62
|
-
attributes = (options[:fields] + [
|
76
|
+
attributes = (options[:fields] + %i[password password_confirmation]).each_with_object({}) do |k, h|
|
77
|
+
h[k] = request[k.to_s]
|
78
|
+
end
|
79
|
+
if model.respond_to?(:column_names) && model.column_names.include?('provider')
|
80
|
+
attributes.reverse_merge!(provider: 'identity')
|
81
|
+
end
|
63
82
|
@identity = model.create(attributes)
|
64
83
|
if @identity.persisted?
|
65
84
|
env['PATH_INFO'] = callback_path
|
66
85
|
callback_phase
|
86
|
+
elsif options[:on_failed_registration]
|
87
|
+
env['omniauth.identity'] = @identity
|
88
|
+
options[:on_failed_registration].call(env)
|
67
89
|
else
|
68
|
-
|
69
|
-
self.env['omniauth.identity'] = @identity
|
70
|
-
options[:on_failed_registration].call(self.env)
|
71
|
-
else
|
72
|
-
registration_form
|
73
|
-
end
|
90
|
+
registration_form
|
74
91
|
end
|
75
92
|
end
|
76
93
|
|
77
|
-
uid{ identity.uid }
|
78
|
-
info{ identity.info }
|
94
|
+
uid { identity.uid }
|
95
|
+
info { identity.info }
|
79
96
|
|
80
97
|
def registration_path
|
81
98
|
options[:registration_path] || "#{path_prefix}/#{name}/register"
|
@@ -86,13 +103,13 @@ module OmniAuth
|
|
86
103
|
end
|
87
104
|
|
88
105
|
def identity
|
89
|
-
if options
|
90
|
-
conditions = instance_exec(request, &options
|
106
|
+
if options[:locate_conditions].is_a? Proc
|
107
|
+
conditions = instance_exec(request, &options[:locate_conditions])
|
91
108
|
conditions.to_hash
|
92
109
|
else
|
93
|
-
conditions = options
|
110
|
+
conditions = options[:locate_conditions].to_hash
|
94
111
|
end
|
95
|
-
@identity ||= model.authenticate(conditions, request['password']
|
112
|
+
@identity ||= model.authenticate(conditions, request['password'])
|
96
113
|
end
|
97
114
|
|
98
115
|
def model
|
@@ -2,87 +2,88 @@ class ExampleModel
|
|
2
2
|
include OmniAuth::Identity::Model
|
3
3
|
end
|
4
4
|
|
5
|
-
describe OmniAuth::Identity::Model do
|
5
|
+
RSpec.describe OmniAuth::Identity::Model do
|
6
6
|
context 'Class Methods' do
|
7
|
-
subject{ ExampleModel }
|
7
|
+
subject { ExampleModel }
|
8
8
|
|
9
9
|
describe '.locate' do
|
10
|
-
it('
|
10
|
+
it('is abstract') { expect { subject.locate('abc') }.to raise_error(NotImplementedError) }
|
11
11
|
end
|
12
12
|
|
13
13
|
describe '.authenticate' do
|
14
|
-
it '
|
15
|
-
mocked_instance = double('ExampleModel', :
|
14
|
+
it 'calls locate and then authenticate' do
|
15
|
+
mocked_instance = double('ExampleModel', authenticate: 'abbadoo')
|
16
16
|
allow(subject).to receive(:locate).with('email' => 'example').and_return(mocked_instance)
|
17
|
-
expect(subject.authenticate({'email' => 'example'},'pass')).to eq('abbadoo')
|
17
|
+
expect(subject.authenticate({ 'email' => 'example' }, 'pass')).to eq('abbadoo')
|
18
18
|
end
|
19
19
|
|
20
|
-
it '
|
21
|
-
mocked_instance = double('ExampleModel', :
|
22
|
-
allow(subject).to receive(:locate).with('email' => 'example',
|
23
|
-
|
20
|
+
it 'calls locate with additional scopes when provided' do
|
21
|
+
mocked_instance = double('ExampleModel', authenticate: 'abbadoo')
|
22
|
+
allow(subject).to receive(:locate).with('email' => 'example',
|
23
|
+
'user_type' => 'admin').and_return(mocked_instance)
|
24
|
+
expect(subject.authenticate({ 'email' => 'example', 'user_type' => 'admin' }, 'pass')).to eq('abbadoo')
|
24
25
|
end
|
25
26
|
|
26
|
-
it '
|
27
|
+
it 'recovers gracefully if locate is nil' do
|
27
28
|
allow(subject).to receive(:locate).and_return(nil)
|
28
|
-
expect(subject.authenticate('blah','foo')).to be false
|
29
|
+
expect(subject.authenticate('blah', 'foo')).to be false
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
34
|
context 'Instance Methods' do
|
34
|
-
subject{ ExampleModel.new }
|
35
|
+
subject { ExampleModel.new }
|
35
36
|
|
36
37
|
describe '#authenticate' do
|
37
|
-
it('
|
38
|
+
it('is abstract') { expect { subject.authenticate('abc') }.to raise_error(NotImplementedError) }
|
38
39
|
end
|
39
40
|
|
40
41
|
describe '#uid' do
|
41
|
-
it '
|
42
|
+
it 'defaults to #id' do
|
42
43
|
allow(subject).to receive(:respond_to?).with(:id).and_return(true)
|
43
44
|
allow(subject).to receive(:id).and_return 'wakka-do'
|
44
45
|
expect(subject.uid).to eq('wakka-do')
|
45
46
|
end
|
46
47
|
|
47
|
-
it '
|
48
|
+
it 'stringifies it' do
|
48
49
|
allow(subject).to receive(:id).and_return 123
|
49
50
|
expect(subject.uid).to eq('123')
|
50
51
|
end
|
51
52
|
|
52
|
-
it '
|
53
|
+
it 'raises NotImplementedError if #id is not defined' do
|
53
54
|
allow(subject).to receive(:respond_to?).with(:id).and_return(false)
|
54
|
-
expect{ subject.uid }.to raise_error(NotImplementedError)
|
55
|
+
expect { subject.uid }.to raise_error(NotImplementedError)
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
58
59
|
describe '#auth_key' do
|
59
|
-
it '
|
60
|
+
it 'defaults to #email' do
|
60
61
|
allow(subject).to receive(:respond_to?).with(:email).and_return(true)
|
61
62
|
allow(subject).to receive(:email).and_return('bob@bob.com')
|
62
63
|
expect(subject.auth_key).to eq('bob@bob.com')
|
63
64
|
end
|
64
65
|
|
65
|
-
it '
|
66
|
+
it 'uses the class .auth_key' do
|
66
67
|
subject.class.auth_key 'login'
|
67
68
|
allow(subject).to receive(:login).and_return 'bob'
|
68
69
|
expect(subject.auth_key).to eq('bob')
|
69
70
|
subject.class.auth_key nil
|
70
71
|
end
|
71
72
|
|
72
|
-
it '
|
73
|
-
expect{ subject.auth_key }.to raise_error(NotImplementedError)
|
73
|
+
it 'raises a NotImplementedError if the auth_key method is not defined' do
|
74
|
+
expect { subject.auth_key }.to raise_error(NotImplementedError)
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
77
78
|
describe '#auth_key=' do
|
78
|
-
it '
|
79
|
+
it 'defaults to setting email' do
|
79
80
|
allow(subject).to receive(:respond_to?).with(:email=).and_return(true)
|
80
81
|
expect(subject).to receive(:email=).with 'abc'
|
81
|
-
|
82
|
+
|
82
83
|
subject.auth_key = 'abc'
|
83
84
|
end
|
84
85
|
|
85
|
-
it '
|
86
|
+
it 'uses a custom .auth_key if one is provided' do
|
86
87
|
subject.class.auth_key 'login'
|
87
88
|
allow(subject).to receive(:respond_to?).with(:login=).and_return(true)
|
88
89
|
expect(subject).to receive(:login=).with('abc')
|
@@ -90,28 +91,28 @@ describe OmniAuth::Identity::Model do
|
|
90
91
|
subject.auth_key = 'abc'
|
91
92
|
end
|
92
93
|
|
93
|
-
it '
|
94
|
-
expect{ subject.auth_key = 'broken' }.to raise_error(NotImplementedError)
|
94
|
+
it 'raises a NotImplementedError if the autH_key method is not defined' do
|
95
|
+
expect { subject.auth_key = 'broken' }.to raise_error(NotImplementedError)
|
95
96
|
end
|
96
97
|
end
|
97
98
|
|
98
99
|
describe '#info' do
|
99
|
-
it '
|
100
|
+
it 'includes attributes that are set' do
|
100
101
|
allow(subject).to receive(:name).and_return('Bob Bobson')
|
101
102
|
allow(subject).to receive(:nickname).and_return('bob')
|
102
103
|
|
103
104
|
expect(subject.info).to eq({
|
104
|
-
|
105
|
-
|
106
|
-
|
105
|
+
'name' => 'Bob Bobson',
|
106
|
+
'nickname' => 'bob'
|
107
|
+
})
|
107
108
|
end
|
108
109
|
|
109
|
-
it '
|
110
|
+
it 'automaticallies set name off of nickname' do
|
110
111
|
allow(subject).to receive(:nickname).and_return('bob')
|
111
112
|
subject.info['name'] == 'bob'
|
112
113
|
end
|
113
114
|
|
114
|
-
it '
|
115
|
+
it 'does not overwrite a provided name' do
|
115
116
|
allow(subject).to receive(:name).and_return('Awesome Dude')
|
116
117
|
allow(subject).to receive(:first_name).and_return('Frank')
|
117
118
|
expect(subject.info['name']).to eq('Awesome Dude')
|
@@ -1,16 +1,28 @@
|
|
1
|
-
describe(OmniAuth::Identity::Models::ActiveRecord, :
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
RSpec.describe(OmniAuth::Identity::Models::ActiveRecord, db: true) do
|
2
|
+
describe 'model', type: :model do
|
3
|
+
subject(:model_klass) do
|
4
|
+
AnonymousActiveRecord.generate(
|
5
|
+
parent_klass: 'OmniAuth::Identity::Models::ActiveRecord',
|
6
|
+
columns: %w[name provider],
|
7
|
+
connection_params: { adapter: 'sqlite3', encoding: 'utf8', database: ':memory:' }
|
8
|
+
) do
|
9
|
+
def flower
|
10
|
+
'🌸'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
6
14
|
|
7
|
-
it '
|
8
|
-
allow(
|
9
|
-
|
15
|
+
it 'delegates locate to the where query method' do
|
16
|
+
allow(model_klass).to receive(:where).with('ham_sandwich' => 'open faced', 'category' => 'sandwiches',
|
17
|
+
'provider' => 'identity').and_return(['wakka'])
|
18
|
+
expect(model_klass.locate('ham_sandwich' => 'open faced', 'category' => 'sandwiches')).to eq('wakka')
|
10
19
|
end
|
20
|
+
end
|
11
21
|
|
12
|
-
|
13
|
-
|
22
|
+
describe '#table_name' do
|
23
|
+
class TestIdentity < OmniAuth::Identity::Models::ActiveRecord; end
|
24
|
+
it 'does not use STI rules for its table name' do
|
25
|
+
expect(TestIdentity.table_name).to eq('test_identities')
|
14
26
|
end
|
15
27
|
end
|
16
28
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
describe(OmniAuth::Identity::Models::CouchPotatoModule, :
|
1
|
+
RSpec.describe(OmniAuth::Identity::Models::CouchPotatoModule, db: true) do
|
2
2
|
class CouchPotatoTestIdentity
|
3
3
|
include CouchPotato::Persistence
|
4
4
|
include OmniAuth::Identity::Models::CouchPotatoModule
|
@@ -8,8 +8,9 @@ describe(OmniAuth::Identity::Models::CouchPotatoModule, :db => true) do
|
|
8
8
|
describe 'model', type: :model do
|
9
9
|
subject { CouchPotatoTestIdentity }
|
10
10
|
|
11
|
-
it '
|
12
|
-
allow(subject).to receive(:where).with('ham_sandwich' => 'open faced',
|
11
|
+
it 'delegates locate to the where query method' do
|
12
|
+
allow(subject).to receive(:where).with('ham_sandwich' => 'open faced',
|
13
|
+
'category' => 'sandwiches').and_return(['wakka'])
|
13
14
|
expect(subject.locate('ham_sandwich' => 'open faced', 'category' => 'sandwiches')).to eq('wakka')
|
14
15
|
end
|
15
16
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
describe(OmniAuth::Identity::Models::Mongoid, :
|
1
|
+
RSpec.describe(OmniAuth::Identity::Models::Mongoid, db: true) do
|
2
2
|
class MongoidTestIdentity
|
3
3
|
include Mongoid::Document
|
4
4
|
include OmniAuth::Identity::Models::Mongoid
|
@@ -6,17 +6,18 @@ describe(OmniAuth::Identity::Models::Mongoid, :db => true) do
|
|
6
6
|
store_in database: 'db1', collection: 'mongoid_test_identities', client: 'secondary'
|
7
7
|
end
|
8
8
|
|
9
|
-
describe
|
9
|
+
describe 'model', type: :model do
|
10
10
|
subject { MongoidTestIdentity }
|
11
11
|
|
12
12
|
it { is_expected.to be_mongoid_document }
|
13
13
|
|
14
14
|
it 'does not munge collection name' do
|
15
|
-
|
15
|
+
expect(subject).to be_stored_in(database: 'db1', collection: 'mongoid_test_identities', client: 'secondary')
|
16
16
|
end
|
17
17
|
|
18
|
-
it '
|
19
|
-
allow(subject).to receive(:where).with('ham_sandwich' => 'open faced',
|
18
|
+
it 'delegates locate to the where query method' do
|
19
|
+
allow(subject).to receive(:where).with('ham_sandwich' => 'open faced',
|
20
|
+
'category' => 'sandwiches').and_return(['wakka'])
|
20
21
|
expect(subject.locate('ham_sandwich' => 'open faced', 'category' => 'sandwiches')).to eq('wakka')
|
21
22
|
end
|
22
23
|
end
|
@@ -5,19 +5,19 @@ end
|
|
5
5
|
class DoesNotHaveTheMethod
|
6
6
|
end
|
7
7
|
|
8
|
-
describe OmniAuth::Identity::SecurePassword do
|
9
|
-
it '
|
8
|
+
RSpec.describe OmniAuth::Identity::SecurePassword do
|
9
|
+
it 'extends with the class methods if it does not have the method' do
|
10
10
|
expect(DoesNotHaveTheMethod).to receive(:extend).with(OmniAuth::Identity::SecurePassword::ClassMethods)
|
11
|
-
DoesNotHaveTheMethod.
|
11
|
+
DoesNotHaveTheMethod.include OmniAuth::Identity::SecurePassword
|
12
12
|
end
|
13
13
|
|
14
|
-
it '
|
14
|
+
it 'does not extend if the method is already defined' do
|
15
15
|
expect(HasTheMethod).not_to receive(:extend)
|
16
|
-
HasTheMethod.
|
16
|
+
HasTheMethod.include OmniAuth::Identity::SecurePassword
|
17
17
|
end
|
18
18
|
|
19
|
-
it '
|
20
|
-
[HasTheMethod,DoesNotHaveTheMethod].each do |klass|
|
19
|
+
it 'responds to has_secure_password afterwards' do
|
20
|
+
[HasTheMethod, DoesNotHaveTheMethod].each do |klass|
|
21
21
|
klass.send(:include, OmniAuth::Identity::SecurePassword)
|
22
22
|
expect(klass).to be_respond_to(:has_secure_password)
|
23
23
|
end
|
@@ -1,139 +1,253 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
describe OmniAuth::Strategies::Identity do
|
1
|
+
RSpec.describe OmniAuth::Strategies::Identity do
|
4
2
|
attr_accessor :app
|
5
3
|
|
6
|
-
let(:auth_hash){ last_response.headers['env']['omniauth.auth'] }
|
7
|
-
let(:identity_hash){ last_response.headers['env']['omniauth.identity'] }
|
4
|
+
let(:auth_hash) { last_response.headers['env']['omniauth.auth'] }
|
5
|
+
let(:identity_hash) { last_response.headers['env']['omniauth.identity'] }
|
6
|
+
let(:identity_options) { {} }
|
7
|
+
let(:anon_ar) do
|
8
|
+
AnonymousActiveRecord.generate(
|
9
|
+
columns: %w[name provider],
|
10
|
+
connection_params: { adapter: 'sqlite3', encoding: 'utf8', database: ':memory:' }
|
11
|
+
) do
|
12
|
+
def balloon
|
13
|
+
'🎈'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
8
17
|
|
9
18
|
# customize rack app for testing, if block is given, reverts to default
|
10
19
|
# rack app after testing is done
|
11
20
|
def set_app!(identity_options = {})
|
12
|
-
|
13
|
-
old_app = self.app
|
21
|
+
old_app = app
|
14
22
|
self.app = Rack::Builder.app do
|
15
23
|
use Rack::Session::Cookie, secret: '1234567890qwertyuiop'
|
16
24
|
use OmniAuth::Strategies::Identity, identity_options
|
17
|
-
run
|
25
|
+
run ->(env) { [404, { 'env' => env }, ['HELLO!']] }
|
18
26
|
end
|
19
27
|
if block_given?
|
20
28
|
yield
|
21
29
|
self.app = old_app
|
22
30
|
end
|
23
|
-
|
31
|
+
app
|
24
32
|
end
|
25
33
|
|
26
|
-
before
|
27
|
-
|
34
|
+
before do
|
35
|
+
opts = identity_options.reverse_merge({ model: anon_ar })
|
36
|
+
set_app!(opts)
|
28
37
|
end
|
29
38
|
|
30
39
|
describe '#request_phase' do
|
31
|
-
|
32
|
-
|
33
|
-
|
40
|
+
context 'with default settings' do
|
41
|
+
let(:identity_options) { { model: anon_ar } }
|
42
|
+
|
43
|
+
it 'displays a form' do
|
44
|
+
get '/auth/identity'
|
45
|
+
|
46
|
+
expect(last_response.body).not_to eq('HELLO!')
|
47
|
+
expect(last_response.body).to be_include('<form')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when login is enabled' do
|
52
|
+
context 'when registration is enabled' do
|
53
|
+
let(:identity_options) { { model: anon_ar, enable_registration: true, enable_login: true } }
|
54
|
+
|
55
|
+
it 'displays a form with a link to register' do
|
56
|
+
get '/auth/identity'
|
57
|
+
|
58
|
+
expect(last_response.body).not_to eq('HELLO!')
|
59
|
+
expect(last_response.body).to be_include('<form')
|
60
|
+
expect(last_response.body).to be_include('<a')
|
61
|
+
expect(last_response.body).to be_include('Create an Identity')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'when registration is disabled' do
|
66
|
+
let(:identity_options) { { model: anon_ar, enable_registration: false, enable_login: true } }
|
67
|
+
|
68
|
+
it 'displays a form without a link to register' do
|
69
|
+
get '/auth/identity'
|
70
|
+
|
71
|
+
expect(last_response.body).not_to eq('HELLO!')
|
72
|
+
expect(last_response.body).to be_include('<form')
|
73
|
+
expect(last_response.body).not_to be_include('<a')
|
74
|
+
expect(last_response.body).not_to be_include('Create an Identity')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when login is disabled' do
|
80
|
+
context 'when registration is enabled' do
|
81
|
+
let(:identity_options) { { model: anon_ar, enable_registration: true, enable_login: false } }
|
82
|
+
|
83
|
+
it 'bypasses registration form' do
|
84
|
+
get '/auth/identity'
|
85
|
+
|
86
|
+
expect(last_response.body).to eq('HELLO!')
|
87
|
+
expect(last_response.body).not_to be_include('<form')
|
88
|
+
expect(last_response.body).not_to be_include('<a')
|
89
|
+
expect(last_response.body).not_to be_include('Create an Identity')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when registration is disabled' do
|
94
|
+
let(:identity_options) { { model: anon_ar, enable_registration: false, enable_login: false } }
|
95
|
+
|
96
|
+
it 'displays a form without a link to register' do
|
97
|
+
get '/auth/identity'
|
98
|
+
|
99
|
+
expect(last_response.body).to eq('HELLO!')
|
100
|
+
expect(last_response.body).not_to be_include('<form')
|
101
|
+
expect(last_response.body).not_to be_include('<a')
|
102
|
+
expect(last_response.body).not_to be_include('Create an Identity')
|
103
|
+
end
|
104
|
+
end
|
34
105
|
end
|
35
106
|
end
|
36
107
|
|
37
108
|
describe '#callback_phase' do
|
38
|
-
let(:user){ double(:
|
109
|
+
let(:user) { double(uid: 'user1', info: { 'name' => 'Rockefeller' }) }
|
39
110
|
|
40
111
|
context 'with valid credentials' do
|
41
112
|
before do
|
42
|
-
allow(
|
43
|
-
expect(
|
44
|
-
post '/auth/identity/callback', :
|
113
|
+
allow(anon_ar).to receive('auth_key').and_return('email')
|
114
|
+
expect(anon_ar).to receive('authenticate').with({ 'email' => 'john' }, 'awesome').and_return(user)
|
115
|
+
post '/auth/identity/callback', auth_key: 'john', password: 'awesome'
|
45
116
|
end
|
46
117
|
|
47
|
-
it '
|
118
|
+
it 'populates the auth hash' do
|
48
119
|
expect(auth_hash).to be_kind_of(Hash)
|
49
120
|
end
|
50
121
|
|
51
|
-
it '
|
122
|
+
it 'populates the uid' do
|
52
123
|
expect(auth_hash['uid']).to eq('user1')
|
53
124
|
end
|
54
125
|
|
55
|
-
it '
|
56
|
-
expect(auth_hash['info']).to eq({'name' => 'Rockefeller'})
|
126
|
+
it 'populates the info hash' do
|
127
|
+
expect(auth_hash['info']).to eq({ 'name' => 'Rockefeller' })
|
57
128
|
end
|
58
129
|
end
|
59
130
|
|
60
131
|
context 'with invalid credentials' do
|
61
132
|
before do
|
62
|
-
allow(
|
63
|
-
OmniAuth.config.on_failure =
|
64
|
-
expect(
|
65
|
-
post '/auth/identity/callback', :
|
133
|
+
allow(anon_ar).to receive('auth_key').and_return('email')
|
134
|
+
OmniAuth.config.on_failure = ->(env) { [401, {}, [env['omniauth.error.type'].inspect]] }
|
135
|
+
expect(anon_ar).to receive(:authenticate).with({ 'email' => 'wrong' }, 'login').and_return(false)
|
136
|
+
post '/auth/identity/callback', auth_key: 'wrong', password: 'login'
|
66
137
|
end
|
67
138
|
|
68
|
-
it '
|
139
|
+
it 'fails with :invalid_credentials' do
|
69
140
|
expect(last_response.body).to eq(':invalid_credentials')
|
70
141
|
end
|
71
142
|
end
|
72
143
|
|
73
144
|
context 'with auth scopes' do
|
145
|
+
let(:identity_options) do
|
146
|
+
{ model: anon_ar, locate_conditions: lambda { |req|
|
147
|
+
{ model.auth_key => req['auth_key'], 'user_type' => 'admin' }
|
148
|
+
} }
|
149
|
+
end
|
74
150
|
|
75
|
-
it '
|
76
|
-
allow(
|
77
|
-
|
78
|
-
|
79
|
-
post '/auth/identity/callback', :
|
151
|
+
it 'evaluates and pass through conditions proc' do
|
152
|
+
allow(anon_ar).to receive('auth_key').and_return('email')
|
153
|
+
expect(anon_ar).to receive('authenticate').with({ 'email' => 'john', 'user_type' => 'admin' },
|
154
|
+
'awesome').and_return(user)
|
155
|
+
post '/auth/identity/callback', auth_key: 'john', password: 'awesome'
|
80
156
|
end
|
81
157
|
end
|
82
158
|
end
|
83
159
|
|
84
160
|
describe '#registration_form' do
|
85
|
-
|
86
|
-
|
87
|
-
|
161
|
+
context 'registration is enabled' do
|
162
|
+
it 'triggers from /auth/identity/register by default' do
|
163
|
+
get '/auth/identity/register'
|
164
|
+
expect(last_response.body).to be_include('Register Identity')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'registration is disabled' do
|
169
|
+
let(:identity_options) { { model: anon_ar, enable_registration: false } }
|
170
|
+
|
171
|
+
it 'calls app' do
|
172
|
+
get '/auth/identity/register'
|
173
|
+
expect(last_response.body).to be_include('HELLO!')
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'supports methods other than GET and POST' do
|
178
|
+
head '/auth/identity/register'
|
179
|
+
expect(last_response.status).to eq(404)
|
88
180
|
end
|
89
181
|
end
|
90
182
|
|
91
183
|
describe '#registration_phase' do
|
184
|
+
context 'registration is disabled' do
|
185
|
+
let(:identity_options) { { model: anon_ar, enable_registration: false } }
|
186
|
+
|
187
|
+
it 'calls app' do
|
188
|
+
post '/auth/identity/register'
|
189
|
+
expect(last_response.body).to eq('HELLO!')
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
92
193
|
context 'with successful creation' do
|
93
|
-
let(:properties)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
194
|
+
let(:properties) do
|
195
|
+
{
|
196
|
+
name: 'Awesome Dude',
|
197
|
+
email: 'awesome@example.com',
|
198
|
+
password: 'face',
|
199
|
+
password_confirmation: 'face',
|
200
|
+
provider: 'identity'
|
201
|
+
}
|
202
|
+
end
|
99
203
|
|
100
204
|
before do
|
101
|
-
allow(
|
102
|
-
m = double(:
|
103
|
-
|
205
|
+
allow(anon_ar).to receive('auth_key').and_return('email')
|
206
|
+
m = double(uid: 'abc', name: 'Awesome Dude', email: 'awesome@example.com',
|
207
|
+
info: { name: 'DUUUUDE!' }, persisted?: true)
|
208
|
+
expect(anon_ar).to receive(:create).with(properties).and_return(m)
|
104
209
|
end
|
105
210
|
|
106
|
-
it '
|
211
|
+
it 'sets the auth hash' do
|
107
212
|
post '/auth/identity/register', properties
|
108
213
|
expect(auth_hash['uid']).to eq('abc')
|
109
214
|
end
|
110
215
|
end
|
111
216
|
|
112
217
|
context 'with invalid identity' do
|
113
|
-
let(:properties)
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
218
|
+
let(:properties) do
|
219
|
+
{
|
220
|
+
name: 'Awesome Dude',
|
221
|
+
email: 'awesome@example.com',
|
222
|
+
password: 'NOT',
|
223
|
+
password_confirmation: 'MATCHING',
|
224
|
+
provider: 'identity'
|
225
|
+
}
|
226
|
+
end
|
227
|
+
let(:invalid_identity) { double(persisted?: false) }
|
119
228
|
|
120
229
|
before do
|
121
|
-
expect(
|
230
|
+
expect(anon_ar).to receive(:create).with(properties).and_return(invalid_identity)
|
122
231
|
end
|
123
232
|
|
124
233
|
context 'default' do
|
125
|
-
it '
|
234
|
+
it 'shows registration form' do
|
126
235
|
post '/auth/identity/register', properties
|
127
|
-
expect(last_response.body).to be_include(
|
236
|
+
expect(last_response.body).to be_include('Register Identity')
|
128
237
|
end
|
129
238
|
end
|
130
239
|
|
131
240
|
context 'custom on_failed_registration endpoint' do
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
241
|
+
let(:identity_options) do
|
242
|
+
{ model: anon_ar, on_failed_registration: lambda { |env|
|
243
|
+
[404, { 'env' => env }, ["FAIL'DOH!"]]
|
244
|
+
} }
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'sets the identity hash' do
|
248
|
+
post '/auth/identity/register', properties
|
249
|
+
expect(identity_hash).to eq(invalid_identity)
|
250
|
+
expect(last_response.body).to be_include("FAIL'DOH!")
|
137
251
|
end
|
138
252
|
end
|
139
253
|
end
|