sorcery 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sorcery might be problematic. Click here for more details.

Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -26
  3. data/CHANGELOG.md +13 -0
  4. data/Gemfile +1 -1
  5. data/README.md +2 -1
  6. data/lib/generators/sorcery/templates/initializer.rb +85 -85
  7. data/lib/generators/sorcery/templates/migration/activity_logging.rb +4 -4
  8. data/lib/generators/sorcery/templates/migration/brute_force_protection.rb +3 -3
  9. data/lib/generators/sorcery/templates/migration/core.rb +2 -2
  10. data/lib/generators/sorcery/templates/migration/external.rb +3 -3
  11. data/lib/generators/sorcery/templates/migration/magic_login.rb +3 -3
  12. data/lib/generators/sorcery/templates/migration/remember_me.rb +2 -2
  13. data/lib/generators/sorcery/templates/migration/reset_password.rb +4 -4
  14. data/lib/generators/sorcery/templates/migration/user_activation.rb +3 -3
  15. data/lib/sorcery/controller/submodules/activity_logging.rb +10 -3
  16. data/lib/sorcery/controller/submodules/brute_force_protection.rb +7 -3
  17. data/lib/sorcery/controller/submodules/external.rb +1 -0
  18. data/lib/sorcery/controller/submodules/http_basic_auth.rb +4 -1
  19. data/lib/sorcery/controller/submodules/remember_me.rb +7 -2
  20. data/lib/sorcery/controller/submodules/session_timeout.rb +7 -2
  21. data/lib/sorcery/crypto_providers/aes256.rb +1 -1
  22. data/lib/sorcery/crypto_providers/bcrypt.rb +6 -1
  23. data/lib/sorcery/model.rb +1 -0
  24. data/lib/sorcery/model/config.rb +5 -0
  25. data/lib/sorcery/model/submodules/magic_login.rb +7 -4
  26. data/lib/sorcery/model/submodules/reset_password.rb +6 -2
  27. data/lib/sorcery/providers/line.rb +47 -0
  28. data/lib/sorcery/providers/linkedin.rb +20 -36
  29. data/lib/sorcery/version.rb +1 -1
  30. data/spec/controllers/controller_oauth2_spec.rb +8 -0
  31. data/spec/rails_app/app/controllers/sorcery_controller.rb +20 -0
  32. data/spec/rails_app/config/routes.rb +3 -0
  33. data/spec/shared_examples/user_reset_password_shared_examples.rb +18 -2
  34. data/spec/shared_examples/user_shared_examples.rb +63 -0
  35. data/spec/sorcery_crypto_providers_spec.rb +60 -0
  36. metadata +3 -5
  37. data/gemfiles/active_record_rails_40.gemfile +0 -6
  38. data/gemfiles/active_record_rails_41.gemfile +0 -6
  39. data/gemfiles/active_record_rails_42.gemfile +0 -6
@@ -216,6 +216,7 @@ describe SorceryController, active_record: true, type: :controller do
216
216
  microsoft
217
217
  instagram
218
218
  auth0
219
+ line
219
220
  ]
220
221
  )
221
222
 
@@ -257,6 +258,9 @@ describe SorceryController, active_record: true, type: :controller do
257
258
  sorcery_controller_external_property_set(:auth0, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8')
258
259
  sorcery_controller_external_property_set(:auth0, :callback_url, 'http://blabla.com')
259
260
  sorcery_controller_external_property_set(:auth0, :site, 'https://sorcery-test.auth0.com')
261
+ sorcery_controller_external_property_set(:line, :key, "eYVNBjBDi33aa9GkA3w")
262
+ sorcery_controller_external_property_set(:line, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
263
+ sorcery_controller_external_property_set(:line, :callback_url, "http://blabla.com")
260
264
  end
261
265
 
262
266
  after(:each) do
@@ -474,6 +478,7 @@ describe SorceryController, active_record: true, type: :controller do
474
478
  microsoft
475
479
  instagram
476
480
  auth0
481
+ line
477
482
  ]
478
483
  )
479
484
  sorcery_controller_external_property_set(:facebook, :key, 'eYVNBjBDi33aa9GkA3w')
@@ -513,6 +518,9 @@ describe SorceryController, active_record: true, type: :controller do
513
518
  sorcery_controller_external_property_set(:auth0, :secret, 'XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8')
514
519
  sorcery_controller_external_property_set(:auth0, :callback_url, 'http://blabla.com')
515
520
  sorcery_controller_external_property_set(:auth0, :site, 'https://sorcery-test.auth0.com')
521
+ sorcery_controller_external_property_set(:line, :key, "eYVNBjBDi33aa9GkA3w")
522
+ sorcery_controller_external_property_set(:line, :secret, "XpbeSdCoaKSmQGSeokz5qcUATClRW5u08QWNfv71N8")
523
+ sorcery_controller_external_property_set(:line, :callback_url, "http://blabla.com")
516
524
  end
517
525
 
518
526
  def provider_url(provider)
@@ -142,6 +142,10 @@ class SorceryController < ActionController::Base
142
142
  login_at(:slack)
143
143
  end
