devise-security 0.14.0 → 0.16.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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +124 -60
  3. data/app/controllers/devise/password_expired_controller.rb +11 -6
  4. data/app/views/devise/paranoid_verification_code/show.html.erb +3 -3
  5. data/app/views/devise/password_expired/show.html.erb +5 -5
  6. data/config/locales/by.yml +49 -0
  7. data/config/locales/cs.yml +41 -0
  8. data/config/locales/de.yml +13 -2
  9. data/config/locales/en.yml +13 -1
  10. data/config/locales/es.yml +10 -9
  11. data/config/locales/fa.yml +41 -0
  12. data/config/locales/fr.yml +1 -0
  13. data/config/locales/hi.yml +42 -0
  14. data/config/locales/it.yml +35 -4
  15. data/config/locales/ja.yml +2 -1
  16. data/config/locales/nl.yml +41 -0
  17. data/config/locales/pt.yml +41 -0
  18. data/config/locales/ru.yml +49 -0
  19. data/config/locales/tr.yml +1 -0
  20. data/config/locales/uk.yml +49 -0
  21. data/config/locales/zh_CN.yml +41 -0
  22. data/config/locales/zh_TW.yml +41 -0
  23. data/lib/devise-security.rb +7 -3
  24. data/lib/devise-security/controllers/helpers.rb +59 -50
  25. data/lib/devise-security/hooks/password_expirable.rb +2 -0
  26. data/lib/devise-security/hooks/session_limitable.rb +29 -14
  27. data/lib/devise-security/models/compatibility.rb +2 -2
  28. data/lib/devise-security/models/compatibility/{active_record.rb → active_record_patch.rb} +12 -1
  29. data/lib/devise-security/models/compatibility/{mongoid.rb → mongoid_patch.rb} +11 -1
  30. data/lib/devise-security/models/password_expirable.rb +5 -1
  31. data/lib/devise-security/models/secure_validatable.rb +15 -1
  32. data/lib/devise-security/models/session_limitable.rb +17 -2
  33. data/lib/devise-security/validators/password_complexity_validator.rb +4 -2
  34. data/lib/devise-security/version.rb +1 -1
  35. data/lib/generators/devise_security/install_generator.rb +3 -3
  36. data/lib/generators/templates/devise_security.rb +47 -0
  37. data/test/{test_captcha_controller.rb → controllers/test_captcha_controller.rb} +0 -0
  38. data/test/controllers/test_password_expired_controller.rb +110 -0
  39. data/test/{test_security_question_controller.rb → controllers/test_security_question_controller.rb} +16 -40
  40. data/test/dummy/app/assets/config/manifest.js +3 -0
  41. data/test/dummy/app/controllers/widgets_controller.rb +6 -0
  42. data/test/dummy/app/models/user.rb +8 -0
  43. data/test/dummy/config/application.rb +1 -0
  44. data/test/dummy/config/environments/test.rb +3 -13
  45. data/test/dummy/config/initializers/migration_class.rb +1 -8
  46. data/test/dummy/config/mongoid.yml +1 -1
  47. data/test/dummy/config/routes.rb +4 -3
  48. data/test/dummy/db/migrate/20120508165529_create_tables.rb +10 -1
  49. data/test/dummy/log/development.log +883 -0
  50. data/test/dummy/log/test.log +21689 -0
  51. data/test/integration/test_password_expirable_workflow.rb +53 -0
  52. data/test/integration/test_session_limitable_workflow.rb +67 -0
  53. data/test/orm/active_record.rb +4 -1
  54. data/test/orm/mongoid.rb +2 -1
  55. data/test/support/integration_helpers.rb +29 -0
  56. data/test/support/mongoid.yml +1 -1
  57. data/test/test_compatibility.rb +13 -0
  58. data/test/test_complexity_validator.rb +12 -0
  59. data/test/test_helper.rb +21 -6
  60. data/test/test_install_generator.rb +11 -1
  61. data/test/test_secure_validatable.rb +76 -0
  62. data/test/test_session_limitable.rb +57 -0
  63. data/{lib/generators/templates → test/tmp/config/initializers}/devise-security.rb +3 -0
  64. data/test/tmp/config/locales/devise.security_extension.by.yml +49 -0
  65. data/test/tmp/config/locales/devise.security_extension.cs.yml +41 -0
  66. data/test/tmp/config/locales/devise.security_extension.de.yml +39 -0
  67. data/test/tmp/config/locales/devise.security_extension.en.yml +41 -0
  68. data/test/tmp/config/locales/devise.security_extension.es.yml +30 -0
  69. data/test/tmp/config/locales/devise.security_extension.fa.yml +41 -0
  70. data/test/tmp/config/locales/devise.security_extension.fr.yml +30 -0
  71. data/test/tmp/config/locales/devise.security_extension.hi.yml +42 -0
  72. data/test/tmp/config/locales/devise.security_extension.it.yml +41 -0
  73. data/test/tmp/config/locales/devise.security_extension.ja.yml +30 -0
  74. data/test/tmp/config/locales/devise.security_extension.nl.yml +41 -0
  75. data/test/tmp/config/locales/devise.security_extension.pt.yml +41 -0
  76. data/test/tmp/config/locales/devise.security_extension.ru.yml +49 -0
  77. data/test/tmp/config/locales/devise.security_extension.tr.yml +18 -0
  78. data/test/tmp/config/locales/devise.security_extension.uk.yml +49 -0
  79. data/test/tmp/config/locales/devise.security_extension.zh_CN.yml +41 -0
  80. data/test/tmp/config/locales/devise.security_extension.zh_TW.yml +41 -0
  81. metadata +156 -133
  82. data/.codeclimate.yml +0 -63
  83. data/.document +0 -5
  84. data/.gitignore +0 -43
  85. data/.mdlrc +0 -1
  86. data/.rubocop.yml +0 -64
  87. data/.ruby-version +0 -1
  88. data/.travis.yml +0 -41
  89. data/Appraisals +0 -35
  90. data/Gemfile +0 -10
  91. data/Rakefile +0 -27
  92. data/devise-security.gemspec +0 -50
  93. data/gemfiles/rails_4.2_stable.gemfile +0 -16
  94. data/gemfiles/rails_5.0_stable.gemfile +0 -15
  95. data/gemfiles/rails_5.1_stable.gemfile +0 -15
  96. data/gemfiles/rails_5.2_stable.gemfile +0 -15
  97. data/gemfiles/rails_6.0_beta.gemfile +0 -15
  98. data/lib/devise-security/orm/active_record.rb +0 -20
  99. data/lib/devise-security/schema.rb +0 -66
  100. data/test/dummy/app/models/.gitkeep +0 -0
  101. data/test/test_password_expired_controller.rb +0 -46
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestPasswordExpirableWorkflow < ActionDispatch::IntegrationTest
6
+ include IntegrationHelpers
7
+
8
+ setup do
9
+ @user = User.create!(password: 'passWord1',
10
+ password_confirmation: 'passWord1',
11
+ email: 'bob@microsoft.com',
12
+ password_changed_at: 4.months.ago) # the default expiration time is 3.months.ago
13
+ @user.confirm
14
+
15
+ assert @user.valid?
16
+ assert @user.need_change_password?
17
+ end
18
+
19
+ test 'sign in and change expired password' do
20
+ sign_in(@user)
21
+ assert_redirected_to(root_path)
22
+ follow_redirect!
23
+ assert_redirected_to(user_password_expired_path)
24
+ # @note This is not the same controller used by Devise for password changes
25
+ put '/users/password_expired', params: {
26
+ user: {
27
+ current_password: 'passWord1',
28
+ password: 'Password12345!',
29
+ password_confirmation: 'Password12345!',
30
+ },
31
+ }
32
+ assert_redirected_to(root_path)
33
+ @user.reload
34
+ assert_not @user.need_change_password?
35
+ end
36
+
37
+ test 'sign in and password is updated before redirect completes' do
38
+ sign_in(@user)
39
+ assert_redirected_to(root_path)
40
+
41
+ # simulates an external process updating the password
42
+ @user.update(password_changed_at: Time.zone.now)
43
+ assert_not @user.need_change_password?
44
+
45
+ follow_redirect!
46
+ assert_response :success
47
+
48
+ # if the password is expired at this point they will be redirected to the
49
+ # password change controller.
50
+ get root_path
51
+ assert_response :success
52
+ end
53
+ end
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ class TestSessionLimitableWorkflow < ActionDispatch::IntegrationTest
4
+ include IntegrationHelpers
5
+
6
+ setup do
7
+ @user = User.create!(password: 'passWord1',
8
+ password_confirmation: 'passWord1',
9
+ email: 'bob@microsoft.com')
10
+ @user.confirm
11
+ end
12
+
13
+ test 'failed login' do
14
+ assert_nil @user.unique_session_id
15
+
16
+ open_session do |session|
17
+ failed_sign_in(@user, session)
18
+ session.assert_response(:success)
19
+ assert_equal session.flash[:alert], I18n.t('devise.failure.invalid', authentication_keys: 'Email')
20
+ assert_nil @user.reload.unique_session_id
21
+ end
22
+ end
23
+
24
+ test 'successful login' do
25
+ assert_nil @user.unique_session_id
26
+
27
+ open_session do |session|
28
+ sign_in(@user, session)
29
+ session.assert_redirected_to '/'
30
+ session.get widgets_path
31
+ session.assert_response(:success)
32
+ assert_equal session.response.body, 'success'
33
+ assert_not_nil @user.reload.unique_session_id
34
+ end
35
+ end
36
+
37
+ test 'session is logged out when another session is created' do
38
+ first_session = open_session
39
+ second_session = open_session
40
+ unique_session_id = nil
41
+
42
+ first_session.tap do |session|
43
+ sign_in(@user, session)
44
+ session.assert_redirected_to '/'
45
+ session.get widgets_path
46
+ session.assert_response(:success)
47
+ assert_equal session.response.body, 'success'
48
+ unique_session_id = @user.reload.unique_session_id
49
+ assert_not_nil unique_session_id
50
+ end
51
+
52
+ second_session.tap do |session|
53
+ sign_in(@user, session)
54
+ session.assert_redirected_to '/'
55
+ session.get widgets_path
56
+ session.assert_response(:success)
57
+ assert_equal session.response.body, 'success'
58
+ assert_not_equal unique_session_id, @user.reload.unique_session_id
59
+ end
60
+
61
+ first_session.tap do |session|
62
+ session.get widgets_path
63
+ session.assert_redirected_to new_user_session_path
64
+ assert_equal session.flash[:alert], I18n.t('devise.failure.session_limited')
65
+ end
66
+ end
67
+ end
@@ -2,7 +2,10 @@ require 'active_record'
2
2
 
