devise-security 0.14.2 → 0.17.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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +125 -59
  3. data/app/controllers/devise/paranoid_verification_code_controller.rb +13 -1
  4. data/app/controllers/devise/password_expired_controller.rb +24 -6
  5. data/app/views/devise/paranoid_verification_code/show.html.erb +3 -3
  6. data/app/views/devise/password_expired/show.html.erb +5 -5
  7. data/config/locales/bg.yml +41 -0
  8. data/config/locales/by.yml +49 -0
  9. data/config/locales/cs.yml +41 -0
  10. data/config/locales/de.yml +15 -2
  11. data/config/locales/en.yml +15 -2
  12. data/config/locales/es.yml +10 -9
  13. data/config/locales/fa.yml +41 -0
  14. data/config/locales/fr.yml +1 -0
  15. data/config/locales/hi.yml +42 -0
  16. data/config/locales/it.yml +35 -4
  17. data/config/locales/ja.yml +2 -1
  18. data/config/locales/nl.yml +41 -0
  19. data/config/locales/pt.yml +41 -0
  20. data/config/locales/ru.yml +49 -0
  21. data/config/locales/tr.yml +1 -0
  22. data/config/locales/uk.yml +49 -0
  23. data/config/locales/zh_CN.yml +41 -0
  24. data/config/locales/zh_TW.yml +41 -0
  25. data/lib/devise-security/controllers/helpers.rb +59 -50
  26. data/lib/devise-security/hooks/password_expirable.rb +2 -0
  27. data/lib/devise-security/hooks/session_limitable.rb +21 -11
  28. data/lib/devise-security/models/database_authenticatable_patch.rb +15 -5
  29. data/lib/devise-security/models/password_archivable.rb +2 -2
  30. data/lib/devise-security/models/password_expirable.rb +5 -1
  31. data/lib/devise-security/models/secure_validatable.rb +56 -6
  32. data/lib/devise-security/models/session_limitable.rb +10 -1
  33. data/lib/devise-security/validators/password_complexity_validator.rb +53 -24
  34. data/lib/devise-security/version.rb +1 -1
  35. data/lib/devise-security.rb +13 -5
  36. data/lib/generators/devise_security/install_generator.rb +3 -3
  37. data/lib/generators/templates/{devise-security.rb → devise_security.rb} +6 -1
  38. data/test/controllers/test_paranoid_verification_code_controller.rb +68 -0
  39. data/test/controllers/test_password_expired_controller.rb +121 -19
  40. data/test/controllers/test_security_question_controller.rb +16 -40
  41. data/test/dummy/app/assets/config/manifest.js +3 -0
  42. data/test/dummy/app/controllers/overrides/paranoid_verification_code_controller.rb +7 -0
  43. data/test/dummy/app/controllers/overrides/password_expired_controller.rb +7 -0
  44. data/test/dummy/app/controllers/widgets_controller.rb +3 -0
  45. data/test/dummy/app/models/application_user_record.rb +2 -1
  46. data/test/dummy/app/models/mongoid/confirmable_fields.rb +2 -0
  47. data/test/dummy/app/models/mongoid/database_authenticable_fields.rb +4 -3
  48. data/test/dummy/app/models/mongoid/expirable_fields.rb +2 -0
  49. data/test/dummy/app/models/mongoid/lockable_fields.rb +2 -0
  50. data/test/dummy/app/models/mongoid/mappings.rb +4 -2
  51. data/test/dummy/app/models/mongoid/omniauthable_fields.rb +2 -0
  52. data/test/dummy/app/models/mongoid/paranoid_verification_fields.rb +2 -0
  53. data/test/dummy/app/models/mongoid/password_archivable_fields.rb +2 -0
  54. data/test/dummy/app/models/mongoid/password_expirable_fields.rb +2 -0
  55. data/test/dummy/app/models/mongoid/recoverable_fields.rb +2 -0
  56. data/test/dummy/app/models/mongoid/registerable_fields.rb +4 -2
  57. data/test/dummy/app/models/mongoid/rememberable_fields.rb +2 -0
  58. data/test/dummy/app/models/mongoid/secure_validatable_fields.rb +2 -0
  59. data/test/dummy/app/models/mongoid/security_questionable_fields.rb +2 -0
  60. data/test/dummy/app/models/mongoid/session_limitable_fields.rb +2 -0
  61. data/test/dummy/app/models/mongoid/timeoutable_fields.rb +2 -0
  62. data/test/dummy/app/models/mongoid/trackable_fields.rb +2 -0
  63. data/test/dummy/app/models/mongoid/validatable_fields.rb +2 -0
  64. data/test/dummy/app/models/paranoid_verification_user.rb +26 -0
  65. data/test/dummy/app/models/password_expired_user.rb +26 -0
  66. data/test/dummy/app/models/user.rb +1 -2
  67. data/test/dummy/app/models/widget.rb +1 -3
  68. data/test/dummy/app/mongoid/one_user.rb +5 -5
  69. data/test/dummy/app/mongoid/user_on_engine.rb +2 -2
  70. data/test/dummy/app/mongoid/user_on_main_app.rb +2 -2
  71. data/test/dummy/app/mongoid/user_with_validations.rb +3 -3
  72. data/test/dummy/app/mongoid/user_without_email.rb +3 -3
  73. data/test/dummy/config/application.rb +4 -4
  74. data/test/dummy/config/boot.rb +1 -1
  75. data/test/dummy/config/environment.rb +1 -1
  76. data/test/dummy/config/environments/test.rb +3 -13
  77. data/test/dummy/config/initializers/migration_class.rb +1 -8
  78. data/test/dummy/config/locales/en.yml +10 -0
  79. data/test/dummy/config/mongoid.yml +1 -1
  80. data/test/dummy/config/routes.rb +5 -3
  81. data/test/dummy/db/migrate/20120508165529_create_tables.rb +3 -3
  82. data/test/dummy/lib/shared_expirable_columns.rb +1 -0
  83. data/test/dummy/lib/shared_security_questions_fields.rb +1 -0
  84. data/test/dummy/lib/shared_user.rb +17 -6
  85. data/test/dummy/lib/shared_user_without_email.rb +2 -1
  86. data/test/dummy/lib/shared_user_without_omniauth.rb +12 -3
  87. data/test/dummy/lib/shared_verification_fields.rb +1 -0
  88. data/test/dummy/{app/models/.gitkeep → log/development.log} +0 -0
  89. data/test/dummy/log/test.log +101533 -0
  90. data/test/integration/test_password_expirable_workflow.rb +53 -0
  91. data/test/integration/test_session_limitable_workflow.rb +2 -0
  92. data/test/orm/active_record.rb +7 -4
  93. data/test/orm/mongoid.rb +2 -1
  94. data/test/support/integration_helpers.rb +15 -33
  95. data/test/support/mongoid.yml +1 -1
  96. data/test/test_compatibility.rb +2 -0
  97. data/test/test_complexity_validator.rb +250 -29
  98. data/test/test_database_authenticatable_patch.rb +146 -0
  99. data/test/test_helper.rb +12 -6
  100. data/test/test_install_generator.rb +12 -2
  101. data/test/test_paranoid_verification.rb +0 -1
  102. data/test/test_password_archivable.rb +34 -11
  103. data/test/test_password_expirable.rb +26 -26
  104. data/test/test_secure_validatable.rb +292 -50
  105. data/test/test_secure_validatable_overrides.rb +185 -0
  106. data/test/test_session_limitable.rb +27 -1
  107. data/test/tmp/config/initializers/devise_security.rb +49 -0
  108. data/test/tmp/config/locales/devise.security_extension.by.yml +49 -0
  109. data/test/tmp/config/locales/devise.security_extension.cs.yml +41 -0
  110. data/test/tmp/config/locales/devise.security_extension.de.yml +41 -0
  111. data/test/tmp/config/locales/devise.security_extension.en.yml +42 -0
  112. data/test/tmp/config/locales/devise.security_extension.es.yml +30 -0
  113. data/test/tmp/config/locales/devise.security_extension.fa.yml +41 -0
  114. data/test/tmp/config/locales/devise.security_extension.fr.yml +30 -0
  115. data/test/tmp/config/locales/devise.security_extension.hi.yml +42 -0
  116. data/test/tmp/config/locales/devise.security_extension.it.yml +41 -0
  117. data/test/tmp/config/locales/devise.security_extension.ja.yml +30 -0
  118. data/test/tmp/config/locales/devise.security_extension.nl.yml +41 -0
  119. data/test/tmp/config/locales/devise.security_extension.pt.yml +41 -0
  120. data/test/tmp/config/locales/devise.security_extension.ru.yml +49 -0
  121. data/test/tmp/config/locales/devise.security_extension.tr.yml +18 -0
  122. data/test/tmp/config/locales/devise.security_extension.uk.yml +49 -0
  123. data/test/tmp/config/locales/devise.security_extension.zh_CN.yml +41 -0
  124. data/test/tmp/config/locales/devise.security_extension.zh_TW.yml +41 -0
  125. metadata +168 -132
  126. data/.codeclimate.yml +0 -63
  127. data/.document +0 -5
  128. data/.gitignore +0 -43
  129. data/.mdlrc +0 -1
  130. data/.rubocop.yml +0 -64
  131. data/.ruby-version +0 -1
  132. data/.travis.yml +0 -39
  133. data/Appraisals +0 -35
  134. data/Gemfile +0 -10
  135. data/Rakefile +0 -27
  136. data/devise-security.gemspec +0 -50
  137. data/gemfiles/rails_4.2_stable.gemfile +0 -16
  138. data/gemfiles/rails_5.0_stable.gemfile +0 -15
  139. data/gemfiles/rails_5.1_stable.gemfile +0 -15
  140. data/gemfiles/rails_5.2_stable.gemfile +0 -15
  141. data/gemfiles/rails_6.0_beta.gemfile +0 -15
  142. data/lib/devise-security/orm/active_record.rb +0 -20
  143. data/lib/devise-security/schema.rb +0 -66
  144. data/test/dummy/app/models/secure_user.rb +0 -9
