sorcery 0.9.1 → 0.16.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/FUNDING.yml +1 -0
- data/.github/ISSUE_TEMPLATE.md +24 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +7 -0
- data/.github/workflows/ruby.yml +70 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +55 -0
- data/.rubocop_todo.yml +163 -0
- data/CHANGELOG.md +132 -34
- data/CODE_OF_CONDUCT.md +14 -0
- data/Gemfile +3 -17
- data/{LICENSE.txt → LICENSE.md} +1 -1
- data/MAINTAINING.md +64 -0
- data/README.md +146 -269
- data/Rakefile +4 -2
- data/SECURITY.md +19 -0
- data/gemfiles/rails_52.gemfile +7 -0
- data/gemfiles/rails_60.gemfile +7 -0
- data/gemfiles/rails_61.gemfile +7 -0
- data/gemfiles/rails_70.gemfile +7 -0
- data/lib/generators/sorcery/USAGE +1 -1
- data/lib/generators/sorcery/helpers.rb +8 -4
- data/lib/generators/sorcery/install_generator.rb +41 -35
- data/lib/generators/sorcery/templates/initializer.rb +216 -112
- data/lib/generators/sorcery/templates/migration/activity_logging.rb +7 -7
- data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +5 -5
- data/lib/generators/sorcery/templates/migration/core.rb +5 -7
- data/lib/generators/sorcery/templates/migration/external.rb +4 -4
- data/lib/generators/sorcery/templates/migration/magic_login.rb +9 -0
- data/lib/generators/sorcery/templates/migration/remember_me.rb +5 -5
- data/lib/generators/sorcery/templates/migration/reset_password.rb +7 -6
- data/lib/generators/sorcery/templates/migration/user_activation.rb +6 -6
- data/lib/sorcery/adapters/active_record_adapter.rb +11 -21
- data/lib/sorcery/adapters/mongoid_adapter.rb +23 -11
- data/lib/sorcery/controller/config.rb +27 -23
- data/lib/sorcery/controller/submodules/activity_logging.rb +16 -18
- data/lib/sorcery/controller/submodules/brute_force_protection.rb +1 -2
- data/lib/sorcery/controller/submodules/external.rb +69 -44
- data/lib/sorcery/controller/submodules/http_basic_auth.rb +18 -19
- data/lib/sorcery/controller/submodules/remember_me.rb +16 -16
- data/lib/sorcery/controller/submodules/session_timeout.rb +33 -11
- data/lib/sorcery/controller.rb +50 -35
- data/lib/sorcery/crypto_providers/aes256.rb +17 -16
- data/lib/sorcery/crypto_providers/bcrypt.rb +26 -22
- data/lib/sorcery/crypto_providers/common.rb +1 -1
- data/lib/sorcery/crypto_providers/md5.rb +5 -5
- data/lib/sorcery/crypto_providers/sha1.rb +5 -5
- data/lib/sorcery/crypto_providers/sha256.rb +2 -2
- data/lib/sorcery/crypto_providers/sha512.rb +3 -3
- data/lib/sorcery/engine.rb +19 -11
- data/lib/sorcery/model/config.rb +73 -50
- data/lib/sorcery/model/submodules/activity_logging.rb +31 -12
- data/lib/sorcery/model/submodules/brute_force_protection.rb +38 -31
- data/lib/sorcery/model/submodules/external.rb +22 -10
- data/lib/sorcery/model/submodules/magic_login.rb +130 -0
- data/lib/sorcery/model/submodules/remember_me.rb +19 -7
- data/lib/sorcery/model/submodules/reset_password.rb +64 -42
- data/lib/sorcery/model/submodules/user_activation.rb +52 -54
- data/lib/sorcery/model/temporary_token.rb +30 -7
- data/lib/sorcery/model.rb +65 -40
- data/lib/sorcery/protocols/oauth.rb +4 -9
- data/lib/sorcery/protocols/oauth2.rb +0 -2
- data/lib/sorcery/providers/auth0.rb +46 -0
- data/lib/sorcery/providers/base.rb +4 -4
- data/lib/sorcery/providers/battlenet.rb +51 -0
- data/lib/sorcery/providers/discord.rb +52 -0
- data/lib/sorcery/providers/facebook.rb +8 -11
- data/lib/sorcery/providers/github.rb +5 -7
- data/lib/sorcery/providers/google.rb +3 -5
- data/lib/sorcery/providers/heroku.rb +7 -8
- data/lib/sorcery/providers/instagram.rb +73 -0
- data/lib/sorcery/providers/jira.rb +12 -17
- data/lib/sorcery/providers/line.rb +63 -0
- data/lib/sorcery/providers/linkedin.rb +44 -35
- data/lib/sorcery/providers/liveid.rb +4 -7
- data/lib/sorcery/providers/microsoft.rb +59 -0
- data/lib/sorcery/providers/paypal.rb +60 -0
- data/lib/sorcery/providers/salesforce.rb +3 -5
- data/lib/sorcery/providers/slack.rb +45 -0
- data/lib/sorcery/providers/twitter.rb +4 -6
- data/lib/sorcery/providers/vk.rb +8 -9
- data/lib/sorcery/providers/wechat.rb +81 -0
- data/lib/sorcery/providers/xing.rb +7 -10
- data/lib/sorcery/test_helpers/internal/rails.rb +25 -17
- data/lib/sorcery/test_helpers/internal.rb +15 -14
- data/lib/sorcery/test_helpers/rails/controller.rb +1 -1
- data/lib/sorcery/test_helpers/rails/integration.rb +5 -6
- data/lib/sorcery/test_helpers/rails/request.rb +20 -0
- data/lib/sorcery/version.rb +1 -1
- data/lib/sorcery.rb +4 -17
- data/sorcery.gemspec +43 -28
- data/spec/active_record/user_activation_spec.rb +4 -5
- data/spec/active_record/user_activity_logging_spec.rb +4 -6
- data/spec/active_record/user_brute_force_protection_spec.rb +5 -6
- data/spec/active_record/user_magic_login_spec.rb +15 -0
- data/spec/active_record/user_oauth_spec.rb +5 -6
- data/spec/active_record/user_remember_me_spec.rb +5 -6
- data/spec/active_record/user_reset_password_spec.rb +4 -5
- data/spec/active_record/user_spec.rb +7 -17
- data/spec/controllers/controller_activity_logging_spec.rb +13 -24
- data/spec/controllers/controller_brute_force_protection_spec.rb +8 -10
- data/spec/controllers/controller_http_basic_auth_spec.rb +20 -21
- data/spec/controllers/controller_oauth2_spec.rb +297 -158
- data/spec/controllers/controller_oauth_spec.rb +97 -71
- data/spec/controllers/controller_remember_me_spec.rb +49 -36
- data/spec/controllers/controller_session_timeout_spec.rb +106 -20
- data/spec/controllers/controller_spec.rb +87 -111
- data/spec/orm/active_record.rb +3 -3
- data/spec/providers/example_provider_spec.rb +17 -0
- data/spec/providers/example_spec.rb +17 -0
- data/spec/providers/examples_spec.rb +17 -0
- data/spec/providers/vk_spec.rb +42 -0
- data/spec/rails_app/app/active_record/authentication.rb +1 -1
- data/spec/rails_app/app/active_record/user.rb +2 -2
- data/spec/rails_app/app/assets/config/manifest.js +1 -0
- data/spec/rails_app/app/controllers/application_controller.rb +2 -0
- data/spec/rails_app/app/controllers/sorcery_controller.rb +250 -46
- data/spec/rails_app/app/mailers/sorcery_mailer.rb +23 -17
- data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.html.erb +13 -0
- data/spec/rails_app/app/views/sorcery_mailer/magic_login_email.text.erb +6 -0
- data/spec/rails_app/config/application.rb +14 -9
- data/spec/rails_app/config/boot.rb +2 -2
- data/spec/rails_app/config/environment.rb +1 -1
- data/spec/rails_app/config/environments/test.rb +1 -1
- data/spec/rails_app/config/initializers/compatible_legacy_migration.rb +11 -0
- data/spec/rails_app/config/initializers/session_store.rb +3 -3
- data/spec/rails_app/config/routes.rb +31 -1
- data/spec/rails_app/config/secrets.yml +4 -0
- data/spec/rails_app/config.ru +1 -1
- data/spec/rails_app/db/migrate/activation/20101224223622_add_activation_to_users.rb +4 -4
- data/spec/rails_app/db/migrate/activity_logging/20101224223624_add_activity_logging_to_users.rb +10 -10
- data/spec/rails_app/db/migrate/brute_force_protection/20101224223626_add_brute_force_protection_to_users.rb +5 -5
- data/spec/rails_app/db/migrate/core/20101224223620_create_users.rb +5 -5
- data/spec/rails_app/db/migrate/external/20101224223628_create_authentications_and_user_providers.rb +3 -3
- data/spec/rails_app/db/migrate/invalidate_active_sessions/20180221093235_add_invalidate_active_sessions_before_to_users.rb +9 -0
- data/spec/rails_app/db/migrate/magic_login/20170924151831_add_magic_login_to_users.rb +17 -0
- data/spec/rails_app/db/migrate/remember_me/20101224223623_add_remember_me_token_to_users.rb +6 -6
- data/spec/rails_app/db/migrate/reset_password/20101224223622_add_reset_password_to_users.rb +7 -5
- data/spec/rails_app/db/schema.rb +7 -9
- data/spec/shared_examples/user_activation_shared_examples.rb +177 -58
- data/spec/shared_examples/user_activity_logging_shared_examples.rb +47 -41
- data/spec/shared_examples/user_brute_force_protection_shared_examples.rb +19 -24
- data/spec/shared_examples/user_magic_login_shared_examples.rb +150 -0
- data/spec/shared_examples/user_oauth_shared_examples.rb +7 -10
- data/spec/shared_examples/user_remember_me_shared_examples.rb +91 -22
- data/spec/shared_examples/user_reset_password_shared_examples.rb +153 -58
- data/spec/shared_examples/user_shared_examples.rb +328 -145
- data/spec/sorcery_crypto_providers_spec.rb +122 -75
- data/spec/sorcery_temporary_token_spec.rb +27 -0
- data/spec/spec.opts +1 -1
- data/spec/spec_helper.rb +19 -14
- data/spec/support/migration_helper.rb +29 -0
- data/spec/support/providers/example.rb +11 -0
- data/spec/support/providers/example_provider.rb +11 -0
- data/spec/support/providers/examples.rb +11 -0
- metadata +119 -89
- data/.travis.yml +0 -132
- data/gemfiles/active_record-rails40.gemfile +0 -7
- data/gemfiles/active_record-rails41.gemfile +0 -7
- data/gemfiles/mongo_mapper-rails40.gemfile +0 -9
- data/gemfiles/mongo_mapper-rails41.gemfile +0 -9
- data/gemfiles/mongoid-rails40.gemfile +0 -9
- data/gemfiles/mongoid-rails41.gemfile +0 -9
- data/gemfiles/mongoid3-rails32.gemfile +0 -9
- data/lib/sorcery/adapters/data_mapper_adapter.rb +0 -176
- data/lib/sorcery/adapters/mongo_mapper_adapter.rb +0 -110
- data/lib/sorcery/railties/tasks.rake +0 -6
- data/spec/data_mapper/user_activation_spec.rb +0 -10
- data/spec/data_mapper/user_activity_logging_spec.rb +0 -14
- data/spec/data_mapper/user_brute_force_protection_spec.rb +0 -9
- data/spec/data_mapper/user_oauth_spec.rb +0 -9
- data/spec/data_mapper/user_remember_me_spec.rb +0 -8
- data/spec/data_mapper/user_reset_password_spec.rb +0 -8
- data/spec/data_mapper/user_spec.rb +0 -27
- data/spec/mongo_mapper/user_activation_spec.rb +0 -9
- data/spec/mongo_mapper/user_activity_logging_spec.rb +0 -8
- data/spec/mongo_mapper/user_brute_force_protection_spec.rb +0 -8
- data/spec/mongo_mapper/user_oauth_spec.rb +0 -8
- data/spec/mongo_mapper/user_remember_me_spec.rb +0 -8
- data/spec/mongo_mapper/user_reset_password_spec.rb +0 -8
- data/spec/mongo_mapper/user_spec.rb +0 -37
- data/spec/mongoid/user_activation_spec.rb +0 -9
- data/spec/mongoid/user_activity_logging_spec.rb +0 -8
- data/spec/mongoid/user_brute_force_protection_spec.rb +0 -8
- data/spec/mongoid/user_oauth_spec.rb +0 -8
- data/spec/mongoid/user_remember_me_spec.rb +0 -8
- data/spec/mongoid/user_reset_password_spec.rb +0 -8
- data/spec/mongoid/user_spec.rb +0 -51
- data/spec/orm/data_mapper.rb +0 -48
- data/spec/orm/mongo_mapper.rb +0 -10
- data/spec/orm/mongoid.rb +0 -22
- data/spec/rails_app/app/data_mapper/authentication.rb +0 -8
- data/spec/rails_app/app/data_mapper/user.rb +0 -7
- data/spec/rails_app/app/mongo_mapper/authentication.rb +0 -6
- data/spec/rails_app/app/mongo_mapper/user.rb +0 -7
- data/spec/rails_app/app/mongoid/authentication.rb +0 -7
- data/spec/rails_app/app/mongoid/user.rb +0 -7
- data/spec/rails_app/config/initializers/secret_token.rb +0 -7
- data/spec/rails_app/log/development.log +0 -1791
@@ -1,10 +1,8 @@
|
|
1
|
-
shared_examples_for
|
2
|
-
|
1
|
+
shared_examples_for 'rails_3_core_model' do
|
3
2
|
let(:user) { create_new_user }
|
4
3
|
let(:crypted_password) { user.send User.sorcery_config.crypted_password_attribute_name }
|
5
4
|
|
6
|
-
describe
|
7
|
-
|
5
|
+
describe 'loaded plugin configuration' do
|
8
6
|
after(:each) { User.sorcery_config.reset! }
|
9
7
|
|
10
8
|
it "enables configuration option 'username_attribute_names'" do
|
@@ -56,8 +54,15 @@ shared_examples_for "rails_3_core_model" do
|
|
56
54
|
expect(User.sorcery_config.custom_encryption_provider).to eq Array
|
57
55
|
end
|
58
56
|
|
57
|
+
it "enables configuration option 'pepper'" do
|
58
|
+
pepper = '*$%&%*++'
|
59
|
+
sorcery_model_property_set(:pepper, pepper)
|
60
|
+
|
61
|
+
expect(User.sorcery_config.pepper).to eq pepper
|
62
|
+
end
|
63
|
+
|
59
64
|
it "enables configuration option 'salt_join_token'" do
|
60
|
-
salt_join_token =
|
65
|
+
salt_join_token = '--%%*&-'
|
61
66
|
sorcery_model_property_set(:salt_join_token, salt_join_token)
|
62
67
|
|
63
68
|
expect(User.sorcery_config.salt_join_token).to eq salt_join_token
|
@@ -70,101 +75,151 @@ shared_examples_for "rails_3_core_model" do
|
|
70
75
|
expect(User.sorcery_config.stretches).to eq stretches
|
71
76
|
end
|
72
77
|
|
78
|
+
it "enables configuration option 'deliver_later_enabled" do
|
79
|
+
sorcery_model_property_set(:email_delivery_method, :deliver_later)
|
80
|
+
expect(User.sorcery_config.email_delivery_method).to eq :deliver_later
|
81
|
+
end
|
82
|
+
|
73
83
|
it 'respond to username=' do
|
74
84
|
expect(User.new).to respond_to(:username=)
|
75
85
|
end
|
76
86
|
end
|
77
87
|
|
78
|
-
describe
|
88
|
+
describe 'when activated with sorcery' do
|
79
89
|
before(:all) { sorcery_reload! }
|
80
90
|
before(:each) { User.sorcery_adapter.delete_all }
|
81
91
|
|
82
|
-
it
|
92
|
+
it 'does not add authenticate method to base class', active_record: true do
|
83
93
|
expect(ActiveRecord::Base).not_to respond_to(:authenticate) if defined?(ActiveRecord)
|
84
94
|
end
|
85
95
|
|
86
|
-
it
|
96
|
+
it 'responds to class method authenticate' do
|
87
97
|
expect(User).to respond_to :authenticate
|
88
98
|
end
|
89
99
|
|
90
|
-
describe
|
91
|
-
it
|
92
|
-
expect(User.authenticate
|
100
|
+
describe '#authenticate' do
|
101
|
+
it 'returns user if credentials are good' do
|
102
|
+
expect(User.authenticate(user.email, 'secret')).to eq user
|
93
103
|
end
|
94
104
|
|
95
|
-
it
|
96
|
-
expect(User.authenticate
|
105
|
+
it 'returns nil if credentials are bad' do
|
106
|
+
expect(User.authenticate(user.email, 'wrong!')).to be nil
|
97
107
|
end
|
98
108
|
|
99
|
-
context
|
100
|
-
before do
|
101
|
-
sorcery_model_property_set(:downcase_username_before_authenticating, true)
|
102
|
-
end
|
103
|
-
|
109
|
+
context 'downcasing username' do
|
104
110
|
after do
|
105
111
|
sorcery_reload!
|
106
112
|
end
|
107
113
|
|
108
|
-
|
109
|
-
|
114
|
+
context 'when downcasing set to false' do
|
115
|
+
before do
|
116
|
+
sorcery_model_property_set(:downcase_username_before_authenticating, false)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'does not find user with wrongly capitalized username' do
|
120
|
+
expect(User.authenticate(user.email.capitalize, 'secret')).to be_nil
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'finds user with correctly capitalized username' do
|
124
|
+
expect(User.authenticate(user.email, 'secret')).to eq user
|
125
|
+
end
|
110
126
|
end
|
111
|
-
end
|
112
127
|
|
113
|
-
|
128
|
+
context 'when downcasing set to true' do
|
129
|
+
before do
|
130
|
+
sorcery_model_property_set(:downcase_username_before_authenticating, true)
|
131
|
+
end
|
114
132
|
|
115
|
-
|
133
|
+
it 'does not find user with wrongly capitalized username' do
|
134
|
+
expect(User.authenticate(user.email.capitalize, 'secret')).to eq user
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'finds user with correctly capitalized username' do
|
138
|
+
expect(User.authenticate(user.email, 'secret')).to eq user
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'and model implements active_for_authentication?' do
|
144
|
+
it 'authenticates returns user if active_for_authentication? returns true' do
|
116
145
|
allow_any_instance_of(User).to receive(:active_for_authentication?) { true }
|
117
146
|
|
118
|
-
expect(User.authenticate
|
147
|
+
expect(User.authenticate(user.email, 'secret')).to eq user
|
119
148
|
end
|
120
149
|
|
121
|
-
it
|
150
|
+
it 'authenticate returns nil if active_for_authentication? returns false' do
|
122
151
|
allow_any_instance_of(User).to receive(:active_for_authentication?) { false }
|
123
152
|
|
124
|
-
expect(User.authenticate
|
153
|
+
expect(User.authenticate(user.email, 'secret')).to be_nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'in block mode' do
|
158
|
+
it 'yields the user if credentials are good' do
|
159
|
+
User.authenticate(user.email, 'secret') do |user2, failure|
|
160
|
+
expect(user2).to eq user
|
161
|
+
expect(failure).to be_nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'yields the user and proper error if credentials are bad' do
|
166
|
+
User.authenticate(user.email, 'wrong!') do |user2, failure|
|
167
|
+
expect(user2).to eq user
|
168
|
+
expect(failure).to eq :invalid_password
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'yields the proper error if no user exists' do
|
173
|
+
[nil, '', 'not@a.user'].each do |email|
|
174
|
+
User.authenticate(email, 'wrong!') do |user2, failure|
|
175
|
+
expect(user2).to be_nil
|
176
|
+
expect(failure).to eq :invalid_login
|
177
|
+
end
|
178
|
+
end
|
125
179
|
end
|
126
180
|
end
|
127
181
|
end
|
128
182
|
|
129
183
|
specify { expect(User).to respond_to(:encrypt) }
|
130
184
|
|
131
|
-
it
|
132
|
-
sorcery_reload!([],
|
185
|
+
it 'subclass inherits config if defined so' do
|
186
|
+
sorcery_reload!([], subclasses_inherit_config: true)
|
133
187
|
class Admin < User; end
|
134
188
|
|
135
189
|
expect(Admin.sorcery_config).not_to be_nil
|
136
190
|
expect(Admin.sorcery_config).to eq User.sorcery_config
|
137
191
|
end
|
138
192
|
|
139
|
-
it
|
140
|
-
sorcery_reload!([],
|
193
|
+
it 'subclass does not inherit config if not defined so' do
|
194
|
+
sorcery_reload!([], subclasses_inherit_config: false)
|
141
195
|
class Admin2 < User; end
|
142
196
|
|
143
197
|
expect(Admin2.sorcery_config).to be_nil
|
144
198
|
end
|
145
199
|
end
|
146
200
|
|
147
|
-
|
148
|
-
describe "registration" do
|
149
|
-
|
201
|
+
describe 'registration' do
|
150
202
|
before(:all) { sorcery_reload! }
|
151
203
|
before(:each) { User.sorcery_adapter.delete_all }
|
152
204
|
|
153
|
-
it
|
205
|
+
it 'by default, encryption_provider is not nil' do
|
154
206
|
expect(User.sorcery_config.encryption_provider).not_to be_nil
|
155
207
|
end
|
156
208
|
|
157
|
-
it
|
158
|
-
expect(
|
209
|
+
it 'encrypts password when a new user is saved' do
|
210
|
+
expect(
|
211
|
+
User.sorcery_config.encryption_provider.matches?(crypted_password, 'secret', user.salt)
|
212
|
+
).to be true
|
159
213
|
end
|
160
214
|
|
161
|
-
it
|
215
|
+
it 'clears the virtual password field if the encryption process worked' do
|
162
216
|
expect(user.password).to be_nil
|
163
217
|
end
|
164
218
|
|
165
|
-
it
|
219
|
+
it 'does not clear the virtual password field if save failed due to validity' do
|
166
220
|
User.class_eval do
|
167
|
-
validates_format_of :email, :
|
221
|
+
validates_format_of :email, with: /\A(.)+@(.)+\Z/,
|
222
|
+
if: proc { |r| r.email }, message: 'is invalid'
|
168
223
|
end
|
169
224
|
|
170
225
|
user.password = 'blupush'
|
@@ -174,35 +229,42 @@ shared_examples_for "rails_3_core_model" do
|
|
174
229
|
expect(user.password).not_to be_nil
|
175
230
|
end
|
176
231
|
|
177
|
-
it
|
232
|
+
it 'does not clear the virtual password field if save failed due to exception' do
|
178
233
|
user.password = '4blupush'
|
179
234
|
user.username = nil
|
180
235
|
|
181
236
|
expect(user).to receive(:save) { raise RuntimeError }
|
182
237
|
|
238
|
+
# rubocop:disable Lint/HandleExceptions
|
183
239
|
begin
|
184
240
|
user.save
|
185
|
-
rescue
|
241
|
+
rescue RuntimeError
|
242
|
+
# Intentionally force exception during save
|
186
243
|
end
|
244
|
+
# rubocop:enable Lint/HandleExceptions
|
187
245
|
|
188
246
|
expect(user.password).not_to be_nil
|
189
247
|
end
|
190
248
|
|
191
|
-
it
|
192
|
-
user.email =
|
249
|
+
it 'does not encrypt the password twice when a user is updated' do
|
250
|
+
user.email = 'blup@bla.com'
|
193
251
|
user.save
|
194
252
|
|
195
|
-
expect(
|
253
|
+
expect(
|
254
|
+
User.sorcery_config.encryption_provider.matches?(crypted_password, 'secret', user.salt)
|
255
|
+
).to be true
|
196
256
|
end
|
197
257
|
|
198
|
-
it
|
258
|
+
it 'replaces the crypted_password in case a new password is set' do
|
199
259
|
user.password = 'new_secret'
|
200
260
|
user.save
|
201
261
|
|
202
|
-
expect(
|
262
|
+
expect(
|
263
|
+
User.sorcery_config.encryption_provider.matches?(crypted_password, 'secret', user.salt)
|
264
|
+
).to be false
|
203
265
|
end
|
204
266
|
|
205
|
-
describe
|
267
|
+
describe 'when user has password_confirmation_defined' do
|
206
268
|
before(:all) do
|
207
269
|
update_model { attr_accessor :password_confirmation }
|
208
270
|
end
|
@@ -212,17 +274,25 @@ shared_examples_for "rails_3_core_model" do
|
|
212
274
|
User.send(:remove_method, :password_confirmation=)
|
213
275
|
end
|
214
276
|
|
215
|
-
it
|
216
|
-
user = create_new_user(
|
277
|
+
it 'clears the virtual password field if the encryption process worked' do
|
278
|
+
user = create_new_user(
|
279
|
+
username: 'u',
|
280
|
+
password: 'secret', password_confirmation: 'secret',
|
281
|
+
email: 'email@example.com'
|
282
|
+
)
|
217
283
|
|
218
284
|
expect(user.password_confirmation).to be_nil
|
219
285
|
end
|
220
286
|
|
221
|
-
it
|
287
|
+
it 'does not clear the virtual password field if save failed due to validity' do
|
222
288
|
User.class_eval do
|
223
|
-
validates_format_of :email, :
|
289
|
+
validates_format_of :email, with: /\A(.)+@(.)+\Z/
|
224
290
|
end
|
225
|
-
user = build_new_user(
|
291
|
+
user = build_new_user(
|
292
|
+
username: 'u',
|
293
|
+
password: 'secret', password_confirmation: 'secret',
|
294
|
+
email: 'asd'
|
295
|
+
)
|
226
296
|
user.save
|
227
297
|
|
228
298
|
expect(user.password_confirmation).not_to be_nil
|
@@ -230,25 +300,93 @@ shared_examples_for "rails_3_core_model" do
|
|
230
300
|
end
|
231
301
|
end
|
232
302
|
|
233
|
-
describe
|
234
|
-
|
235
|
-
|
303
|
+
describe 'password validation' do
|
304
|
+
let(:user_with_pass) do
|
305
|
+
create_new_user(username: 'foo_bar', email: 'foo@bar.com', password: 'foobar')
|
306
|
+
end
|
236
307
|
|
237
308
|
specify { expect(user_with_pass).to respond_to :valid_password? }
|
238
|
-
|
239
|
-
it
|
240
|
-
expect(user_with_pass.valid_password?(
|
309
|
+
|
310
|
+
it 'returns true if password is correct' do
|
311
|
+
expect(user_with_pass.valid_password?('foobar')).to be true
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'returns false if password is incorrect' do
|
315
|
+
expect(user_with_pass.valid_password?('foobug')).to be false
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
describe 'generic send email' do
|
320
|
+
before(:all) do
|
321
|
+
MigrationHelper.migrate("#{Rails.root}/db/migrate/activation")
|
322
|
+
User.reset_column_information
|
241
323
|
end
|
242
|
-
|
243
|
-
|
244
|
-
|
324
|
+
|
325
|
+
after(:all) do
|
326
|
+
MigrationHelper.rollback("#{Rails.root}/db/migrate/activation")
|
327
|
+
end
|
328
|
+
|
329
|
+
before do
|
330
|
+
@mail = double('mail')
|
331
|
+
allow(::SorceryMailer).to receive(:activation_success_email).and_return(@mail)
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'use deliver_later' do
|
335
|
+
sorcery_reload!(
|
336
|
+
%i[
|
337
|
+
user_activation
|
338
|
+
user_activation_mailer
|
339
|
+
activation_needed_email_method_name
|
340
|
+
email_delivery_method
|
341
|
+
],
|
342
|
+
user_activation_mailer: SorceryMailer,
|
343
|
+
activation_needed_email_method_name: nil,
|
344
|
+
email_delivery_method: :deliver_later
|
345
|
+
)
|
346
|
+
|
347
|
+
expect(@mail).to receive(:deliver_later).once
|
348
|
+
user.activate!
|
349
|
+
end
|
350
|
+
|
351
|
+
describe 'email_delivery_method is default' do
|
352
|
+
it 'use deliver_now if rails version 4.2+' do
|
353
|
+
allow(Rails).to receive(:version).and_return('4.2.0')
|
354
|
+
sorcery_reload!(
|
355
|
+
%i[
|
356
|
+
user_activation
|
357
|
+
user_activation_mailer
|
358
|
+
activation_needed_email_method_name
|
359
|
+
],
|
360
|
+
user_activation_mailer: SorceryMailer,
|
361
|
+
activation_needed_email_method_name: nil
|
362
|
+
)
|
363
|
+
|
364
|
+
expect(@mail).to receive(:deliver_now).once
|
365
|
+
user.activate!
|
366
|
+
end
|
367
|
+
|
368
|
+
it 'use deliver if rails version < 4.2' do
|
369
|
+
allow(Rails).to receive(:version).and_return('4.1.0')
|
370
|
+
sorcery_reload!(
|
371
|
+
%i[
|
372
|
+
user_activation
|
373
|
+
user_activation_mailer
|
374
|
+
activation_needed_email_method_name
|
375
|
+
],
|
376
|
+
user_activation_mailer: SorceryMailer,
|
377
|
+
activation_needed_email_method_name: nil
|
378
|
+
)
|
379
|
+
|
380
|
+
expect(@mail).to receive(:deliver).once
|
381
|
+
user.activate!
|
382
|
+
end
|
245
383
|
end
|
246
384
|
end
|
247
385
|
|
248
|
-
describe
|
386
|
+
describe 'special encryption cases' do
|
249
387
|
before(:all) do
|
250
|
-
sorcery_reload!
|
251
|
-
@text =
|
388
|
+
sorcery_reload!
|
389
|
+
@text = 'Some Text!'
|
252
390
|
end
|
253
391
|
|
254
392
|
before(:each) do
|
@@ -259,20 +397,20 @@ shared_examples_for "rails_3_core_model" do
|
|
259
397
|
User.sorcery_config.reset!
|
260
398
|
end
|
261
399
|
|
262
|
-
it
|
400
|
+
it 'works with no password encryption' do
|
263
401
|
sorcery_model_property_set(:encryption_algorithm, :none)
|
264
402
|
username = user.send(User.sorcery_config.username_attribute_names.first)
|
265
403
|
|
266
|
-
expect(User.authenticate
|
404
|
+
expect(User.authenticate(username, 'secret')).to be_truthy
|
267
405
|
end
|
268
406
|
|
269
|
-
it
|
407
|
+
it 'works with custom password encryption' do
|
270
408
|
class MyCrypto
|
271
409
|
def self.encrypt(*tokens)
|
272
|
-
tokens.flatten.join('').
|
410
|
+
tokens.flatten.join('').tr('e', 'A')
|
273
411
|
end
|
274
412
|
|
275
|
-
def self.matches?(crypted
|
413
|
+
def self.matches?(crypted, *tokens)
|
276
414
|
crypted == encrypt(*tokens)
|
277
415
|
end
|
278
416
|
end
|
@@ -281,87 +419,143 @@ shared_examples_for "rails_3_core_model" do
|
|
281
419
|
|
282
420
|
username = user.send(User.sorcery_config.username_attribute_names.first)
|
283
421
|
|
284
|
-
expect(User.authenticate
|
422
|
+
expect(User.authenticate(username, 'secret')).to be_truthy
|
285
423
|
end
|
286
424
|
|
287
|
-
it
|
425
|
+
it 'if encryption algo is aes256, it sets key to crypto provider' do
|
288
426
|
sorcery_model_property_set(:encryption_algorithm, :aes256)
|
289
427
|
sorcery_model_property_set(:encryption_key, nil)
|
290
428
|
|
291
429
|
expect { User.encrypt @text }.to raise_error(ArgumentError)
|
292
430
|
|
293
|
-
sorcery_model_property_set(:encryption_key,
|
431
|
+
sorcery_model_property_set(:encryption_key, 'asd234dfs423fddsmndsflktsdf32343')
|
294
432
|
|
295
433
|
expect { User.encrypt @text }.not_to raise_error
|
296
434
|
end
|
297
435
|
|
298
|
-
it
|
436
|
+
it 'if encryption algo is aes256, it sets key to crypto provider, even if attributes are set in reverse' do
|
299
437
|
sorcery_model_property_set(:encryption_key, nil)
|
300
438
|
sorcery_model_property_set(:encryption_algorithm, :none)
|
301
|
-
sorcery_model_property_set(:encryption_key,
|
439
|
+
sorcery_model_property_set(:encryption_key, 'asd234dfs423fddsmndsflktsdf32343')
|
302
440
|
sorcery_model_property_set(:encryption_algorithm, :aes256)
|
303
441
|
|
304
442
|
expect { User.encrypt @text }.not_to raise_error
|
305
443
|
end
|
306
444
|
|
307
|
-
it
|
445
|
+
it 'if encryption algo is md5 it works' do
|
308
446
|
sorcery_model_property_set(:encryption_algorithm, :md5)
|
309
447
|
|
310
|
-
expect(User.encrypt
|
448
|
+
expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::MD5.encrypt(@text)
|
311
449
|
end
|
312
450
|
|
313
|
-
it
|
451
|
+
it 'if encryption algo is sha1 it works' do
|
314
452
|
sorcery_model_property_set(:encryption_algorithm, :sha1)
|
315
453
|
|
316
|
-
expect(User.encrypt
|
454
|
+
expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA1.encrypt(@text)
|
317
455
|
end
|
318
456
|
|
319
|
-
it
|
457
|
+
it 'if encryption algo is sha256 it works' do
|
320
458
|
sorcery_model_property_set(:encryption_algorithm, :sha256)
|
321
459
|
|
322
|
-
expect(User.encrypt
|
460
|
+
expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA256.encrypt(@text)
|
323
461
|
end
|
324
462
|
|
325
|
-
it
|
463
|
+
it 'if encryption algo is sha512 it works' do
|
326
464
|
sorcery_model_property_set(:encryption_algorithm, :sha512)
|
327
465
|
|
328
|
-
expect(User.encrypt
|
466
|
+
expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA512.encrypt(@text)
|
329
467
|
end
|
330
468
|
|
331
|
-
it
|
469
|
+
it 'if encryption algo is bcrypt it works' do
|
470
|
+
sorcery_model_property_set(:encryption_algorithm, :bcrypt)
|
471
|
+
|
472
|
+
# comparison is done using BCrypt::Password#==(raw_token), not by String#==
|
473
|
+
expect(User.encrypt(@text)).to be_an_instance_of BCrypt::Password
|
474
|
+
expect(User.encrypt(@text)).to eq @text
|
475
|
+
end
|
476
|
+
|
477
|
+
it 'salt is random for each user and saved in db' do
|
332
478
|
sorcery_model_property_set(:salt_attribute_name, :salt)
|
333
479
|
|
334
480
|
expect(user.salt).not_to be_nil
|
335
481
|
end
|
336
482
|
|
337
|
-
it
|
483
|
+
it 'if salt is set uses it to encrypt' do
|
338
484
|
sorcery_model_property_set(:salt_attribute_name, :salt)
|
339
485
|
sorcery_model_property_set(:encryption_algorithm, :sha512)
|
340
486
|
|
341
487
|
expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::SHA512.encrypt('secret')
|
342
|
-
expect(user.crypted_password).to eq Sorcery::CryptoProviders::SHA512.encrypt('secret',user.salt)
|
488
|
+
expect(user.crypted_password).to eq Sorcery::CryptoProviders::SHA512.encrypt('secret', user.salt)
|
343
489
|
end
|
344
490
|
|
345
|
-
it
|
491
|
+
it 'if salt_join_token is set uses it to encrypt' do
|
346
492
|
sorcery_model_property_set(:salt_attribute_name, :salt)
|
347
|
-
sorcery_model_property_set(:salt_join_token,
|
493
|
+
sorcery_model_property_set(:salt_join_token, '-@=>')
|
348
494
|
sorcery_model_property_set(:encryption_algorithm, :sha512)
|
349
495
|
|
350
496
|
expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::SHA512.encrypt('secret')
|
351
497
|
|
352
|
-
Sorcery::CryptoProviders::SHA512.join_token =
|
498
|
+
Sorcery::CryptoProviders::SHA512.join_token = ''
|
353
499
|
|
354
|
-
expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::SHA512.encrypt('secret',user.salt)
|
500
|
+
expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::SHA512.encrypt('secret', user.salt)
|
355
501
|
|
356
502
|
Sorcery::CryptoProviders::SHA512.join_token = User.sorcery_config.salt_join_token
|
357
503
|
|
358
|
-
expect(user.crypted_password).to eq Sorcery::CryptoProviders::SHA512.encrypt('secret',user.salt)
|
504
|
+
expect(user.crypted_password).to eq Sorcery::CryptoProviders::SHA512.encrypt('secret', user.salt)
|
505
|
+
end
|
506
|
+
|
507
|
+
it 'if pepper is set uses it to encrypt' do
|
508
|
+
sorcery_model_property_set(:salt_attribute_name, :salt)
|
509
|
+
sorcery_model_property_set(:pepper, '++@^$')
|
510
|
+
sorcery_model_property_set(:encryption_algorithm, :bcrypt)
|
511
|
+
|
512
|
+
# password comparison is done using BCrypt::Password#==(raw_token), not String#==
|
513
|
+
bcrypt_password = BCrypt::Password.new(user.crypted_password)
|
514
|
+
allow(::BCrypt::Password).to receive(:create) do |token, options = {}|
|
515
|
+
# need to use common BCrypt's salt when genarating BCrypt::Password objects
|
516
|
+
# so that any generated password hashes can be compared each other
|
517
|
+
::BCrypt::Engine.hash_secret(token, bcrypt_password.salt)
|
518
|
+
end
|
519
|
+
|
520
|
+
expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret')
|
521
|
+
|
522
|
+
Sorcery::CryptoProviders::BCrypt.pepper = ''
|
523
|
+
|
524
|
+
expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt)
|
525
|
+
|
526
|
+
Sorcery::CryptoProviders::BCrypt.pepper = User.sorcery_config.pepper
|
527
|
+
|
528
|
+
expect(user.crypted_password).to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt)
|
529
|
+
end
|
530
|
+
|
531
|
+
it 'if pepper is empty string (default) does not use pepper to encrypt' do
|
532
|
+
sorcery_model_property_set(:salt_attribute_name, :salt)
|
533
|
+
sorcery_model_property_set(:pepper, '')
|
534
|
+
sorcery_model_property_set(:encryption_algorithm, :bcrypt)
|
535
|
+
|
536
|
+
# password comparison is done using BCrypt::Password#==(raw_token), not String#==
|
537
|
+
bcrypt_password = BCrypt::Password.new(user.crypted_password)
|
538
|
+
allow(::BCrypt::Password).to receive(:create) do |token, options = {}|
|
539
|
+
# need to use common BCrypt's salt when genarating BCrypt::Password objects
|
540
|
+
# so that any generated password hashes can be compared each other
|
541
|
+
::BCrypt::Engine.hash_secret(token, bcrypt_password.salt)
|
542
|
+
end
|
543
|
+
|
544
|
+
expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret')
|
545
|
+
|
546
|
+
Sorcery::CryptoProviders::BCrypt.pepper = 'some_pepper'
|
547
|
+
|
548
|
+
expect(user.crypted_password).not_to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt)
|
549
|
+
|
550
|
+
Sorcery::CryptoProviders::BCrypt.pepper = User.sorcery_config.pepper
|
551
|
+
|
552
|
+
expect(user.crypted_password).to eq Sorcery::CryptoProviders::BCrypt.encrypt('secret', user.salt)
|
359
553
|
end
|
360
554
|
end
|
361
555
|
|
362
|
-
describe
|
556
|
+
describe 'ORM adapter' do
|
363
557
|
before(:all) do
|
364
|
-
sorcery_reload!
|
558
|
+
sorcery_reload!
|
365
559
|
User.sorcery_adapter.delete_all
|
366
560
|
end
|
367
561
|
|
@@ -372,29 +566,43 @@ shared_examples_for "rails_3_core_model" do
|
|
372
566
|
User.sorcery_config.reset!
|
373
567
|
end
|
374
568
|
|
375
|
-
|
376
|
-
it "find_by_username works as expected" do
|
569
|
+
it 'find_by_username works as expected' do
|
377
570
|
sorcery_model_property_set(:username_attribute_names, [:username])
|
378
571
|
|
379
|
-
expect(User.sorcery_adapter.find_by_username
|
572
|
+
expect(User.sorcery_adapter.find_by_username('gizmo')).to eq user
|
380
573
|
end
|
381
574
|
|
382
|
-
it
|
383
|
-
sorcery_model_property_set(:username_attribute_names, [
|
575
|
+
it 'find_by_username works as expected with multiple username attributes' do
|
576
|
+
sorcery_model_property_set(:username_attribute_names, %i[username email])
|
384
577
|
|
385
|
-
expect(User.sorcery_adapter.find_by_username
|
578
|
+
expect(User.sorcery_adapter.find_by_username('gizmo')).to eq user
|
386
579
|
end
|
387
580
|
|
388
|
-
it
|
389
|
-
expect(User.sorcery_adapter.find_by_email
|
581
|
+
it 'find_by_email works as expected' do
|
582
|
+
expect(User.sorcery_adapter.find_by_email('bla@bla.com')).to eq user
|
390
583
|
end
|
391
584
|
end
|
392
585
|
end
|
393
586
|
|
394
|
-
shared_examples_for
|
587
|
+
shared_examples_for 'external_user' do
|
395
588
|
let(:user) { create_new_user }
|
396
589
|
let(:external_user) { create_new_external_user :twitter }
|
397
590
|
|
591
|
+
before(:all) do
|
592
|
+
if SORCERY_ORM == :active_record
|
593
|
+
MigrationHelper.migrate("#{Rails.root}/db/migrate/external")
|
594
|
+
MigrationHelper.migrate("#{Rails.root}/db/migrate/activation")
|
595
|
+
end
|
596
|
+
sorcery_reload!
|
597
|
+
end
|
598
|
+
|
599
|
+
after(:all) do
|
600
|
+
if SORCERY_ORM == :active_record
|
601
|
+
MigrationHelper.rollback("#{Rails.root}/db/migrate/external")
|
602
|
+
MigrationHelper.rollback("#{Rails.root}/db/migrate/activation")
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
398
606
|
before(:each) do
|
399
607
|
User.sorcery_adapter.delete_all
|
400
608
|
end
|
@@ -403,85 +611,61 @@ shared_examples_for "external_user" do
|
|
403
611
|
expect(user).to respond_to(:external?)
|
404
612
|
end
|
405
613
|
|
406
|
-
it
|
614
|
+
it 'external? is false for regular users' do
|
407
615
|
expect(user.external?).to be false
|
408
616
|
end
|
409
617
|
|
410
|
-
it
|
618
|
+
it 'external? is true for external users' do
|
411
619
|
expect(external_user.external?).to be true
|
412
620
|
end
|
413
621
|
|
414
|
-
describe
|
415
|
-
|
416
|
-
before(:all) do
|
417
|
-
if SORCERY_ORM == :active_record
|
418
|
-
ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external")
|
419
|
-
User.reset_column_information
|
420
|
-
end
|
421
|
-
|
622
|
+
describe '.create_from_provider' do
|
623
|
+
before(:each) do
|
422
624
|
sorcery_reload!([:external])
|
423
|
-
|
424
|
-
|
425
|
-
after(:all) do
|
426
|
-
if SORCERY_ORM == :active_record
|
427
|
-
ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
|
428
|
-
end
|
625
|
+
sorcery_model_property_set(:authentications_class, Authentication)
|
429
626
|
end
|
430
627
|
|
431
628
|
it 'supports nested attributes' do
|
432
|
-
|
629
|
+
expect do
|
630
|
+
User.create_from_provider('facebook', '123', username: 'Noam Ben Ari')
|
631
|
+
end.to change { User.count }.by(1)
|
433
632
|
|
434
|
-
expect { User.create_from_provider('facebook', '123', {username: 'Noam Ben Ari'}) }.to change { User.count }.by(1)
|
435
633
|
expect(User.first.username).to eq 'Noam Ben Ari'
|
436
634
|
end
|
437
635
|
|
438
636
|
context 'with block' do
|
439
637
|
it 'create user when block return true' do
|
440
|
-
expect
|
441
|
-
User.create_from_provider('facebook', '123',
|
442
|
-
|
638
|
+
expect do
|
639
|
+
User.create_from_provider('facebook', '123', username: 'Noam Ben Ari') { true }
|
640
|
+
end.to change { User.count }.by(1)
|
443
641
|
end
|
444
642
|
|
445
643
|
it 'does not create user when block return false' do
|
446
|
-
expect
|
447
|
-
User.create_from_provider('facebook', '123',
|
448
|
-
|
644
|
+
expect do
|
645
|
+
User.create_from_provider('facebook', '123', username: 'Noam Ben Ari') { false }
|
646
|
+
end.not_to(change { User.count })
|
449
647
|
end
|
450
648
|
end
|
451
|
-
|
452
649
|
end
|
453
650
|
|
454
651
|
describe 'activation' do
|
455
|
-
before(:
|
456
|
-
|
457
|
-
ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/external")
|
458
|
-
ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate/activation")
|
459
|
-
end
|
460
|
-
|
461
|
-
sorcery_reload!([:user_activation,:external], :user_activation_mailer => ::SorceryMailer)
|
462
|
-
end
|
463
|
-
|
464
|
-
after(:all) do
|
465
|
-
if SORCERY_ORM == :active_record
|
466
|
-
ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/activation")
|
467
|
-
ActiveRecord::Migrator.rollback("#{Rails.root}/db/migrate/external")
|
468
|
-
end
|
652
|
+
before(:each) do
|
653
|
+
sorcery_reload!(%i[user_activation external], user_activation_mailer: ::SorceryMailer)
|
469
654
|
end
|
470
655
|
|
471
656
|
after(:each) do
|
472
657
|
User.sorcery_adapter.delete_all
|
473
658
|
end
|
474
659
|
|
475
|
-
[
|
476
|
-
|
477
|
-
it "does not send activation email to external users" do
|
660
|
+
%i[facebook github google liveid slack].each do |provider|
|
661
|
+
it 'does not send activation email to external users' do
|
478
662
|
old_size = ActionMailer::Base.deliveries.size
|
479
663
|
create_new_external_user(provider)
|
480
664
|
|
481
665
|
expect(ActionMailer::Base.deliveries.size).to eq old_size
|
482
666
|
end
|
483
667
|
|
484
|
-
it
|
668
|
+
it 'does not send external users an activation success email' do
|
485
669
|
sorcery_model_property_set(:activation_success_email_method_name, nil)
|
486
670
|
create_new_external_user(provider)
|
487
671
|
old_size = ActionMailer::Base.deliveries.size
|
@@ -490,6 +674,5 @@ shared_examples_for "external_user" do
|
|
490
674
|
expect(ActionMailer::Base.deliveries.size).to eq old_size
|
491
675
|
end
|
492
676
|
end
|
493
|
-
|
494
677
|
end
|
495
678
|
end
|