3
3
  ActiveRecord::Migration.verbose = false
4
4
  ActiveRecord::Base.logger = Logger.new(nil)
5
- if Rails.gem_version >= Gem::Version.new('5.2.0')
5
+ case
6
+ when Rails.gem_version >= Gem::Version.new('6.0.0')
7
+ ActiveRecord::MigrationContext.new(File.expand_path('../../dummy/db/migrate', __FILE__), ActiveRecord::SchemaMigration).migrate
8
+ when Rails.gem_version >= Gem::Version.new('5.2.0')
6
9
  ActiveRecord::MigrationContext.new(File.expand_path('../../dummy/db/migrate', __FILE__)).migrate
7
10
  else
8
11
  ActiveRecord::Migrator.migrate(File.expand_path('../../dummy/db/migrate', __FILE__))
data/test/orm/mongoid.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'mongoid/version'
4
+ require 'database_cleaner-mongoid'
4
5
 
5
6
  Mongoid.configure do |config|
6
7
  config.load!('test/support/mongoid.yml', Rails.env)
@@ -8,5 +9,5 @@ Mongoid.configure do |config|
8
9
  config.include_root_in_json = true
9
10
  end
10
11
 
11
- DatabaseCleaner[:mongoid].strategy = :truncation
12
+ DatabaseCleaner[:mongoid].strategy = :deletion
12
13
  ORMInvalidRecordException = Mongoid::Errors::Validations
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IntegrationHelpers
4
+ # login the user. This will exercise all the Warden Hooks
5
+ # @param user [User]
6
+ # @param session [ActionDispatch::Integration::Session]
7
+ # @return [void]
8
+ def sign_in(user, session = integration_session)
9
+ session.post new_user_session_path, params: {
10
+ user: {
11
+ email: user.email,
12
+ password: user.password,
13
+ },
14
+ }
15
+ end
16
+
17
+ # attempt to login the user with a bad password. This will exercise all the Warden Hooks
18
+ # @param user [User]
19
+ # @param session [ActionDispatch::Integration::Session]
20
+ # @return [void]
21
+ def failed_sign_in(user, session)
22
+ session.post new_user_session_path, params: {
23
+ user: {
24
+ email: user.email,
25
+ password: 'bad-password',
26
+ },
27
+ }
28
+ end
29
+ end
@@ -1,5 +1,5 @@
1
1
  test:
