devise-security 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +3 -1
- data/README.md +14 -5
- data/app/controllers/devise/paranoid_verification_code_controller.rb +14 -12
- data/app/controllers/devise/password_expired_controller.rb +8 -4
- data/config/locales/bg.yml +1 -0
- data/config/locales/by.yml +1 -0
- data/config/locales/cs.yml +5 -0
- data/config/locales/de.yml +1 -0
- data/config/locales/es.yml +12 -0
- data/config/locales/fa.yml +1 -0
- data/config/locales/fr.yml +14 -2
- data/config/locales/hi.yml +1 -0
- data/config/locales/it.yml +1 -0
- data/config/locales/ja.yml +12 -0
- data/config/locales/nl.yml +1 -0
- data/config/locales/pt.yml +1 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/tr.yml +25 -1
- data/config/locales/uk.yml +1 -0
- data/config/locales/zh_CN.yml +1 -0
- data/config/locales/zh_TW.yml +1 -0
- data/lib/devise-security/controllers/helpers.rb +23 -11
- data/lib/devise-security/hooks/expirable.rb +3 -3
- data/lib/devise-security/hooks/paranoid_verification.rb +1 -3
- data/lib/devise-security/hooks/password_expirable.rb +1 -3
- data/lib/devise-security/hooks/session_limitable.rb +4 -4
- data/lib/devise-security/models/compatibility/active_record_patch.rb +4 -3
- data/lib/devise-security/models/compatibility/mongoid_patch.rb +3 -2
- data/lib/devise-security/models/database_authenticatable_patch.rb +12 -14
- data/lib/devise-security/models/expirable.rb +6 -5
- data/lib/devise-security/models/paranoid_verification.rb +2 -2
- data/lib/devise-security/models/password_archivable.rb +1 -1
- data/lib/devise-security/models/secure_validatable.rb +6 -5
- data/lib/devise-security/orm/mongoid.rb +1 -1
- data/lib/devise-security/patches.rb +14 -8
- data/lib/devise-security/routes.rb +2 -3
- data/lib/devise-security/version.rb +1 -1
- data/lib/devise-security.rb +2 -1
- data/lib/generators/devise_security/install_generator.rb +3 -5
- data/lib/generators/templates/devise_security.rb +3 -0
- data/test/controllers/test_paranoid_verification_code_controller.rb +70 -5
- data/test/controllers/test_password_expired_controller.rb +57 -41
- data/test/controllers/test_security_question_controller.rb +25 -19
- data/test/dummy/app/controllers/overrides/password_expired_controller.rb +10 -0
- data/test/dummy/app/models/user.rb +4 -3
- data/test/dummy/app/mongoid/user_without_email.rb +4 -1
- data/test/dummy/config/application.rb +0 -4
- data/test/dummy/config/environments/test.rb +1 -0
- data/test/dummy/config/initializers/devise.rb +1 -5
- data/test/dummy/config/routes.rb +1 -1
- data/test/dummy/config.ru +1 -1
- data/test/dummy/db/migrate/20120508165529_create_tables.rb +2 -2
- data/test/dummy/log/test.log +34100 -90393
- data/test/i18n_test.rb +22 -0
- data/test/integration/test_paranoid_verification_code_workflow.rb +53 -0
- data/test/integration/test_password_expirable_workflow.rb +2 -2
- data/test/integration/test_session_limitable_workflow.rb +3 -3
- data/test/support/integration_helpers.rb +18 -12
- data/test/test_complexity_validator.rb +42 -41
- data/test/test_database_authenticatable_patch.rb +3 -3
- data/test/test_paranoid_verification.rb +8 -8
- data/test/test_password_expirable.rb +1 -1
- data/test/test_secure_validatable.rb +5 -13
- data/test/test_session_limitable.rb +7 -7
- data/test/tmp/config/initializers/devise_security.rb +3 -0
- data/test/tmp/config/locales/devise.security_extension.by.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.cs.yml +5 -0
- data/test/tmp/config/locales/devise.security_extension.de.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.es.yml +12 -0
- data/test/tmp/config/locales/devise.security_extension.fa.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.fr.yml +14 -2
- data/test/tmp/config/locales/devise.security_extension.hi.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.it.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.ja.yml +12 -0
- data/test/tmp/config/locales/devise.security_extension.nl.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.pt.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.ru.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.tr.yml +25 -1
- data/test/tmp/config/locales/devise.security_extension.uk.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.zh_CN.yml +1 -0
- data/test/tmp/config/locales/devise.security_extension.zh_TW.yml +1 -0
- metadata +45 -27
- data/lib/devise-security/patches/confirmations_controller_captcha.rb +0 -23
- data/lib/devise-security/patches/confirmations_controller_security_question.rb +0 -26
- data/lib/devise-security/patches/passwords_controller_captcha.rb +0 -22
- data/lib/devise-security/patches/passwords_controller_security_question.rb +0 -25
- data/lib/devise-security/patches/registrations_controller_captcha.rb +0 -35
- data/lib/devise-security/patches/sessions_controller_captcha.rb +0 -26
- data/lib/devise-security/patches/unlocks_controller_captcha.rb +0 -22
- data/lib/devise-security/patches/unlocks_controller_security_question.rb +0 -25
- data/test/dummy/app/controllers/foos_controller.rb +0 -0
- data/test/dummy/lib/shared_user_without_email.rb +0 -29
- data/test/dummy/log/development.log +0 -0
data/test/i18n_test.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'i18n/tasks'
|
|
4
|
+
|
|
5
|
+
class I18nTest < ActiveSupport::TestCase
|
|
6
|
+
def setup
|
|
7
|
+
@i18n = I18n::Tasks::BaseTask.new
|
|
8
|
+
@missing_keys = @i18n.missing_keys
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_no_missing_keys
|
|
12
|
+
assert_empty @missing_keys,
|
|
13
|
+
"Missing #{@missing_keys.leaves.count} i18n keys, run `i18n-tasks missing' to show them"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_no_inconsistent_interpolations
|
|
17
|
+
inconsistent_interpolations = @i18n.inconsistent_interpolations
|
|
18
|
+
error_message = "#{inconsistent_interpolations.leaves.count} i18n keys have inconsistent interpolations.\n" \
|
|
19
|
+
"Please run `i18n-tasks check-consistent-interpolations' to show them"
|
|
20
|
+
assert_empty inconsistent_interpolations, error_message
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
|
|
5
|
+
class TestParanoidVerificationCodeWorkflow < ActionDispatch::IntegrationTest
|
|
6
|
+
include IntegrationHelpers
|
|
7
|
+
|
|
8
|
+
setup do
|
|
9
|
+
@user = User.create!(
|
|
10
|
+
password: 'passWord1',
|
|
11
|
+
password_confirmation: 'passWord1',
|
|
12
|
+
email: 'bob@microsoft.com',
|
|
13
|
+
paranoid_verification_code: 'cookies'
|
|
14
|
+
) # the default verification code is nil
|
|
15
|
+
@user.confirm
|
|
16
|
+
|
|
17
|
+
assert @user.valid?
|
|
18
|
+
assert @user.need_paranoid_verification?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
test 'sign in and check paranoid verification code' do
|
|
22
|
+
sign_in(@user)
|
|
23
|
+
assert_redirected_to(root_path)
|
|
24
|
+
follow_redirect!
|
|
25
|
+
assert_redirected_to(user_paranoid_verification_code_path)
|
|
26
|
+
# @note This is not the same controller used by Devise for password changes
|
|
27
|
+
patch '/users/verification_code', params: {
|
|
28
|
+
user: {
|
|
29
|
+
paranoid_verification_code: 'cookies'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
assert_redirected_to(root_path)
|
|
33
|
+
@user.reload
|
|
34
|
+
assert_not @user.need_paranoid_verification?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test 'sign in and paranoid verification code is checked before redirect completes' do
|
|
38
|
+
sign_in(@user)
|
|
39
|
+
assert_redirected_to(root_path)
|
|
40
|
+
|
|
41
|
+
# simulates an external process verifying the paranoid verification code
|
|
42
|
+
@user.update(paranoid_verification_code: nil)
|
|
43
|
+
assert_not @user.need_paranoid_verification?
|
|
44
|
+
|
|
45
|
+
follow_redirect!
|
|
46
|
+
assert_response :success
|
|
47
|
+
|
|
48
|
+
# if the paranoid verification code is not empty/nil at this point they will be redirected to the
|
|
49
|
+
# paranoid verification code change controller.
|
|
50
|
+
get root_path
|
|
51
|
+
assert_response :success
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -26,8 +26,8 @@ class TestPasswordExpirableWorkflow < ActionDispatch::IntegrationTest
|
|
|
26
26
|
user: {
|
|
27
27
|
current_password: 'passWord1',
|
|
28
28
|
password: 'Password12345!',
|
|
29
|
-
password_confirmation: 'Password12345!'
|
|
30
|
-
}
|
|
29
|
+
password_confirmation: 'Password12345!'
|
|
30
|
+
}
|
|
31
31
|
}
|
|
32
32
|
assert_redirected_to(root_path)
|
|
33
33
|
@user.reload
|
|
@@ -31,7 +31,7 @@ class TestSessionLimitableWorkflow < ActionDispatch::IntegrationTest
|
|
|
31
31
|
session.assert_redirected_to '/'
|
|
32
32
|
session.get widgets_path
|
|
33
33
|
session.assert_response(:success)
|
|
34
|
-
assert_equal session.response.body
|
|
34
|
+
assert_equal('success', session.response.body)
|
|
35
35
|
assert_not_nil @user.reload.unique_session_id
|
|
36
36
|
end
|
|
37
37
|
end
|
|
@@ -46,7 +46,7 @@ class TestSessionLimitableWorkflow < ActionDispatch::IntegrationTest
|
|
|
46
46
|
session.assert_redirected_to '/'
|
|
47
47
|
session.get widgets_path
|
|
48
48
|
session.assert_response(:success)
|
|
49
|
-
assert_equal session.response.body
|
|
49
|
+
assert_equal('success', session.response.body)
|
|
50
50
|
unique_session_id = @user.reload.unique_session_id
|
|
51
51
|
assert_not_nil unique_session_id
|
|
52
52
|
end
|
|
@@ -56,7 +56,7 @@ class TestSessionLimitableWorkflow < ActionDispatch::IntegrationTest
|
|
|
56
56
|
session.assert_redirected_to '/'
|
|
57
57
|
session.get widgets_path
|
|
58
58
|
session.assert_response(:success)
|
|
59
|
-
assert_equal session.response.body
|
|
59
|
+
assert_equal('success', session.response.body)
|
|
60
60
|
assert_not_equal unique_session_id, @user.reload.unique_session_id
|
|
61
61
|
end
|
|
62
62
|
|
|
@@ -6,12 +6,15 @@ module IntegrationHelpers
|
|
|
6
6
|
# @param session [ActionDispatch::Integration::Session]
|
|
7
7
|
# @return [void]
|
|
8
8
|
def sign_in(user, session = integration_session)
|
|
9
|
-
session.post
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
session.post(
|
|
10
|
+
new_user_session_path,
|
|
11
|
+
params: {
|
|
12
|
+
user: {
|
|
13
|
+
email: user.email,
|
|
14
|
+
password: user.password
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
)
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
# attempt to login the user with a bad password. This will exercise all the Warden Hooks
|
|
@@ -19,11 +22,14 @@ module IntegrationHelpers
|
|
|
19
22
|
# @param session [ActionDispatch::Integration::Session]
|
|
20
23
|
# @return [void]
|
|
21
24
|
def failed_sign_in(user, session)
|
|
22
|
-
session.post
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
session.post(
|
|
26
|
+
new_user_session_path,
|
|
27
|
+
params: {
|
|
28
|
+
user: {
|
|
29
|
+
email: user.email,
|
|
30
|
+
password: 'bad-password'
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
)
|
|
28
34
|
end
|
|
29
35
|
end
|
|
@@ -37,8 +37,8 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
37
37
|
|
|
38
38
|
assert_not(model.valid?)
|
|
39
39
|
assert_equal(
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
{ password: ['must contain at least one upper-case letter'] },
|
|
41
|
+
model.errors.messages
|
|
42
42
|
)
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -51,8 +51,8 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
51
51
|
|
|
52
52
|
assert_not(model.valid?)
|
|
53
53
|
assert_equal(
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
{ password: ['must contain at least 2 upper-case letters'] },
|
|
55
|
+
model.errors.messages
|
|
56
56
|
)
|
|
57
57
|
end
|
|
58
58
|
|
|
@@ -65,7 +65,7 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
65
65
|
|
|
66
66
|
assert_not(model.valid?)
|
|
67
67
|
assert_equal(
|
|
68
|
-
|
|
68
|
+
{ password: ['must contain at least one digit'] }, model.errors.messages
|
|
69
69
|
)
|
|
70
70
|
end
|
|
71
71
|
|
|
@@ -78,7 +78,7 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
78
78
|
|
|
79
79
|
assert_not(model.valid?)
|
|
80
80
|
assert_equal(
|
|
81
|
-
|
|
81
|
+
{ password: ['must contain at least 2 digits'] }, model.errors.messages
|
|
82
82
|
)
|
|
83
83
|
end
|
|
84
84
|
|
|
@@ -91,7 +91,7 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
91
91
|
|
|
92
92
|
assert_not(model.valid?)
|
|
93
93
|
assert_equal(
|
|
94
|
-
|
|
94
|
+
{ password: ['must contain at least one digit'] }, model.errors.messages
|
|
95
95
|
)
|
|
96
96
|
end
|
|
97
97
|
|
|
@@ -104,7 +104,7 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
104
104
|
|
|
105
105
|
assert_not(model.valid?)
|
|
106
106
|
assert_equal(
|
|
107
|
-
|
|
107
|
+
{ password: ['must contain at least 2 digits'] }, model.errors.messages
|
|
108
108
|
)
|
|
109
109
|
end
|
|
110
110
|
|
|
@@ -117,8 +117,8 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
117
117
|
|
|
118
118
|
assert_not(model.valid?)
|
|
119
119
|
assert_equal(
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
{ password: ['must contain at least one lower-case letter'] },
|
|
121
|
+
model.errors.messages
|
|
122
122
|
)
|
|
123
123
|
end
|
|
124
124
|
|
|
@@ -131,8 +131,8 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
131
131
|
|
|
132
132
|
assert_not(model.valid?)
|
|
133
133
|
assert_equal(
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
{ password: ['must contain at least 2 lower-case letters'] },
|
|
135
|
+
model.errors.messages
|
|
136
136
|
)
|
|
137
137
|
end
|
|
138
138
|
|
|
@@ -145,8 +145,8 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
145
145
|
|
|
146
146
|
assert_not(model.valid?)
|
|
147
147
|
assert_equal(
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
{ password: ['must contain at least one punctuation mark or symbol'] },
|
|
149
|
+
model.errors.messages
|
|
150
150
|
)
|
|
151
151
|
end
|
|
152
152
|
|
|
@@ -159,8 +159,8 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
159
159
|
|
|
160
160
|
assert_not(model.valid?)
|
|
161
161
|
assert_equal(
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
{ password: ['must contain at least 2 punctuation marks or symbols'] },
|
|
163
|
+
model.errors.messages
|
|
164
164
|
)
|
|
165
165
|
end
|
|
166
166
|
|
|
@@ -173,8 +173,8 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
173
173
|
|
|
174
174
|
assert_not(model.valid?)
|
|
175
175
|
assert_equal(
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
{ password: ['must contain at least one punctuation mark or symbol'] },
|
|
177
|
+
model.errors.messages
|
|
178
178
|
)
|
|
179
179
|
end
|
|
180
180
|
|
|
@@ -187,8 +187,8 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
187
187
|
|
|
188
188
|
assert_not(model.valid?)
|
|
189
189
|
assert_equal(
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
{ password: ['must contain at least 2 punctuation marks or symbols'] },
|
|
191
|
+
model.errors.messages
|
|
192
192
|
)
|
|
193
193
|
end
|
|
194
194
|
|
|
@@ -201,15 +201,15 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
201
201
|
|
|
202
202
|
assert_not(model.valid?)
|
|
203
203
|
assert_equal(
|
|
204
|
-
model.errors.messages,
|
|
205
204
|
{
|
|
206
205
|
password:
|
|
207
206
|
[
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
'must contain at least one digit',
|
|
208
|
+
'must contain at least one punctuation mark or symbol',
|
|
209
|
+
'must contain at least one upper-case letter'
|
|
211
210
|
]
|
|
212
|
-
}
|
|
211
|
+
},
|
|
212
|
+
model.errors.messages
|
|
213
213
|
)
|
|
214
214
|
end
|
|
215
215
|
|
|
@@ -218,15 +218,15 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
218
218
|
|
|
219
219
|
assert_not(model.valid?)
|
|
220
220
|
assert_equal(
|
|
221
|
-
model.errors.messages,
|
|
222
221
|
{
|
|
223
222
|
password:
|
|
224
223
|
[
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
224
|
+
'must contain at least one digit',
|
|
225
|
+
'must contain at least one lower-case letter',
|
|
226
|
+
'must contain at least one punctuation mark or symbol'
|
|
228
227
|
]
|
|
229
|
-
}
|
|
228
|
+
},
|
|
229
|
+
model.errors.messages
|
|
230
230
|
)
|
|
231
231
|
end
|
|
232
232
|
|
|
@@ -235,15 +235,15 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
235
235
|
|
|
236
236
|
assert_not(model.valid?)
|
|
237
237
|
assert_equal(
|
|
238
|
-
model.errors.messages,
|
|
239
238
|
{
|
|
240
239
|
password:
|
|
241
240
|
[
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
'must contain at least one lower-case letter',
|
|
242
|
+
'must contain at least one punctuation mark or symbol',
|
|
243
|
+
'must contain at least one upper-case letter'
|
|
245
244
|
]
|
|
246
|
-
}
|
|
245
|
+
},
|
|
246
|
+
model.errors.messages
|
|
247
247
|
)
|
|
248
248
|
end
|
|
249
249
|
|
|
@@ -252,15 +252,15 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
252
252
|
|
|
253
253
|
assert_not(model.valid?)
|
|
254
254
|
assert_equal(
|
|
255
|
-
model.errors.messages,
|
|
256
255
|
{
|
|
257
256
|
password:
|
|
258
257
|
[
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
258
|
+
'must contain at least one digit',
|
|
259
|
+
'must contain at least one lower-case letter',
|
|
260
|
+
'must contain at least one upper-case letter'
|
|
262
261
|
]
|
|
263
|
-
}
|
|
262
|
+
},
|
|
263
|
+
model.errors.messages
|
|
264
264
|
)
|
|
265
265
|
end
|
|
266
266
|
|
|
@@ -269,7 +269,8 @@ class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
|
|
269
269
|
|
|
270
270
|
assert_not(model.valid?)
|
|
271
271
|
assert_equal(
|
|
272
|
-
|
|
272
|
+
{ password: ['must contain at least one digit'] },
|
|
273
|
+
model.errors.messages
|
|
273
274
|
)
|
|
274
275
|
end
|
|
275
276
|
|
|
@@ -51,7 +51,7 @@ class TestDatabaseAuthenticatablePatch < ActiveSupport::TestCase
|
|
|
51
51
|
}
|
|
52
52
|
)
|
|
53
53
|
|
|
54
|
-
assert_equal([
|
|
54
|
+
assert_equal(['Current password is invalid'], user.errors.full_messages)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
test 'does not update if password is missing' do
|
|
@@ -71,7 +71,7 @@ class TestDatabaseAuthenticatablePatch < ActiveSupport::TestCase
|
|
|
71
71
|
test 'does not update if password is invalid and mismatches confirmation' do
|
|
72
72
|
user = create_user
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
user.update_with_password(
|
|
75
75
|
{
|
|
76
76
|
current_password: 'Password1!',
|
|
77
77
|
password: 'f',
|
|
@@ -93,7 +93,7 @@ class TestDatabaseAuthenticatablePatch < ActiveSupport::TestCase
|
|
|
93
93
|
test 'does not update if password is invalid and matches confirmation' do
|
|
94
94
|
user = create_user
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
user.update_with_password(
|
|
97
97
|
{
|
|
98
98
|
current_password: 'Password1!',
|
|
99
99
|
password: 'f',
|
|
@@ -6,12 +6,12 @@ class TestParanoidVerification < ActiveSupport::TestCase
|
|
|
6
6
|
test 'need to paranoid verify if code present' do
|
|
7
7
|
user = User.new
|
|
8
8
|
user.generate_paranoid_code
|
|
9
|
-
|
|
9
|
+
assert(user.need_paranoid_verification?)
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
test 'no need to paranoid verify if no code' do
|
|
13
13
|
user = User.new
|
|
14
|
-
|
|
14
|
+
assert_not(user.need_paranoid_verification?)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
test 'generate code' do
|
|
@@ -29,23 +29,23 @@ class TestParanoidVerification < ActiveSupport::TestCase
|
|
|
29
29
|
user.generate_paranoid_code
|
|
30
30
|
# default generator generates 5 char string
|
|
31
31
|
assert_equal(user.paranoid_verification_code.class, String)
|
|
32
|
-
assert_equal(user.paranoid_verification_code.length
|
|
32
|
+
assert_equal(5, user.paranoid_verification_code.length)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
test 'when code match upon verify code, should mark record that it\'s no loger needed to verify' do
|
|
36
36
|
user = User.new(paranoid_verification_code: 'abcde')
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
assert(user.need_paranoid_verification?)
|
|
39
39
|
user.verify_code('abcde')
|
|
40
|
-
|
|
40
|
+
assert_not(user.need_paranoid_verification?)
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
test 'when code match upon verify code, should no longer need verification' do
|
|
44
44
|
user = User.new(paranoid_verification_code: 'abcde')
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
assert(user.need_paranoid_verification?)
|
|
47
47
|
user.verify_code('abcde')
|
|
48
|
-
|
|
48
|
+
assert_not(user.need_paranoid_verification?)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
test 'when code match upon verification code, should set when verification was accepted' do
|
|
@@ -57,7 +57,7 @@ class TestParanoidVerification < ActiveSupport::TestCase
|
|
|
57
57
|
test 'when code not match upon verify code, should still need verification' do
|
|
58
58
|
user = User.new(paranoid_verification_code: 'abcde')
|
|
59
59
|
user.verify_code('wrong')
|
|
60
|
-
|
|
60
|
+
assert(user.need_paranoid_verification?)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
test 'when code not match upon verification code, should not set paranoid_verified_at' do
|
|
@@ -47,7 +47,7 @@ class TestPasswordArchivable < ActiveSupport::TestCase
|
|
|
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_nil user.password_changed_at
|
|
51
51
|
assert_not user.password_change_requested?
|
|
52
52
|
assert_not user.password_expired?
|
|
53
53
|
user.save
|
|
@@ -112,7 +112,7 @@ class TestSecureValidatable < ActiveSupport::TestCase
|
|
|
112
112
|
user.password_confirmation = nil
|
|
113
113
|
|
|
114
114
|
assert user.invalid?
|
|
115
|
-
assert_equal(["Password can't be blank"],user.errors.full_messages)
|
|
115
|
+
assert_equal(["Password can't be blank"], user.errors.full_messages)
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
test 'password_confirmation must match password' do
|
|
@@ -213,35 +213,27 @@ class TestSecureValidatable < ActiveSupport::TestCase
|
|
|
213
213
|
options = {
|
|
214
214
|
email: 'bob@microsoft.com',
|
|
215
215
|
password: 'Password1!',
|
|
216
|
-
password_confirmation: 'Password1!'
|
|
216
|
+
password_confirmation: 'Password1!'
|
|
217
217
|
}
|
|
218
218
|
User.create!(options)
|
|
219
219
|
user = User.new(options)
|
|
220
220
|
|
|
221
221
|
assert user.invalid?
|
|
222
|
-
|
|
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
|
|
222
|
+
assert_equal(['Email has already been taken'], user.errors.full_messages)
|
|
227
223
|
end
|
|
228
224
|
|
|
229
225
|
test "new user can't use existing user's email with different casing" do
|
|
230
226
|
options = {
|
|
231
227
|
email: 'bob@microsoft.com',
|
|
232
228
|
password: 'Password1!',
|
|
233
|
-
password_confirmation: 'Password1!'
|
|
229
|
+
password_confirmation: 'Password1!'
|
|
234
230
|
}
|
|
235
231
|
User.create!(options)
|
|
236
232
|
options[:email] = 'BOB@MICROSOFT.COM'
|
|
237
233
|
user = User.new(options)
|
|
238
234
|
|
|
239
235
|
assert user.invalid?
|
|
240
|
-
|
|
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
|
|
236
|
+
assert_equal(['Email has already been taken'], user.errors.full_messages)
|
|
245
237
|
end
|
|
246
238
|
|
|
247
239
|
test 'password cannot equal email for new user' do
|
|
@@ -10,13 +10,13 @@ class TestSessionLimitable < ActiveSupport::TestCase
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
test 'check is not skipped by default' do
|
|
13
|
-
user = User.
|
|
14
|
-
|
|
13
|
+
user = User.new(email: 'bob@microsoft.com', password: 'password1', password_confirmation: 'password1')
|
|
14
|
+
assert_not(user.skip_session_limitable?)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
test 'default check can be overridden by record instance' do
|
|
18
|
-
modified_user = ModifiedUser.
|
|
19
|
-
|
|
18
|
+
modified_user = ModifiedUser.new(email: 'bob2@microsoft.com', password: 'password1', password_confirmation: 'password1')
|
|
19
|
+
assert(modified_user.skip_session_limitable?)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
class SessionLimitableUser < User
|
|
@@ -34,7 +34,7 @@ class TestSessionLimitable < ActiveSupport::TestCase
|
|
|
34
34
|
assert_nil user.unique_session_id
|
|
35
35
|
user.update_unique_session_id!('unique_value')
|
|
36
36
|
user.reload
|
|
37
|
-
assert_equal user.unique_session_id
|
|
37
|
+
assert_equal('unique_value', user.unique_session_id)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
test '#update_unique_session_id!(value) updates invalid record atomically' do
|
|
@@ -45,8 +45,8 @@ class TestSessionLimitable < ActiveSupport::TestCase
|
|
|
45
45
|
assert_nil user.unique_session_id
|
|
46
46
|
user.update_unique_session_id!('unique_value')
|
|
47
47
|
user.reload
|
|
48
|
-
assert_equal
|
|
49
|
-
assert_equal user.unique_session_id
|
|
48
|
+
assert_equal('bob@microsoft.com', user.email)
|
|
49
|
+
assert_equal('unique_value', user.unique_session_id)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
test '#update_unique_session_id!(value) raises an exception on an unpersisted record' do
|
|
@@ -46,4 +46,7 @@ Devise.setup do |config|
|
|
|
46
46
|
|
|
47
47
|
# Allow password to equal the email
|
|
48
48
|
# config.allow_passwords_equal_to_email = false
|
|
49
|
+
|
|
50
|
+
# paranoid_verification will regenerate verification code after failed attempt
|
|
51
|
+
# config.paranoid_code_regenerate_after_attempt = 10
|
|
49
52
|
end
|
|
@@ -31,6 +31,7 @@ by:
|
|
|
31
31
|
paranoid_verify:
|
|
32
32
|
code_required: 'Калі ласка, увядзіце код, атрыманы ад нашай каманды падтрымкі'
|
|
33
33
|
paranoid_verification_code:
|
|
34
|
+
updated: Код спраўджання прыняты
|
|
34
35
|
show:
|
|
35
36
|
submit_verification_code: 'Увод кода пацверджання'
|
|
36
37
|
verification_code: 'Код пацверджання'
|
|
@@ -7,15 +7,19 @@ cs:
|
|
|
7
7
|
password_complexity:
|
|
8
8
|
digit:
|
|
9
9
|
one: musí obsahovat alespoň jednu číslici
|
|
10
|
+
few: musí obsahovat alespoň %{count} číslice
|
|
10
11
|
other: musí obsahovat alespoň %{count} číslice
|
|
11
12
|
lower:
|
|
12
13
|
one: musí obsahovat alespoň jedno malé písmeno
|
|
14
|
+
few: musí obsahovat alespoň %{count} malé písmena
|
|
13
15
|
other: musí obsahovat alespoň %{count} malé písmena
|
|
14
16
|
symbol:
|
|
15
17
|
one: musí obsahovat alespoň jedno interpunkční znaménko nebo symbol
|
|
18
|
+
few: musí obsahovat alespoň %{count} interpunkční znaménka nebo symboly
|
|
16
19
|
other: musí obsahovat alespoň %{count} interpunkční znaménka nebo symboly
|
|
17
20
|
upper:
|
|
18
21
|
one: musí obsahovat alespoň jedno velké písmeno
|
|
22
|
+
few: musí obsahovat alespoň %{count} velké písmena
|
|
19
23
|
other: musí obsahovat alespoň %{count} velké písmena
|
|
20
24
|
devise:
|
|
21
25
|
invalid_captcha: Chybná captcha.
|
|
@@ -23,6 +27,7 @@ cs:
|
|
|
23
27
|
paranoid_verify:
|
|
24
28
|
code_required: Zadejte kód, který poskytla naše podpora
|
|
25
29
|
paranoid_verification_code:
|
|
30
|
+
updated: Ověřovací kód přijat
|
|
26
31
|
show:
|
|
27
32
|
submit_verification_code: Odeslat ověřovací kód
|
|
28
33
|
verification_code: Ověřovací kód
|
|
@@ -23,6 +23,7 @@ de:
|
|
|
23
23
|
paranoid_verify:
|
|
24
24
|
code_required: 'Bitte geben Sie den Code ein, den unser Support-Team zur Verfügung gestellt hat.'
|
|
25
25
|
paranoid_verification_code:
|
|
26
|
+
updated: Bestätigungscode akzeptiert
|
|
26
27
|
show:
|
|
27
28
|
submit_verification_code: Bestätigungscode eingeben
|
|
28
29
|
verification_code: Bestätigungscode
|
|
@@ -22,9 +22,21 @@ es:
|
|
|
22
22
|
invalid_security_question: 'La respuesta a la pregunta de seguridad fue incorrecta.'
|
|
23
23
|
paranoid_verify:
|
|
24
24
|
code_required: 'Por favor ingrese el código provisto por nuestro equipo de soporte'
|
|
25
|
+
paranoid_verification_code:
|
|
26
|
+
updated: Se acepta el código de verificación
|
|
27
|
+
show:
|
|
28
|
+
submit_verification_code: Envíe el código de verificación
|
|
29
|
+
verification_code: Código de verificación
|
|
30
|
+
submit: Entregar
|
|
25
31
|
password_expired:
|
|
26
32
|
updated: 'Su nueva contraseña ha sido guardada.'
|
|
27
33
|
change_required: 'Su contraseña ha expirado. Por favor renueve su contraseña.'
|
|
34
|
+
show:
|
|
35
|
+
renew_your_password: Renueve su contraseña
|
|
36
|
+
current_password: Actual contraseña
|
|
37
|
+
new_password: Nueva contraseña
|
|
38
|
+
new_password_confirmation: Confirmar nueva contraseña
|
|
39
|
+
change_my_password: Cambiar mi contraseña
|
|
28
40
|
failure:
|
|
29
41
|
session_limited: 'Sus credenciales de inicio de sesión fueron usadas en otro navegador. Por favor inicie sesión nuevamente para continuar en este navegador.'
|
|
30
42
|
expired: 'Su cuenta ha expirado debido a inactividad. Por favor contacte al administrador de la aplicación.'
|