144
144
 
145
+ def login_at_test_line
146
+ login_at(:line)
147
+ end
148
+
145
149
  def login_at_test_with_state
146
150
  login_at(:facebook, state: 'bla')
147
151
  end
@@ -268,6 +272,14 @@ class SorceryController < ActionController::Base
268
272
  end
269
273
  end
270
274
 
275
+ def test_login_from_line
276
+ if @user = login_from(:line)
277
+ redirect_to 'bla', notice: 'Success!'
278
+ else
279
+ redirect_to 'blu', alert: 'Failed!'
280
+ end
281
+ end
282
+
271
283
  def test_return_to_with_external_twitter
272
284
  if (@user = login_from(:twitter))
273
285
  redirect_back_or_to 'bla', notice: 'Success!'
@@ -382,6 +394,14 @@ class SorceryController < ActionController::Base
382
394
  end
383
395
  end
384
396
 
397
+ def test_return_to_with_external_line
398
+ if @user = login_from(:line)
399
+ redirect_back_or_to 'bla', notice: 'Success!'
400
+ else
401
+ redirect_to 'blu', alert: 'Failed!'
402
+ end
403
+ end
404
+
385
405
  def test_create_from_provider
386
406
  provider = params[:provider]
387
407
  login_from(provider)
@@ -32,6 +32,7 @@ AppRoot::Application.routes.draw do
32
32
  get :test_login_from_slack
33
33
  get :test_login_from_instagram
34
34
  get :test_login_from_auth0
35
+ get :test_login_from_line
35
36
  get :login_at_test
36
37
  get :login_at_test_twitter
37
38
  get :login_at_test_facebook
@@ -47,6 +48,7 @@ AppRoot::Application.routes.draw do
47
48
  get :login_at_test_slack
48
49
  get :login_at_test_instagram
49
50
  get :login_at_test_auth0
51
+ get :login_at_test_line
50
52
  get :test_return_to_with_external
51
53
  get :test_return_to_with_external_twitter
52
54
  get :test_return_to_with_external_facebook
@@ -62,6 +64,7 @@ AppRoot::Application.routes.draw do
62
64
  get :test_return_to_with_external_slack
63
65
  get :test_return_to_with_external_instagram
64
66
  get :test_return_to_with_external_auth0
67
+ get :test_return_to_with_external_line
65
68
  get :test_http_basic_auth
66
69
  get :some_action_making_a_non_persisted_change_to_the_user
67
70
  post :test_login_with_remember
@@ -14,6 +14,8 @@ shared_examples_for 'rails_3_reset_password_model' do
14
14
  context 'API' do
15
15
  specify { expect(user).to respond_to :deliver_reset_password_instructions! }
16
16
 
17
+ specify { expect(user).to respond_to :change_password }
18
+
17
19
  specify { expect(user).to respond_to :change_password! }
18
20
 
19
21
  it 'responds to .load_from_reset_password_token' do
@@ -314,13 +316,27 @@ shared_examples_for 'rails_3_reset_password_model' do
314
316
  end
315
317
  end
316
318
 
317
- it 'when change_password! is called, deletes reset_password_token' do
319
+ it 'when change_password! is called, deletes reset_password_token and calls #save!' do
318
320
  user.deliver_reset_password_instructions!
319
321
 
320
322
  expect(user.reset_password_token).not_to be_nil
323
+ expect(user).to_not receive(:save)
324
+ expect(user).to receive(:save!)
321
325
 
322
326
  user.change_password!('blabulsdf')
323
- user.save!
327
+
328
+ expect(user.reset_password_token).to be_nil
329
+ end
330
+
331
+ it 'when change_password is called, deletes reset_password_token and calls #save' do
332
+ new_password = 'blabulsdf'
333
+
334
+ user.deliver_reset_password_instructions!
335
+ expect(user.reset_password_token).not_to be_nil
336
+ expect(user).to_not receive(:save!)
337
+ expect(user).to receive(:save)
338
+
339
+ user.change_password(new_password)
324
340
 
325
341
  expect(user.reset_password_token).to be_nil
326
342
  end
@@ -54,6 +54,13 @@ shared_examples_for 'rails_3_core_model' do
54
54
  expect(User.sorcery_config.custom_encryption_provider).to eq Array
55
55
  end
56
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
+
57
64
  it "enables configuration option 'salt_join_token'" do
58
65
  salt_join_token = '--%%*&-'
59
66
  sorcery_model_property_set(:salt_join_token, salt_join_token)
@@ -459,6 +466,14 @@ shared_examples_for 'rails_3_core_model' do
459
466
  expect(User.encrypt(@text)).to eq Sorcery::CryptoProviders::SHA512.encrypt(@text)
460
467
  end
461
468
 
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
+
462
477
  it 'salt is random for each user and saved in db' do
463
478
  sorcery_model_property_set(:salt_attribute_name, :salt)
464
479
 
@@ -488,6 +503,54 @@ shared_examples_for 'rails_3_core_model' do
488
503
 
489
504
  expect(user.crypted_password).to eq Sorcery::CryptoProviders::SHA512.encrypt('secret', user.salt)