2
- <%= Mongoid::VERSION.to_i > 4 ? 'clients' : 'sessions' %>:
2
+ clients:
3
3
  default:
4
4
  database: devise-test-suite
5
5
  hosts:
@@ -0,0 +1,13 @@
1
+ require 'test_helper'
2
+
3
+ class TestCompatibility < ActiveSupport::TestCase
4
+ test 'can access ActiveRecord namespace' do
5
+ skip unless DEVISE_ORM == :active_record
6
+ assert_nothing_raised { User.new.some_method_calling_active_record }
7
+ end
8
+
9
+ test 'can access Mongoid namespace' do
10
+ skip unless DEVISE_ORM == :mongoid
11
+ assert_nothing_raised { User.new.some_method_calling_mongoid }
12
+ end
13
+ end
@@ -37,6 +37,12 @@ class PasswordComplexityValidatorTest < Minitest::Test
37
37
  assert(ModelWithPassword.new('aaa1').valid?)
38
38
  end
39
39
 
40
+ def test_enforces_digits
41
+ ModelWithPassword.validates :password, 'devise_security/password_complexity': { digits: 2 }
42
+ refute(ModelWithPassword.new('aaa1').valid?)
43
+ assert(ModelWithPassword.new('aa12').valid?)
44
+ end
45
+
40
46
  def test_enforces_lower