@@ -94,7 +94,6 @@ class TestParanoidVerification < ActiveSupport::TestCase
94
94
  Devise.paranoid_code_regenerate_after_attempt = original_regenerate
95
95
  end
96
96
 
97
-
98
97
  test 'by default paranoid code regenerate should have 10 attempts' do
99
98
  user = User.new(paranoid_verification_code: 'abcde')
100
99
  assert_equal 10, user.paranoid_attempts_remaining
@@ -3,7 +3,6 @@
3
3
  require 'test_helper'
4
4
 
5
5
  class TestPasswordArchivable < ActiveSupport::TestCase
6
-
7
6
  setup do
8
7
  Devise.password_archiving_count = 2
9
8
  end
@@ -20,7 +19,7 @@ class TestPasswordArchivable < ActiveSupport::TestCase
20
19
 
21
20
  test 'cannot use same password' do
22
21
  user = User.create email: 'bob@microsoft.com', password: 'Password1', password_confirmation: 'Password1'
23
- assert_raises(ORMInvalidRecordException) { set_password(user, 'Password1') }
22
+ assert_raises(ORMInvalidRecordException) { set_password(user, 'Password1') }
24
23
  end
25
24
 
26
25
  test 'indirectly saving associated user does not cause deprecation warning' do
@@ -43,19 +42,19 @@ class TestPasswordArchivable < ActiveSupport::TestCase
43
42
 