490
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, cost:|
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, cost:|
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)
553
+ end
491
554
  end
492
555
 
493
556
  describe 'ORM adapter' do
@@ -148,6 +148,7 @@ describe 'Crypto Providers wrappers' do
148
148
  before(:all) do
149
149
  Sorcery::CryptoProviders::BCrypt.cost = 1
150
150
  @digest = BCrypt::Password.create('Noam Ben-Ari', cost: Sorcery::CryptoProviders::BCrypt.cost)
151
+ @tokens = %w[password gq18WBnJYNh2arkC1kgH]
151
152
  end
152
153
 
153
154
  after(:each) do
@@ -181,5 +182,64 @@ describe 'Crypto Providers wrappers' do
181
182
  # stubbed in Sorcery::TestHelpers::Internal
182
183
  expect(Sorcery::CryptoProviders::BCrypt.cost).to eq 1
183
184
  end
185
+
186
+ it 'matches token encrypted with salt from upstream' do
187
+ # note: actual comparison is done by BCrypt::Password#==(raw_token)
188
+ expect(Sorcery::CryptoProviders::BCrypt.encrypt(@tokens)).to eq @tokens.flatten.join
189
+ end
190
+
191
+ it 'respond_to?(:pepper) returns true' do
192
+ expect(Sorcery::CryptoProviders::BCrypt.respond_to?(:pepper)).to be true
193
+ end
194
+
195
+ context 'when pepper is provided' do
196
+ before(:each) do
197
+ Sorcery::CryptoProviders::BCrypt.pepper = 'pepper'
198
+ @digest = Sorcery::CryptoProviders::BCrypt.encrypt(@tokens) # a BCrypt::Password object
199
+ end
200
+
201
+ it 'matches token encrypted with salt and pepper from upstream' do
202
+ # note: actual comparison is done by BCrypt::Password#==(raw_token)
203
+ expect(@digest).to eq @tokens.flatten.join.concat('pepper')
204
+ end
205
+
206
+ it 'matches? returns true when matches' do
207
+ expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be true
208
+ end
209
+
210
+ it 'matches? returns false when pepper is replaced with empty string' do
211
+ Sorcery::CryptoProviders::BCrypt.pepper = ''
212
+ expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be false
213
+ end
214
+
215
+ it 'matches? returns false when no match' do
216
+ expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, 'a_random_incorrect_password')).to be false
217
+ end
218
+ end
219
+
220
+ context "when pepper is an empty string (default)" do
221
+ before(:each) do
222
+ Sorcery::CryptoProviders::BCrypt.pepper = ''
223
+ @digest = Sorcery::CryptoProviders::BCrypt.encrypt(@tokens) # a BCrypt::Password object
224
+ end
225
+
226
+ # make sure the default pepper '' does nothing
227
+ it 'matches token encrypted with salt only (without pepper)' do
228
+ expect(@digest).to eq @tokens.flatten.join # keep consistency with the older versions of #join_token
229
+ end
230
+
231
+ it 'matches? returns true when matches' do
232
+ expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be true
233
+ end
234
+
235
+ it 'matches? returns false when pepper has changed' do
236
+ Sorcery::CryptoProviders::BCrypt.pepper = 'a new pepper'
237
+ expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, *@tokens)).to be false
238
+ end
239
+
240
+ it 'matches? returns false when no match' do
241
+ expect(Sorcery::CryptoProviders::BCrypt.matches?(@digest, 'a_random_incorrect_password')).to be false
242
+ end
243
+ end
184
244
  end
185
245
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorcery
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Noam Ben Ari
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2018-11-29 00:00:00.000000000 Z
15
+ date: 2019-05-23 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: bcrypt
@@ -207,9 +207,6 @@ files:
207
207
  - LICENSE.md
208
208
  - README.md
209
209
  - Rakefile
210
- - gemfiles/active_record_rails_40.gemfile
211
- - gemfiles/active_record_rails_41.gemfile
212
- - gemfiles/active_record_rails_42.gemfile
213
210
  - lib/generators/sorcery/USAGE
214
211
  - lib/generators/sorcery/helpers.rb
215
212
  - lib/generators/sorcery/install_generator.rb
@@ -263,6 +260,7 @@ files:
263
260
  - lib/sorcery/providers/heroku.rb
264
261
  - lib/sorcery/providers/instagram.rb
265
262
  - lib/sorcery/providers/jira.rb
263
+ - lib/sorcery/providers/line.rb
266
264
  - lib/sorcery/providers/linkedin.rb
267
265
  - lib/sorcery/providers/liveid.rb
268
266
  - lib/sorcery/providers/microsoft.rb
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'rails', '~> 4.0.1'
4
- gem 'sqlite3', platform: :mri
5
-
6
- gemspec path: '..'
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'rails', '~> 4.1.0'
4
- gem 'sqlite3', platform: :mri
5
-
6
- gemspec path: '..'
@@ -1,6 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'rails', '~> 4.2.0'
4
- gem 'sqlite3', platform: :mri
5
-
6
- gemspec path: '..'