devise-security 0.16.0 → 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.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/app/controllers/devise/paranoid_verification_code_controller.rb +13 -1
- data/app/controllers/devise/password_expired_controller.rb +14 -1
- data/config/locales/bg.yml +41 -0
- data/config/locales/de.yml +2 -0
- data/config/locales/en.yml +2 -1
- data/lib/devise-security/models/database_authenticatable_patch.rb +15 -5
- data/lib/devise-security/models/password_archivable.rb +2 -2
- data/lib/devise-security/models/secure_validatable.rb +51 -15
- data/lib/devise-security/validators/password_complexity_validator.rb +53 -26
- data/lib/devise-security/version.rb +1 -1
- data/lib/devise-security.rb +7 -2
- data/lib/generators/templates/devise_security.rb +3 -1
- data/test/controllers/test_paranoid_verification_code_controller.rb +68 -0
- data/test/controllers/test_password_expired_controller.rb +38 -0
- data/test/dummy/app/controllers/overrides/paranoid_verification_code_controller.rb +7 -0
- data/test/dummy/app/controllers/overrides/password_expired_controller.rb +7 -0
- data/test/dummy/app/controllers/widgets_controller.rb +3 -0
- data/test/dummy/app/models/application_user_record.rb +2 -1
- data/test/dummy/app/models/mongoid/confirmable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/database_authenticable_fields.rb +4 -3
- data/test/dummy/app/models/mongoid/expirable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/lockable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/mappings.rb +4 -2
- data/test/dummy/app/models/mongoid/omniauthable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/paranoid_verification_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/password_archivable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/password_expirable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/recoverable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/registerable_fields.rb +4 -2
- data/test/dummy/app/models/mongoid/rememberable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/secure_validatable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/security_questionable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/session_limitable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/timeoutable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/trackable_fields.rb +2 -0
- data/test/dummy/app/models/mongoid/validatable_fields.rb +2 -0
- data/test/dummy/app/models/paranoid_verification_user.rb +26 -0
- data/test/dummy/app/models/password_expired_user.rb +26 -0
- data/test/dummy/app/models/user.rb +1 -2
- data/test/dummy/app/models/widget.rb +1 -3
- data/test/dummy/app/mongoid/one_user.rb +5 -5
- data/test/dummy/app/mongoid/user_on_engine.rb +2 -2
- data/test/dummy/app/mongoid/user_on_main_app.rb +2 -2
- data/test/dummy/app/mongoid/user_with_validations.rb +3 -3
- data/test/dummy/app/mongoid/user_without_email.rb +3 -3
- data/test/dummy/config/application.rb +4 -4
- data/test/dummy/config/boot.rb +1 -1
- data/test/dummy/config/environment.rb +1 -1
- data/test/dummy/config/locales/en.yml +10 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/dummy/db/migrate/20120508165529_create_tables.rb +3 -3
- data/test/dummy/lib/shared_expirable_columns.rb +1 -0
- data/test/dummy/lib/shared_security_questions_fields.rb +1 -0
- data/test/dummy/lib/shared_user.rb +17 -6
- data/test/dummy/lib/shared_user_without_email.rb +2 -1
- data/test/dummy/lib/shared_user_without_omniauth.rb +12 -3
- data/test/dummy/lib/shared_verification_fields.rb +1 -0
- data/test/dummy/log/development.log +0 -883
- data/test/dummy/log/test.log +95414 -15570
- data/test/integration/test_session_limitable_workflow.rb +2 -0
- data/test/orm/active_record.rb +7 -7
- data/test/test_compatibility.rb +2 -0
- data/test/test_complexity_validator.rb +246 -37
- data/test/test_database_authenticatable_patch.rb +146 -0
- data/test/test_helper.rb +7 -8
- data/test/test_install_generator.rb +1 -1
- data/test/test_paranoid_verification.rb +0 -1
- data/test/test_password_archivable.rb +34 -11
- data/test/test_password_expirable.rb +26 -26
- data/test/test_secure_validatable.rb +273 -107
- data/test/test_secure_validatable_overrides.rb +185 -0
- data/test/test_session_limitable.rb +2 -2
- data/test/tmp/config/initializers/{devise-security.rb → devise_security.rb} +3 -1
- data/test/tmp/config/locales/devise.security_extension.de.yml +2 -0
- data/test/tmp/config/locales/devise.security_extension.en.yml +2 -1
- data/test/tmp/config/locales/devise.security_extension.hi.yml +20 -20
- metadata +42 -19
- data/test/dummy/app/models/secure_user.rb +0 -9
data/test/orm/active_record.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_record'
|
2
4
|
|
3
5
|
ActiveRecord::Migration.verbose = false
|
4
6
|
ActiveRecord::Base.logger = Logger.new(nil)
|
5
|
-
|
6
|
-
|
7
|
-
ActiveRecord::MigrationContext.new(File.expand_path('
|
8
|
-
|
9
|
-
ActiveRecord::MigrationContext.new(File.expand_path('
|
10
|
-
else
|
11
|
-
ActiveRecord::Migrator.migrate(File.expand_path('../../dummy/db/migrate', __FILE__))
|
7
|
+
|
8
|
+
if Rails.gem_version >= Gem::Version.new('6.0.0')
|
9
|
+
ActiveRecord::MigrationContext.new(File.expand_path('../dummy/db/migrate', __dir__), ActiveRecord::SchemaMigration).migrate
|
10
|
+
elsif Rails.gem_version >= Gem::Version.new('5.2.0')
|
11
|
+
ActiveRecord::MigrationContext.new(File.expand_path('../dummy/db/migrate', __dir__)).migrate
|
12
12
|
end
|
13
13
|
|
14
14
|
DatabaseCleaner[:active_record].strategy = :transaction
|
data/test/test_compatibility.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'test_helper'
|
2
4
|
|
3
|
-
class PasswordComplexityValidatorTest <
|
5
|
+
class PasswordComplexityValidatorTest < ActiveSupport::TestCase
|
4
6
|
class ModelWithPassword
|
5
7
|
include ActiveModel::Validations
|
6
8
|
|
@@ -15,58 +17,265 @@ class PasswordComplexityValidatorTest < Minitest::Test
|
|
15
17
|
ModelWithPassword.clear_validators!
|
16
18
|
end
|
17
19
|
|
20
|
+
def create_model(password, opts = {})
|
21
|
+
ModelWithPassword.validates(
|
22
|
+
:password, 'devise_security/password_complexity': opts
|
23
|
+
)
|
24
|
+
ModelWithPassword.new(password)
|
25
|
+
end
|
26
|
+
|
18
27
|
def test_with_no_rules_anything_goes
|
19
|
-
assert(
|
28
|
+
assert(create_model('aaaa').valid?)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_allows_blank
|
32
|
+
assert(create_model('', { upper: 1 }).valid?)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_enforces_uppercase_invalid
|
36
|
+
model = create_model('aaaa', { upper: 1 })
|
37
|
+
|
38
|
+
assert_not(model.valid?)
|
39
|
+
assert_equal(
|
40
|
+
model.errors.messages,
|
41
|
+
{ password: ["must contain at least one upper-case letter"] }
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_enforces_uppercase_valid
|
46
|
+
assert(create_model('Aaaa', { upper: 1 }).valid?)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_enforces_uppercase_count_invalid
|
50
|
+
model = create_model('Aaaa', { upper: 2 })
|
51
|
+
|
52
|
+
assert_not(model.valid?)
|
53
|
+
assert_equal(
|
54
|
+
model.errors.messages,
|
55
|
+
{ password: ["must contain at least 2 upper-case letters"] }
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_enforces_uppercase_count_valid
|
60
|
+
assert(create_model('AAaa', { upper: 2 }).valid?)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_enforces_digit_invalid
|
64
|
+
model = create_model('aaaa', { digit: 1 })
|
65
|
+
|
66
|
+
assert_not(model.valid?)
|
67
|
+
assert_equal(
|
68
|
+
model.errors.messages, { password: ["must contain at least one digit"] }
|
69
|
+
)
|
20
70
|
end
|
21
71
|
|
22
|
-
def
|
23
|
-
|
24
|
-
refute(ModelWithPassword.new('aaaa').valid?)
|
25
|
-
assert(ModelWithPassword.new('Aaaa').valid?)
|
72
|
+
def test_enforces_digit_valid
|
73
|
+
assert(create_model('1aaa', { digit: 1 }).valid?)
|
26
74
|
end
|
27
75
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
76
|
+
def test_enforces_digit_count_invalid
|
77
|
+
model = create_model('1aaa', { digit: 2 })
|
78
|
+
|
79
|
+
assert_not(model.valid?)
|
80
|
+
assert_equal(
|
81
|
+
model.errors.messages, { password: ["must contain at least 2 digits"] }
|
82
|
+
)
|
32
83
|
end
|
33
84
|
|
34
|
-
def
|
35
|
-
|
36
|
-
refute(ModelWithPassword.new('aaaa').valid?)
|
37
|
-
assert(ModelWithPassword.new('aaa1').valid?)
|
85
|
+
def test_enforces_digit_count_valid
|
86
|
+
assert(create_model('11aa', { digit: 2 }).valid?)
|
38
87
|
end
|
39
88
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
89
|
+
def test_enforces_digits_invalid
|
90
|
+
model = create_model('aaaa', { digits: 1 })
|
91
|
+
|
92
|
+
assert_not(model.valid?)
|
93
|
+
assert_equal(
|
94
|
+
model.errors.messages, { password: ["must contain at least one digit"] }
|
95
|
+
)
|
44
96
|
end
|
45
97
|
|
46
|
-
def
|
47
|
-
|
48
|
-
refute(ModelWithPassword.new('AAAA').valid?)
|
49
|
-
assert(ModelWithPassword.new('AAAa').valid?)
|
98
|
+
def test_enforces_digits_valid
|
99
|
+
assert(create_model('1aaa', { digits: 1 }).valid?)
|
50
100
|
end
|
51
101
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
102
|
+
def test_enforces_digits_count_invalid
|
103
|
+
model = create_model('1aaa', { digits: 2 })
|
104
|
+
|
105
|
+
assert_not(model.valid?)
|
106
|
+
assert_equal(
|
107
|
+
model.errors.messages, { password: ["must contain at least 2 digits"] }
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_enforces_digits_count_valid
|
112
|
+
assert(create_model('11aa', { digits: 2 }).valid?)
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_enforces_lower_invalid
|
116
|
+
model = create_model('AAAA', { lower: 1 })
|
117
|
+
|
118
|
+
assert_not(model.valid?)
|
119
|
+
assert_equal(
|
120
|
+
model.errors.messages,
|
121
|
+
{ password: ["must contain at least one lower-case letter"] }
|
122
|
+
)
|
56
123
|
end
|
57
124
|
|
58
|
-
def
|
59
|
-
|
60
|
-
|
61
|
-
|
125
|
+
def test_enforces_lower_valid
|
126
|
+
assert(create_model('aAAA', { lower: 1 }).valid?)
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_enforces_lower_count_invalid
|
130
|
+
model = create_model('aAAA', { lower: 2 })
|
131
|
+
|
132
|
+
assert_not(model.valid?)
|
133
|
+
assert_equal(
|
134
|
+
model.errors.messages,
|
135
|
+
{ password: ["must contain at least 2 lower-case letters"] }
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_enforces_lower_count_valid
|
140
|
+
assert(create_model('aaAA', { lower: 2 }).valid?)
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_enforces_symbol_invalid
|
144
|
+
model = create_model('aaaa', { symbol: 1 })
|
145
|
+
|
146
|
+
assert_not(model.valid?)
|
147
|
+
assert_equal(
|
148
|
+
model.errors.messages,
|
149
|
+
{ password: ["must contain at least one punctuation mark or symbol"] }
|
150
|
+
)
|
62
151
|
end
|
63
152
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
153
|
+
def test_enforces_symbol_valid
|
154
|
+
assert(create_model('!aaa', { symbol: 1 }).valid?)
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_enforces_symbol_count_invalid
|
158
|
+
model = create_model('!aaa', { symbol: 2 })
|
159
|
+
|
160
|
+
assert_not(model.valid?)
|
161
|
+
assert_equal(
|
162
|
+
model.errors.messages,
|
163
|
+
{ password: ["must contain at least 2 punctuation marks or symbols"] }
|
164
|
+
)
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_enforces_symbol_count_valid
|
168
|
+
assert(create_model('!!aa', { symbol: 2 }).valid?)
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_enforces_symbols_invalid
|
172
|
+
model = create_model('aaaa', { symbols: 1 })
|
173
|
+
|
174
|
+
assert_not(model.valid?)
|
175
|
+
assert_equal(
|
176
|
+
model.errors.messages,
|
177
|
+
{ password: ["must contain at least one punctuation mark or symbol"] }
|
178
|
+
)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_enforces_symbols_valid
|
182
|
+
assert(create_model('!aaa', { symbols: 1 }).valid?)
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_enforces_symbols_count_invalid
|
186
|
+
model = create_model('!aaa', { symbols: 2 })
|
187
|
+
|
188
|
+
assert_not(model.valid?)
|
189
|
+
assert_equal(
|
190
|
+
model.errors.messages,
|
191
|
+
{ password: ["must contain at least 2 punctuation marks or symbols"] }
|
192
|
+
)
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_enforces_symbols_count_valid
|
196
|
+
assert(create_model('!!aa', { symbols: 2 }).valid?)
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_enforces_combination_only_lower_invalid
|
200
|
+
model = create_model('aaaa', { lower: 1, upper: 1, digit: 1, symbol: 1 })
|
201
|
+
|
202
|
+
assert_not(model.valid?)
|
203
|
+
assert_equal(
|
204
|
+
model.errors.messages,
|
205
|
+
{
|
206
|
+
password:
|
207
|
+
[
|
208
|
+
"must contain at least one digit",
|
209
|
+
"must contain at least one punctuation mark or symbol",
|
210
|
+
"must contain at least one upper-case letter"
|
211
|
+
]
|
212
|
+
}
|
213
|
+
)
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_enforces_combination_only_upper_invalid
|
217
|
+
model = create_model('AAAA', { lower: 1, upper: 1, digit: 1, symbol: 1 })
|
218
|
+
|
219
|
+
assert_not(model.valid?)
|
220
|
+
assert_equal(
|
221
|
+
model.errors.messages,
|
222
|
+
{
|
223
|
+
password:
|
224
|
+
[
|
225
|
+
"must contain at least one digit",
|
226
|
+
"must contain at least one lower-case letter",
|
227
|
+
"must contain at least one punctuation mark or symbol"
|
228
|
+
]
|
229
|
+
}
|
230
|
+
)
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_enforces_combination_only_digit_invalid
|
234
|
+
model = create_model('1111', { lower: 1, upper: 1, digit: 1, symbol: 1 })
|
235
|
+
|
236
|
+
assert_not(model.valid?)
|
237
|
+
assert_equal(
|
238
|
+
model.errors.messages,
|
239
|
+
{
|
240
|
+
password:
|
241
|
+
[
|
242
|
+
"must contain at least one lower-case letter",
|
243
|
+
"must contain at least one punctuation mark or symbol",
|
244
|
+
"must contain at least one upper-case letter"
|
245
|
+
]
|
246
|
+
}
|
247
|
+
)
|
248
|
+
end
|
249
|
+
|
250
|
+
def test_enforces_combination_only_symbol_invalid
|
251
|
+
model = create_model('!!!!', { lower: 1, upper: 1, digit: 1, symbol: 1 })
|
252
|
+
|
253
|
+
assert_not(model.valid?)
|
254
|
+
assert_equal(
|
255
|
+
model.errors.messages,
|
256
|
+
{
|
257
|
+
password:
|
258
|
+
[
|
259
|
+
"must contain at least one digit",
|
260
|
+
"must contain at least one lower-case letter",
|
261
|
+
"must contain at least one upper-case letter"
|
262
|
+
]
|
263
|
+
}
|
264
|
+
)
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_enforces_combination_some_but_not_all_invalid
|
268
|
+
model = create_model('aAa!', { lower: 1, upper: 1, digit: 1, symbol: 1 })
|
269
|
+
|
270
|
+
assert_not(model.valid?)
|
271
|
+
assert_equal(
|
272
|
+
model.errors.messages, { password: ["must contain at least one digit"] }
|
273
|
+
)
|
274
|
+
end
|
275
|
+
|
276
|
+
def test_enforces_combination_all_valid
|
277
|
+
model = create_model('aA1!', { lower: 1, upper: 1, digit: 1, symbol: 1 })
|
278
|
+
|
279
|
+
assert(model.valid?)
|
71
280
|
end
|
72
281
|
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class TestDatabaseAuthenticatablePatch < ActiveSupport::TestCase
|
6
|
+
def create_user
|
7
|
+
User.create(
|
8
|
+
email: 'bob@microsoft.com',
|
9
|
+
password: 'Password1!',
|
10
|
+
password_confirmation: 'Password1!'
|
11
|
+
) do |user|
|
12
|
+
user.extend(Devise::Models::DatabaseAuthenticatablePatch)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
test 'updates if all params are present and valid' do
|
17
|
+
user = create_user
|
18
|
+
|
19
|
+
assert(
|
20
|
+
user.update_with_password(
|
21
|
+
{
|
22
|
+
current_password: 'Password1!',
|
23
|
+
password: 'Password2!',
|
24
|
+
password_confirmation: 'Password2!'
|
25
|
+
}
|
26
|
+
)
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
test 'does not update if current_password is missing' do
|
31
|
+
user = create_user
|
32
|
+
|
33
|
+
user.update_with_password(
|
34
|
+
{
|
35
|
+
password: 'Password2!',
|
36
|
+
password_confirmation: 'Password2!'
|
37
|
+
}
|
38
|
+
)
|
39
|
+
|
40
|
+
assert_equal(["Current password can't be blank"], user.errors.full_messages)
|
41
|
+
end
|
42
|
+
|
43
|
+
test 'does not update if current_password is incorrect' do
|
44
|
+
user = create_user
|
45
|
+
|
46
|
+
user.update_with_password(
|
47
|
+
{
|
48
|
+
current_password: 'Password2!',
|
49
|
+
password: 'Password2!',
|
50
|
+
password_confirmation: 'Password2!'
|
51
|
+
}
|
52
|
+
)
|
53
|
+
|
54
|
+
assert_equal(["Current password is invalid"], user.errors.full_messages)
|
55
|
+
end
|
56
|
+
|
57
|
+
test 'does not update if password is missing' do
|
58
|
+
user = create_user
|
59
|
+
|
60
|
+
user.update_with_password(
|
61
|
+
{
|
62
|
+
current_password: 'Password1!',
|
63
|
+
password: '',
|
64
|
+
password_confirmation: 'Password2!'
|
65
|
+
}
|
66
|
+
)
|
67
|
+
|
68
|
+
assert_equal(["Password can't be blank"], user.errors.full_messages)
|
69
|
+
end
|
70
|
+
|
71
|
+
test 'does not update if password is invalid and mismatches confirmation' do
|
72
|
+
user = create_user
|
73
|
+
|
74
|
+
result = user.update_with_password(
|
75
|
+
{
|
76
|
+
current_password: 'Password1!',
|
77
|
+
password: 'f',
|
78
|
+
password_confirmation: 'Password2!'
|
79
|
+
}
|
80
|
+
)
|
81
|
+
|
82
|
+
assert_equal(
|
83
|
+
[
|
84
|
+
"Password confirmation doesn't match Password",
|
85
|
+
'Password is too short (minimum is 7 characters)',
|
86
|
+
'Password must contain at least one digit',
|
87
|
+
'Password must contain at least one upper-case letter'
|
88
|
+
],
|
89
|
+
user.errors.full_messages
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
test 'does not update if password is invalid and matches confirmation' do
|
94
|
+
user = create_user
|
95
|
+
|
96
|
+
result = user.update_with_password(
|
97
|
+
{
|
98
|
+
current_password: 'Password1!',
|
99
|
+
password: 'f',
|
100
|
+
password_confirmation: 'f'
|
101
|
+
}
|
102
|
+
)
|
103
|
+
|
104
|
+
assert_equal(
|
105
|
+
[
|
106
|
+
'Password is too short (minimum is 7 characters)',
|
107
|
+
'Password must contain at least one digit',
|
108
|
+
'Password must contain at least one upper-case letter'
|
109
|
+
],
|
110
|
+
user.errors.full_messages
|
111
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
test 'does not update if password_confirmation is missing' do
|
115
|
+
user = create_user
|
116
|
+
|
117
|
+
user.update_with_password(
|
118
|
+
{
|
119
|
+
current_password: 'Password1!',
|
120
|
+
password: 'Password2!',
|
121
|
+
password_confirmation: ''
|
122
|
+
}
|
123
|
+
)
|
124
|
+
|
125
|
+
assert_equal(
|
126
|
+
["Password confirmation can't be blank"], user.errors.full_messages
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
test 'does not update if password_confirmation is mismatched' do
|
131
|
+
user = create_user
|
132
|
+
|
133
|
+
user.update_with_password(
|
134
|
+
{
|
135
|
+
current_password: 'Password1!',
|
136
|
+
password: 'Password2!',
|
137
|
+
password_confirmation: 'Password3!'
|
138
|
+
}
|
139
|
+
)
|
140
|
+
|
141
|
+
assert_equal(
|
142
|
+
["Password confirmation doesn't match Password"],
|
143
|
+
user.errors.full_messages
|
144
|
+
)
|
145
|
+
end
|
146
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -3,6 +3,13 @@
|
|
3
3
|
ENV['RAILS_ENV'] ||= 'test'
|
4
4
|
|
5
5
|
require 'simplecov'
|
6
|
+
|
7
|
+
if ENV['CI']
|
8
|
+
require 'simplecov-lcov'
|
9
|
+
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
|
10
|
+
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
|
11
|
+
end
|
12
|
+
|
6
13
|
SimpleCov.start do
|
7
14
|
add_filter 'gemfiles'
|
8
15
|
add_filter 'test/dummy/db'
|
@@ -18,14 +25,6 @@ SimpleCov.start do
|
|
18
25
|
add_group 'Tests', 'test'
|
19
26
|
end
|
20
27
|
|
21
|
-
if ENV['CI']
|
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
|
27
|
-
end
|
28
|
-
|
29
28
|
require 'pry'
|
30
29
|
require 'dummy/config/environment'
|
31
30
|
require 'minitest/autorun'
|
@@ -6,7 +6,7 @@ require 'generators/devise_security/install_generator'
|
|
6
6
|
|
7
7
|
class TestInstallGenerator < Rails::Generators::TestCase
|
8
8
|
tests DeviseSecurity::Generators::InstallGenerator
|
9
|
-
destination File.expand_path('
|
9
|
+
destination File.expand_path('tmp', __dir__)
|
10
10
|
setup :prepare_destination
|
11
11
|
|
12
12
|
test 'Assert all files are properly created' do
|
@@ -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,
|
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,
|
45
|
+
set_password(user, 'Password2')
|
47
46
|
assert_equal 1, OldPassword.count
|
48
47
|
|
49
|
-
assert_raises(ORMInvalidRecordException) { set_password(user,
|
50
|
-
set_password(user,
|
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,
|
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,
|
58
|
-
assert set_password(user,
|
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,
|
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
|
-
|
92
|
+
# rotate first password out of archive
|
93
|
+
assert set_password(user, 'Password4')
|
73
94
|
|
74
|
-
|
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
|