44
43
  user = User.create! email: 'bob@microsoft.com', password: 'Password1', password_confirmation: 'Password1'
45
44
  assert_equal 0, OldPassword.count
46
- set_password(user, 'Password2')
45
+ set_password(user, 'Password2')
47
46
  assert_equal 1, OldPassword.count
48
47
 
49
- assert_raises(ORMInvalidRecordException) { set_password(user, 'Password1') }
50
- set_password(user, 'Password3')
48
+ assert_raises(ORMInvalidRecordException) { set_password(user, 'Password1') }
49
+ set_password(user, 'Password3')
51
50
  assert_equal 2, OldPassword.count
52
51
 
53
52
  # rotate first password out of archive
54
- assert set_password(user, 'Password4')
53
+ assert set_password(user, 'Password4')
55
54
 
56
55
  # archive count was 2, so first password should work again
57
- assert set_password(user, 'Password1')
58
- assert set_password(user, 'Password2')
56
+ assert set_password(user, 'Password1')
57
+ assert set_password(user, 'Password2')
59
58
  end
60
59
 
61
60
  test 'the option should be dynamic during runtime' do
@@ -67,10 +66,34 @@ class TestPasswordArchivable < ActiveSupport::TestCase
67
66
 
68
67
  user = User.create email: 'bob@microsoft.com', password: 'Password1', password_confirmation: 'Password1'