41
47
  ModelWithPassword.validates :password, 'devise_security/password_complexity': { lower: 1 }
42
48
  refute(ModelWithPassword.new('AAAA').valid?)
@@ -49,6 +55,12 @@ class PasswordComplexityValidatorTest < Minitest::Test
49
55
  assert(ModelWithPassword.new('aaa!').valid?)
50
56
  end
51
57
 
58
+ def test_enforces_symbols
59
+ ModelWithPassword.validates :password, 'devise_security/password_complexity': { symbols: 2 }
60
+ refute(ModelWithPassword.new('aaa!').valid?)
61
+ assert(ModelWithPassword.new('aa!?').valid?)
62
+ end
63
+
52
64
  def test_enforces_combination
53
65
  ModelWithPassword.validates :password, 'devise_security/password_complexity': { lower: 1, upper: 1, digit: 1, symbol: 1 }
54
66
  refute(ModelWithPassword.new('abcd').valid?)
data/test/test_helper.rb CHANGED
@@ -1,20 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  ENV['RAILS_ENV'] ||= 'test'
4
- DEVISE_ORM = ENV.fetch('DEVISE_ORM', 'active_record').to_sym
5
4
 
6
5
  require 'simplecov'
7
6
  SimpleCov.start do
8
7
  add_filter 'gemfiles'
8
+ add_filter 'test/dummy/db'
9
+ add_group 'ActiveRecord', 'active_record'
10
+ add_group 'Expirable', /(?<!password_)expirable/
11
+ add_group 'Mongoid', 'mongoid'
12
+ add_group 'Paranoid Verifiable', 'paranoid_verification'
13
+ add_group 'Password Archivable', /password_archivable|old_password/
14
+ add_group 'Password Expirable', /password_expirable|password_expired/
15
+ add_group 'Secure Validateable', 'secure_validatable'
16
+ add_group 'Security Questionable', 'security_question'
17
+ add_group 'Session Limitable', 'session_limitable'
9
18
  add_group 'Tests', 'test'
10
- add_group 'Password Archivable', 'password_archivable'
11
- add_group 'Password Expirable', 'password_expirable'
12
19
  end
13
20
 
14
21
  if ENV['CI']
15
- require 'coveralls'
16
- SimpleCov.formatter = Coveralls::SimpleCov::Formatter
17
- Coveralls.wear!
22
+ require 'simplecov'
23
+ require 'simplecov-lcov'
24
+ SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
25
+ SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
26
+ SimpleCov.start
18
27
  end
19
28
 
20
29
  require 'pry'
@@ -25,6 +34,12 @@ require 'devise-security'
25
34
  require 'database_cleaner'
26
35
  require "orm/#{DEVISE_ORM}"
27
36
 
37
+ # Controller testing is the way that Devise itself tests the functionality of
38
+ # controller, even though it has been deprecated in favor of request tests.
39
+ require 'rails-controller-testing'
40
+ Rails::Controller::Testing.install
41
+ require 'support/integration_helpers'
42
+
28
43
  class Minitest::Test