69
68
 
70
- assert set_password(user, 'Password2')
69
+ assert set_password(user, 'Password2')
70
+
71
+ assert_raises(ORMInvalidRecordException) { set_password(user, 'Password2') }
72
+
73
+ assert_raises(ORMInvalidRecordException) { set_password(user, 'Password1') }
74
+ end
75
+
76
+ test 'default sort orders do not affect archiving' do
77
+ class ::OldPassword
78
+ default_scope { order(created_at: :asc) }
79
+ end
80
+
81
+ assert_equal 2, Devise.password_archiving_count
82
+
83
+ user = User.create! email: 'bob@microsoft.com', password: 'Password1', password_confirmation: 'Password1'
84
+ assert_equal 0, OldPassword.count
85
+ set_password(user, 'Password2')
86
+ assert_equal 1, OldPassword.count
87
+
88
+ assert_raises(ORMInvalidRecordException) { set_password(user, 'Password1') }
89
+ set_password(user, 'Password3')
90
+ assert_equal 2, OldPassword.count
71
91
 
72
- assert_raises(ORMInvalidRecordException) { set_password(user, 'Password2') }
92
+ # rotate first password out of archive
93
+ assert set_password(user, 'Password4')
73
94
 
74
- assert_raises(ORMInvalidRecordException) { set_password(user, 'Password1') }
95
+ # archive count was 2, so first password should work again
96
+ assert set_password(user, 'Password1')
97
+ assert set_password(user, 'Password2')
75
98
  end
76
99
  end
@@ -14,59 +14,59 @@ class TestPasswordArchivable < ActiveSupport::TestCase
14
14
  test 'does nothing if disabled' do
15
15
  Devise.expire_password_after = false
16
16
  user = User.create email: 'bob@microsoft.com', password: 'Password1', password_confirmation: 'Password1'
17
- refute user.need_change_password?
18
- refute user.password_expired?
17
+ assert_not user.need_change_password?
18
+ assert_not user.password_expired?
19
19
  user.need_change_password!
20
- refute user.need_change_password?
21
- refute user.password_expired?
20
+ assert_not user.need_change_password?
21
+ assert_not user.password_expired?
22
22
  end
23
23
 
24
24
  test 'password change can be requested' do
25
25
  Devise.expire_password_after = true
26
26
  user = User.create email: 'bob@microsoft.com', password: 'Password1', password_confirmation: 'Password1'
27
- refute user.need_change_password?
28
- refute user.password_expired?
29
- refute user.password_change_requested?
27
+ assert_not user.need_change_password?
28
+ assert_not user.password_expired?
29
+ assert_not user.password_change_requested?
30
30
  user.need_change_password!
31
31
  assert user.need_change_password?
32
- refute user.password_expired? # it's not too old because it's not set at all
32
+ assert_not user.password_expired? # it's not too old because it's not set at all
33
33
  assert user.password_change_requested?
34
34
  end
35
35
 
36
36
  test 'password expires' do
37
37
  user = User.create email: 'bob@microsoft.com', password: 'Password1', password_confirmation: 'Password1'
38
- refute user.need_change_password?
39
- refute user.password_expired?
40
- refute user.password_too_old?
41
- user.update(password_changed_at: Time.now.ago(3.months))
38
+ assert_not user.need_change_password?
39
+ assert_not user.password_expired?
40
+ assert_not user.password_too_old?
41
+ user.update(password_changed_at: Time.zone.now.ago(3.months))
42
42
  assert user.password_too_old?
43
43
  assert user.need_change_password?
44
44
  assert user.password_expired?
45
- refute user.password_change_requested?
45
+ assert_not user.password_change_requested?
46
46
  end
47
47
 
48
48
  test 'saving a record records the time the password was changed' do
49
49
  user = User.new email: 'bob@microsoft.com', password: 'Password1', password_confirmation: 'Password1'
50
50
  assert user.password_changed_at.nil?
51
- refute user.password_change_requested?
52
- refute user.password_expired?
51
+ assert_not user.password_change_requested?
52
+ assert_not user.password_expired?
53
53
  user.save
54
54
  assert user.password_changed_at.present?
55
- refute user.password_change_requested?
56
- refute user.password_expired?
55
+ assert_not user.password_change_requested?
56
+ assert_not user.password_expired?
57
57
  end
58
58
 
59
59
  test 'updating a record updates the time the password was changed if the password is changed' do
60
60
  user = User.create email: 'bob@microsoft.com', password: 'Password1', password_confirmation: 'Password1'
61
- user.update(password_changed_at: Time.now.ago(3.months))
61
+ user.update(password_changed_at: Time.zone.now.ago(3.months))
62
62
  original_password_changed_at = user.password_changed_at
63
63
  user.expire_password!
64
64
  assert user.password_change_requested?
65
- user.password = "NewPassword1"
66
- user.password_confirmation = "NewPassword1"
65
+ user.password = 'NewPassword1'
66
+ user.password_confirmation = 'NewPassword1'
67
67
  user.save
68
68
  assert user.password_changed_at > original_password_changed_at
69
- refute user.password_change_requested?
69
+ assert_not user.password_change_requested?
70
70
  end
71
71
 
72
72
  test 'updating a record does not updates the time the password was changed if the password was not changed' do
@@ -74,7 +74,7 @@ class TestPasswordArchivable < ActiveSupport::TestCase
74
74
  user.expire_password!
75
75
  assert user.password_change_requested?
76
76
  user.save
77
- refute user.previous_changes.key?(:password_changed_at)
77
+ assert_not user.previous_changes.key?(:password_changed_at)
78
78
  assert user.password_change_requested?
79
79
  end
80
80
 
@@ -85,10 +85,10 @@ class TestPasswordArchivable < ActiveSupport::TestCase
85
85
  4.months
86
86
  end
87
87
  end
88
- user.password_changed_at = Time.now.ago(3.months)
89
- refute user.need_change_password?
90
- refute user.password_expired?
91
- user.password_changed_at = Time.now.ago(5.months)
88
+ user.password_changed_at = Time.zone.now.ago(3.months)
89
+ assert_not user.need_change_password?
90
+ assert_not user.password_expired?
91
+ user.password_changed_at = Time.zone.now.ago(5.months)
92
92
  assert user.need_change_password?
93
93
  assert user.password_expired?
94
94
  end
@@ -1,85 +1,327 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'test_helper'
4
- require 'rails_email_validator'
5
4
 
6
5
  class TestSecureValidatable < ActiveSupport::TestCase
7
6
  class User < ApplicationRecord
8
- devise :database_authenticatable, :password_archivable,
9
- :paranoid_verification, :password_expirable, :secure_validatable
7
+ devise :database_authenticatable, :secure_validatable
10
8
  include ::Mongoid::Mappings if DEVISE_ORM == :mongoid
11
9
  end
12
10
 
13
- test 'email cannot be blank' do
14
- msg = "Email can't be blank"
15
- user = User.create password: 'passWord1', password_confirmation: 'passWord1'
11
+ class EmailNotRequiredUser < User
12
+ protected
16
13
 
17
- assert_equal(false, user.valid?)
18
- assert_equal([msg], user.errors.full_messages)
19
- assert_raises(ORMInvalidRecordException) do
20
- user.save!
14
+ def email_required?
15
+ false
21
16
  end
22
17
  end
23
18
 