29
44
  def before_setup
30
45
  DatabaseCleaner.start
@@ -11,13 +11,23 @@ class TestInstallGenerator < Rails::Generators::TestCase
11
11
 
12
12
  test 'Assert all files are properly created' do
13
13
  run_generator
14
- assert_file 'config/initializers/devise-security.rb'
14
+ assert_file 'config/initializers/devise_security.rb'
15
+ assert_file 'config/locales/devise.security_extension.by.yml'
16
+ assert_file 'config/locales/devise.security_extension.cs.yml'
15
17
  assert_file 'config/locales/devise.security_extension.de.yml'
16
18
  assert_file 'config/locales/devise.security_extension.en.yml'
17
19
  assert_file 'config/locales/devise.security_extension.es.yml'
20
+ assert_file 'config/locales/devise.security_extension.fa.yml'
18
21
  assert_file 'config/locales/devise.security_extension.fr.yml'
22
+ assert_file 'config/locales/devise.security_extension.hi.yml'
19
23
  assert_file 'config/locales/devise.security_extension.it.yml'
20
24
  assert_file 'config/locales/devise.security_extension.ja.yml'
25
+ assert_file 'config/locales/devise.security_extension.nl.yml'
26
+ assert_file 'config/locales/devise.security_extension.pt.yml'
27
+ assert_file 'config/locales/devise.security_extension.ru.yml'
21
28
  assert_file 'config/locales/devise.security_extension.tr.yml'
29
+ assert_file 'config/locales/devise.security_extension.uk.yml'
30
+ assert_file 'config/locales/devise.security_extension.zh_CN.yml'
31
+ assert_file 'config/locales/devise.security_extension.zh_TW.yml'
22
32
  end
23
33
  end
@@ -82,4 +82,80 @@ class TestSecureValidatable < ActiveSupport::TestCase
82
82
  refute user.valid?
83
83
  assert_equal DEVISE_ORM == :active_record ? ['Email has already been taken'] : ['Email is already taken'], user.errors.full_messages
84
84
  end
85
+
86
+ test 'password can not equal email for new user' do
87
+ msg = 'Password must be different than the email.'
88
+ user = User.create email: 'bob@microsoft.com', password: 'bob@microsoft.com', password_confirmation: 'bob@microsoft.com'
89
+ refute user.valid?
90
+ assert_includes(user.errors.full_messages, msg)
91
+ assert_raises(ORMInvalidRecordException) { user.save! }
92
+ end
93
+
94
+ test 'password can not equal case sensitive version of email for new user' do
95
+ msg = 'Password must be different than the email.'
96
+ user = User.create email: 'bob@microsoft.com', password: 'BoB@microsoft.com', password_confirmation: 'BoB@microsoft.com'
97
+ refute user.valid?
98
+ assert_includes(user.errors.full_messages, msg)
99
+ assert_raises(ORMInvalidRecordException) { user.save! }
100
+ end
101
+
102
+ test 'password can not equal email with spaces for new user' do
103
+ msg = 'Password must be different than the email.'
104
+ user = User.create email: 'bob@microsoft.com', password: 'bob@microsoft.com ', password_confirmation: 'bob@microsoft.com '
105
+ refute user.valid?
106
+ assert_includes(user.errors.full_messages, msg)
107
+ assert_raises(ORMInvalidRecordException) { user.save! }
108
+ end
109
+
110
+ test 'password can not equal case sensitive version of email with spaces for new user' do
111
+ msg = 'Password must be different than the email.'
112
+ user = User.create email: 'bob@microsoft.com', password: ' BoB@microsoft.com ', password_confirmation: ' BoB@microsoft.com '
113
+ refute user.valid?
114
+ assert_includes(user.errors.full_messages, msg)
115
+ assert_raises(ORMInvalidRecordException) { user.save! }
116
+ end
117
+
118
+ test 'password can not equal email for existing user' do
119
+ user = User.create email: 'bob@microsoft.com', password: 'pAs5W0rd!Is5e6Ure', password_confirmation: 'pAs5W0rd!Is5e6Ure'
120
+
121
+ msg = 'Password must be different than the email.'
122
+ user.password = 'bob@microsoft.com'
123
+ user.password_confirmation = 'bob@microsoft.com'
124
+ refute user.valid?
125
+ assert_includes(user.errors.full_messages, msg)
126
+ assert_raises(ORMInvalidRecordException) { user.save! }
127
+ end
128
+
129
+ test 'password can not equal case sensitive version of email for existing user' do
130
+ user = User.create email: 'bob@microsoft.com', password: 'pAs5W0rd!Is5e6Ure', password_confirmation: 'pAs5W0rd!Is5e6Ure'
131
+
132
+ msg = 'Password must be different than the email.'
133
+ user.password = 'BoB@microsoft.com'
134
+ user.password_confirmation = 'BoB@microsoft.com'
135
+ refute user.valid?
136
+ assert_includes(user.errors.full_messages, msg)
137
+ assert_raises(ORMInvalidRecordException) { user.save! }
138
+ end
139
+
140
+ test 'password can not equal email with spaces for existing user' do
141
+ user = User.create email: 'bob@microsoft.com', password: 'pAs5W0rd!Is5e6Ure', password_confirmation: 'pAs5W0rd!Is5e6Ure'
142
+
143
+ msg = 'Password must be different than the email.'
144
+ user.password = 'bob@microsoft.com '
145
+ user.password_confirmation = 'bob@microsoft.com '
146
+ refute user.valid?
147
+ assert_includes(user.errors.full_messages, msg)
148
+ assert_raises(ORMInvalidRecordException) { user.save! }
149
+ end
150
+
151
+ test 'password can not equal case sensitive version of email with spaces for existing user' do
152
+ user = User.create email: 'bob@microsoft.com', password: 'pAs5W0rd!Is5e6Ure', password_confirmation: 'pAs5W0rd!Is5e6Ure'
153
+
154
+ msg = 'Password must be different than the email.'
155
+ user.password = ' BoB@microsoft.com '
156
+ user.password_confirmation = ' BoB@microsoft.com '
157
+ refute user.valid?
158
+ assert_includes(user.errors.full_messages, msg)
159
+ assert_raises(ORMInvalidRecordException) { user.save! }
160
+ end
85
161
  end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestSessionLimitable < ActiveSupport::TestCase