19
+ test 'email cannot be blank upon creation' do
20
+ user = User.new(
21
+ password: 'Password1!', password_confirmation: 'Password1!'
22
+ )
23
+
24
+ assert user.invalid?
25
+ assert_equal(["Email can't be blank"], user.errors.full_messages)
26
+ end
27
+
28
+ test 'email can be blank upon creation if email not required' do
29
+ user = EmailNotRequiredUser.new(
30
+ password: 'Password1!', password_confirmation: 'Password1!'
31
+ )
32
+
33
+ assert user.valid?
34
+ end
35
+
36
+ test 'email cannot be updated to be blank' do
37
+ user = User.new(
38
+ email: 'bob@microsoft.com',
39
+ password: 'Password1!',
40
+ password_confirmation: 'Password1!'
41
+ )
42
+
43
+ assert user.valid?
44
+
45
+ user.email = nil
46
+
47
+ assert user.invalid?
48
+ assert_equal(["Email can't be blank"], user.errors.full_messages)
49
+ end
50
+
51
+ test 'email can be updated to be blank if email not required' do
52
+ user = EmailNotRequiredUser.new(
53
+ email: 'bob@microsoft.com',
54
+ password: 'Password1!',
55
+ password_confirmation: 'Password1!'
56
+ )
57
+
58
+ assert user.valid?
59
+
60
+ user.email = nil
61
+
62
+ assert user.valid?
63
+ end
64
+
24
65
  test 'email must be valid' do
25
- msg = 'Email is invalid'
26
- user = User.create email: 'bob', password: 'passWord1', password_confirmation: 'passWord1'
27
- assert_equal(false, user.valid?)
28
- assert_equal([msg], user.errors.full_messages)
29
- assert_raises(ORMInvalidRecordException) do
30
- user.save!
31
- end
66
+ user = User.new(
67
+ email: 'bob', password: 'Password1!', password_confirmation: 'Password1!'
68
+ )
69
+
70
+ assert user.invalid?
71
+ assert_equal(['Email is invalid'], user.errors.full_messages)
32
72
  end
33
73
 
34
74
  test 'validate both email and password' do
35
- msgs = ['Email is invalid', 'Password must contain at least one upper-case letter']
36
- user = User.create email: 'bob@@foo.tv', password: 'password1', password_confirmation: 'password1'
37
- assert_equal(false, user.valid?)
75
+ user = User.new(
76
+ email: 'bob',
77
+ password: 'password1!',
78
+ password_confirmation: 'password1!'
79
+ )
80
+
81
+ assert user.invalid?
82
+ assert_equal(
83
+ [
84
+ 'Email is invalid',
85
+ 'Password must contain at least one upper-case letter'
86
+ ],
87
+ user.errors.full_messages
88
+ )
89
+ end
90
+
91
+ test 'password cannot be blank upon creation' do
92
+ user = User.new(email: 'bob@microsoft.com')
93
+
94
+ msgs = ["Password can't be blank"]
95
+
96
+ msgs << "Encrypted password can't be blank" if DEVISE_ORM == :mongoid
97
+
98
+ assert user.invalid?
38
99
  assert_equal(msgs, user.errors.full_messages)
39
- assert_raises(ORMInvalidRecordException) { user.save! }
100
+ end
101
+
102
+ test 'password cannot be updated to be blank' do
103
+ user = User.new(
104
+ email: 'bob@microsoft.com',
105
+ password: 'Password1!',
106
+ password_confirmation: 'Password1!'
107
+ )
108
+
109
+ assert user.valid?
110
+
111
+ user.password = nil
112
+ user.password_confirmation = nil
113
+
114
+ assert user.invalid?
115
+ assert_equal(["Password can't be blank"],user.errors.full_messages)
116
+ end
117
+
118
+ test 'password_confirmation must match password' do
119
+ user = User.new(
120
+ email: 'bob@microsoft.com',
121
+ password: 'Password1!',
122
+ password_confirmation: 'not the same password'
123
+ )
124
+
125
+ assert user.invalid?
126
+ assert_equal(
127
+ ["Password confirmation doesn't match Password"],
128
+ user.errors.full_messages
129
+ )
130
+ end
131
+
132
+ test 'password_confirmation cannot be blank' do
133
+ user = User.new(
134
+ email: 'bob@microsoft.com',
135
+ password: 'Password1!',
136
+ password_confirmation: ''
137
+ )
138
+
139
+ assert user.invalid?
140
+ assert_equal(
141
+ ["Password confirmation doesn't match Password"],
142
+ user.errors.full_messages
143
+ )
144
+ end
145
+
146
+ test 'password_confirmation can be skipped' do
147
+ user = User.new(
148
+ email: 'bob@microsoft.com',
149
+ password: 'Password1!',
150
+ password_confirmation: nil
151
+ )
152
+
153
+ assert user.valid?
40
154
  end
41
155
 
42
156
  test 'password must have capital letter' do
43
- msgs = ['Password must contain at least one upper-case letter']
44
- user = User.create email: 'bob@microsoft.com', password: 'password1', password_confirmation: 'password1'
45
- assert_equal(false, user.valid?)
46
- assert_equal(msgs, user.errors.full_messages)
47
- assert_raises(ORMInvalidRecordException) { user.save! }
157
+ user = User.new(
158
+ email: 'bob@microsoft.com',
159
+ password: 'password1',
160
+ password_confirmation: 'password1'
161
+ )
162
+
163
+ assert user.invalid?
164
+ assert_equal(
165
+ ['Password must contain at least one upper-case letter'],
166
+ user.errors.full_messages
167
+ )
48
168
  end
49
169
 
50
170
  test 'password must have lowercase letter' do
51
- msg = 'Password must contain at least one lower-case letter'
52
- user = User.create email: 'bob@microsoft.com', password: 'PASSWORD1', password_confirmation: 'PASSWORD1'
53
- assert_equal(false, user.valid?)
54
- assert_equal([msg], user.errors.full_messages)
55
- assert_raises(ORMInvalidRecordException) { user.save! }
171
+ user = User.new(
172
+ email: 'bob@microsoft.com',
173
+ password: 'PASSWORD1',
174
+ password_confirmation: 'PASSWORD1'
175
+ )
176
+
177
+ assert user.invalid?
178
+ assert_equal(
179
+ ['Password must contain at least one lower-case letter'],
180
+ user.errors.full_messages
181
+ )
56
182
  end
57
183
 
58
184
  test 'password must have number' do
59
- msg = 'Password must contain at least one digit'
60
- user = User.create email: 'bob@microsoft.com', password: 'PASSword', password_confirmation: 'PASSword'
61
- assert_equal(false, user.valid?)
62
- assert_equal([msg], user.errors.full_messages)
63
- assert_raises(ORMInvalidRecordException) { user.save! }
185
+ user = User.new(
186
+ email: 'bob@microsoft.com',
187
+ password: 'PASSword',
188
+ password_confirmation: 'PASSword'
189
+ )
190
+
191
+ assert user.invalid?
192
+ assert_equal(
193
+ ['Password must contain at least one digit'],
194
+ user.errors.full_messages
195
+ )
196
+ end
197
+
198
+ test 'password must meet minimum length' do
199
+ user = User.new(
200
+ email: 'bob@microsoft.com',
201
+ password: 'Pa3zZ',
202
+ password_confirmation: 'Pa3zZ'
203
+ )
204
+
205
+ assert user.invalid?
206
+ assert_equal(
207
+ ['Password is too short (minimum is 7 characters)'],
208
+ user.errors.full_messages
209
+ )
64
210
  end
65
211
 
66
- test 'password must have minimum length' do
67
- msg = 'Password is too short (minimum is 7 characters)'
68
- user = User.create email: 'bob@microsoft.com', password: 'Pa3zZ', password_confirmation: 'Pa3zZ'
69
- assert_equal(false, user.valid?)
70
- assert_equal([msg], user.errors.full_messages)
71
- assert_raises(ORMInvalidRecordException) { user.save! }
212
+ test "new user can't use existing user's email" do
213
+ options = {
214
+ email: 'bob@microsoft.com',
215
+ password: 'Password1!',
216
+ password_confirmation: 'Password1!',
217
+ }
218
+ User.create!(options)
219
+ user = User.new(options)
220
+
221
+ assert user.invalid?
222
+ if DEVISE_ORM == :active_record
223
+ assert_equal(['Email has already been taken'], user.errors.full_messages)
224
+ else
225
+ assert_equal(['Email is already taken'], user.errors.full_messages)
226
+ end
72
227
  end