6
+ class ModifiedUser < User
7
+ def skip_session_limitable?
8
+ true
9
+ end
10
+ end
11
+
12
+ test 'check is not skipped by default' do
13
+ user = User.create email: 'bob@microsoft.com', password: 'password1', password_confirmation: 'password1'
14
+ assert_equal(false, user.skip_session_limitable?)
15
+ end
16
+
17
+ test 'default check can be overridden by record instance' do
18
+ modified_user = ModifiedUser.create email: 'bob2@microsoft.com', password: 'password1', password_confirmation: 'password1'
19
+ assert_equal(true, modified_user.skip_session_limitable?)
20
+ end
21
+
22
+ class SessionLimitableUser < User
23
+ devise :session_limitable
24
+ include ::Mongoid::Mappings if DEVISE_ORM == :mongoid
25
+ end
26
+
27
+ test 'includes Devise::Models::Compatibility' do
28
+ assert_kind_of(Devise::Models::Compatibility, SessionLimitableUser.new)
29
+ end
30
+
31
+ test '#update_unique_session_id!(value) updates valid record' do
32
+ user = User.create! password: 'passWord1', password_confirmation: 'passWord1', email: 'bob@microsoft.com'
33
+ assert user.persisted?
34
+ assert_nil user.unique_session_id
35
+ user.update_unique_session_id!('unique_value')
36
+ user.reload
37
+ assert_equal user.unique_session_id, 'unique_value'
38
+ end
39
+
40
+ test '#update_unique_session_id!(value) updates invalid record atomically' do
41
+ user = User.create! password: 'passWord1', password_confirmation: 'passWord1', email: 'bob@microsoft.com'
42
+ assert user.persisted?
43
+ user.email = ''
44
+ assert user.invalid?
45
+ assert_nil user.unique_session_id
46
+ user.update_unique_session_id!('unique_value')
47
+ user.reload
48
+ assert_equal user.email, 'bob@microsoft.com'
49
+ assert_equal user.unique_session_id, 'unique_value'
50
+ end
51
+
52
+ test '#update_unique_session_id!(value) raises an exception on an unpersisted record' do
53
+ user = User.create
54
+ assert !user.persisted?
55
+ assert_raises(Devise::Models::Compatibility::NotPersistedError) { user.update_unique_session_id!('unique_value') }
56
+ end
57
+ end