73
228
 
74
- test 'duplicate email validation message is added only once' do
229
+ test "new user can't use existing user's email with different casing" do
75
230
  options = {
76
- email: 'test@example.org',
77
- password: 'Test12345',
78
- password_confirmation: 'Test12345',
231
+ email: 'bob@microsoft.com',
232
+ password: 'Password1!',
233
+ password_confirmation: 'Password1!',
79
234
  }
80
- SecureUser.create!(options)
81
- user = SecureUser.new(options)
82
- refute user.valid?
83
- assert_equal DEVISE_ORM == :active_record ? ['Email has already been taken'] : ['Email is already taken'], user.errors.full_messages
235
+ User.create!(options)
236
+ options[:email] = 'BOB@MICROSOFT.COM'
237
+ user = User.new(options)
238
+
239
+ assert user.invalid?
240
+ if DEVISE_ORM == :active_record
241
+ assert_equal(['Email has already been taken'], user.errors.full_messages)
242
+ else
243
+ assert_equal(['Email is already taken'], user.errors.full_messages)
244
+ end
245
+ end
246
+
247
+ test 'password cannot equal email for new user' do
248
+ user = User.new(
249
+ email: 'Bob1@microsoft.com',
250
+ password: 'Bob1@microsoft.com',
251
+ password_confirmation: 'Bob1@microsoft.com'
252
+ )
253
+
254
+ assert user.invalid?
255
+ assert_equal(
256
+ ['Password must be different than the email.'],
257
+ user.errors.full_messages
258
+ )
259
+ end
260
+
261
+ test 'password cannot equal case sensitive version of email for new user' do
262
+ user = User.new(
263
+ email: 'bob1@microsoft.com',
264
+ password: 'BoB1@microsoft.com',
265
+ password_confirmation: 'BoB1@microsoft.com'
266
+ )
267
+
268
+ assert user.invalid?
269
+ assert_equal(
270
+ ['Password must be different than the email.'],
271
+ user.errors.full_messages
272
+ )
273
+ end
274
+
275
+ test 'password cannot equal email with spaces for new user' do
276
+ user = User.new(
277
+ email: 'Bob1@microsoft.com',
278
+ password: 'Bob1@microsoft.com ',
279
+ password_confirmation: 'Bob1@microsoft.com '
280
+ )
281
+
282
+ assert user.invalid?
283
+ assert_equal(
284
+ ['Password must be different than the email.'],
285
+ user.errors.full_messages
286
+ )
287
+ end
288
+
289
+ test 'password cannot equal case sensitive version of email with spaces '\
290
+ 'for new user' do
291
+ user = User.new(
292
+ email: 'Bob1@microsoft.com',
293
+ password: ' boB1@microsoft.com ',
294
+ password_confirmation: ' boB1@microsoft.com '
295
+ )
296
+
297
+ assert user.invalid?
298
+ assert_equal(
299
+ ['Password must be different than the email.'],
300
+ user.errors.full_messages
301
+ )
302
+ end
303
+
304
+ test 'new password cannot equal current password' do
305
+ user = User.create(
306
+ email: 'bob@microsoft.com',
307
+ password: 'Password1!',
308
+ password_confirmation: 'Password1!'
309
+ )
310
+
311
+ user.password = 'Password1!'
312
+
313
+ assert user.invalid?
314
+ assert_equal(
315
+ ['Password must be different than the current password.'],
316
+ user.errors.full_messages
317
+ )
318
+ end
319
+
320
+ test 'should not be included in objects with invalid API' do
321
+ error = assert_raise RuntimeError do
322
+ class ::Dog; include Devise::Models::SecureValidatable; end
323
+ end
324
+
325
+ assert_equal('Could not use SecureValidatable on Dog', error.message)
84
326
  end
85
327
  end