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
|
@@ -34,9 +34,11 @@ module Devise
|
|
|
34
34
|
# @return [bool]
|
|
35
35
|
def expired?
|
|
36
36
|
# expired_at set (manually, via cron, etc.)
|
|
37
|
-
return
|
|
37
|
+
return expired_at < Time.now.utc unless expired_at.nil?
|
|
38
|
+
|
|
38
39
|
# if it is not set, check the last activity against configured expire_after time range
|
|
39
|
-
return
|
|
40
|
+
return last_activity_at < self.class.expire_after.ago unless last_activity_at.nil?
|
|
41
|
+
|
|
40
42
|
# if last_activity_at is nil as well, the user has to be 'fresh' and is therefore not expired
|
|
41
43
|
false
|
|
42
44
|
end
|
|
@@ -58,13 +60,13 @@ module Devise
|
|
|
58
60
|
#
|
|
59
61
|
# @return [bool]
|
|
60
62
|
def active_for_authentication?
|
|
61
|
-
super && !
|
|
63
|
+
super && !expired?
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
# The message sym, if {#active_for_authentication?} returns +false+. E.g. needed
|
|
65
67
|
# for i18n.
|
|
66
68
|
def inactive_message
|
|
67
|
-
!
|
|
69
|
+
!expired? ? super : :expired
|
|
68
70
|
end
|
|
69
71
|
|
|
70
72
|
module ClassMethods
|
|
@@ -80,7 +82,6 @@ module Devise
|
|
|
80
82
|
all.each do |u|
|
|
81
83
|
u.expire! if u.expired? && u.expired_at.nil?
|
|
82
84
|
end
|
|
83
|
-
return
|
|
84
85
|
end
|
|
85
86
|
|
|
86
87
|
# Scope method to collect all expired users since +time+ ago
|
|
@@ -20,7 +20,7 @@ module Devise
|
|
|
20
20
|
elsif code == paranoid_verification_code
|
|
21
21
|
attempt = 0
|
|
22
22
|
update_without_password paranoid_verification_code: nil,
|
|
23
|
-
paranoid_verified_at: Time.now,
|
|
23
|
+
paranoid_verified_at: Time.zone.now,
|
|
24
24
|
paranoid_verification_attempt: attempt
|
|
25
25
|
else
|
|
26
26
|
update_without_password paranoid_verification_attempt: attempt
|
|
@@ -32,7 +32,7 @@ module Devise
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def generate_paranoid_code
|
|
35
|
-
update_without_password paranoid_verification_code: Devise.verification_code_generator.call
|
|
35
|
+
update_without_password paranoid_verification_code: Devise.verification_code_generator.call,
|
|
36
36
|
paranoid_verification_attempt: 0
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -35,7 +35,7 @@ module Devise
|
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
# validate
|
|
38
|
+
# validate if the password was used in the past
|
|
39
39
|
# @return [true] if current password was used previously
|
|
40
40
|
# @return [false] if disabled or not previously used
|
|
41
41
|
def password_archive_included?
|
|
@@ -26,7 +26,7 @@ module Devise
|
|
|
26
26
|
already_validated_email = false
|
|
27
27
|
|
|
28
28
|
# validate login in a strict way if not yet validated
|
|
29
|
-
unless
|
|
29
|
+
unless uniqueness_validation_of_login?
|
|
30
30
|
validation_condition = "#{login_attribute}_changed?".to_sym
|
|
31
31
|
|
|
32
32
|
validates login_attribute, uniqueness: {
|
|
@@ -86,10 +86,11 @@ module Devise
|
|
|
86
86
|
|
|
87
87
|
def current_equal_password_validation
|
|
88
88
|
return if new_record? || !will_save_change_to_encrypted_password? || password.blank?
|
|
89
|
+
|
|
89
90
|
dummy = self.class.new(encrypted_password: encrypted_password_was).tap do |user|
|
|
90
91
|
user.password_salt = password_salt_was if respond_to?(:password_salt)
|
|
91
92
|
end
|
|
92
|
-
|
|
93
|
+
errors.add(:password, :equal_to_current_password) if dummy.valid_password?(password)
|
|
93
94
|
end
|
|
94
95
|
|
|
95
96
|
def email_not_equal_password_validation
|
|
@@ -138,10 +139,10 @@ module Devise
|
|
|
138
139
|
|
|
139
140
|
private
|
|
140
141
|
|
|
141
|
-
def
|
|
142
|
+
def uniqueness_validation_of_login?
|
|
142
143
|
validators.any? do |validator|
|
|
143
144
|
validator_orm_klass = DEVISE_ORM == :active_record ? ActiveRecord::Validations::UniquenessValidator : ::Mongoid::Validatable::UniquenessValidator
|
|
144
|
-
validator.
|
|
145
|
+
validator.is_a?(validator_orm_klass) && validator.attributes.include?(login_attribute)
|
|
145
146
|
end
|
|
146
147
|
end
|
|
147
148
|
|
|
@@ -150,7 +151,7 @@ module Devise
|
|
|
150
151
|
end
|
|
151
152
|
|
|
152
153
|
def devise_validation_enabled?
|
|
153
|
-
|
|
154
|
+
ancestors.map(&:to_s).include? 'Devise::Models::Validatable'
|
|
154
155
|
end
|
|
155
156
|
end
|
|
156
157
|
end
|
|
@@ -6,18 +6,24 @@ module DeviseSecurity
|
|
|
6
6
|
autoload :ControllerSecurityQuestion, 'devise-security/patches/controller_security_question'
|
|
7
7
|
|
|
8
8
|
class << self
|
|
9
|
+
# rubocop:disable Metrics/AbcSize
|
|
10
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
11
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
9
12
|
def apply
|
|
10
|
-
Devise::PasswordsController.
|
|
11
|
-
Devise::UnlocksController.
|
|
12
|
-
Devise::ConfirmationsController.
|
|
13
|
+
Devise::PasswordsController.include(Patches::ControllerCaptcha) if Devise.captcha_for_recover || Devise.security_question_for_recover
|
|
14
|
+
Devise::UnlocksController.include(Patches::ControllerCaptcha) if Devise.captcha_for_unlock || Devise.security_question_for_unlock
|
|
15
|
+
Devise::ConfirmationsController.include(Patches::ControllerCaptcha) if Devise.captcha_for_confirmation
|
|
13
16
|
|
|
14
|
-
Devise::PasswordsController.
|
|
15
|
-
Devise::UnlocksController.
|
|
16
|
-
Devise::ConfirmationsController.
|
|
17
|
+
Devise::PasswordsController.include(Patches::ControllerSecurityQuestion) if Devise.security_question_for_recover
|
|
18
|
+
Devise::UnlocksController.include(Patches::ControllerSecurityQuestion) if Devise.security_question_for_unlock
|
|
19
|
+
Devise::ConfirmationsController.include(Patches::ControllerSecurityQuestion) if Devise.security_question_for_confirmation
|
|
17
20
|
|
|
18
|
-
Devise::RegistrationsController.
|
|
19
|
-
Devise::SessionsController.
|
|
21
|
+
Devise::RegistrationsController.include(Patches::ControllerCaptcha) if Devise.captcha_for_sign_up
|
|
22
|
+
Devise::SessionsController.include(Patches::ControllerCaptcha) if Devise.captcha_for_sign_in
|
|
20
23
|
end
|
|
24
|
+
# rubocop:enable Metrics/AbcSize
|
|
25
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
26
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
21
27
|
end
|
|
22
28
|
end
|
|
23
29
|
end
|
|
@@ -2,17 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module ActionDispatch::Routing
|
|
4
4
|
class Mapper
|
|
5
|
-
|
|
6
5
|
protected
|
|
7
6
|
|
|
8
7
|
# route for handle expired passwords
|
|
9
8
|
def devise_password_expired(mapping, controllers)
|
|
10
|
-
resource :password_expired, only: [
|
|
9
|
+
resource :password_expired, only: %i[show update], path: mapping.path_names[:password_expired], controller: controllers[:password_expired]
|
|
11
10
|
end
|
|
12
11
|
|
|
13
12
|
# route for handle paranoid verification
|
|
14
13
|
def devise_verification_code(mapping, controllers)
|
|
15
|
-
resource :paranoid_verification_code, only: [
|
|
14
|
+
resource :paranoid_verification_code, only: %i[show update], path: mapping.path_names[:verification_code], controller: controllers[:paranoid_verification_code]
|
|
16
15
|
end
|
|
17
16
|
end
|
|
18
17
|
end
|
data/lib/devise-security.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
DEVISE_ORM = ENV.fetch('DEVISE_ORM', 'active_record').to_sym unless defined?(DEVISE_ORM)
|
|
3
4
|
|
|
4
|
-
require DEVISE_ORM.to_s if DEVISE_ORM.in? [
|
|
5
|
+
require DEVISE_ORM.to_s if DEVISE_ORM.in? %i[active_record mongoid]
|
|
5
6
|
require 'active_support/core_ext/integer'
|
|
6
7
|
require 'active_support/ordered_hash'
|
|
7
8
|
require 'active_support/concern'
|
|
@@ -6,20 +6,18 @@ module DeviseSecurity
|
|
|
6
6
|
class InstallGenerator < Rails::Generators::Base
|
|
7
7
|
LOCALES = %w[by cs de en es fa fr hi it ja nl pt ru tr uk zh_CN zh_TW].freeze
|
|
8
8
|
|
|
9
|
-
source_root File.expand_path('
|
|
9
|
+
source_root File.expand_path('../templates', __dir__)
|
|
10
10
|
desc 'Install the devise security extension'
|
|
11
11
|
|
|
12
12
|
def copy_initializer
|
|
13
|
-
template('devise_security.rb',
|
|
14
|
-
'config/initializers/devise_security.rb',
|
|
15
|
-
)
|
|
13
|
+
template('devise_security.rb', 'config/initializers/devise_security.rb')
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def copy_locales
|
|
19
17
|
LOCALES.each do |locale|
|
|
20
18
|
copy_file(
|
|
21
19
|
"../../../config/locales/#{locale}.yml",
|
|
22
|
-
"config/locales/devise.security_extension.#{locale}.yml"
|
|
20
|
+
"config/locales/devise.security_extension.#{locale}.yml"
|
|
23
21
|
)
|
|
24
22
|
end
|
|
25
23
|
end
|
|
@@ -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
|
|
@@ -6,14 +6,17 @@ class Devise::ParanoidVerificationCodeControllerTest < ActionController::TestCas
|
|
|
6
6
|
include Devise::Test::ControllerHelpers
|
|
7
7
|
|
|
8
8
|
setup do
|
|
9
|
+
@controller.class.respond_to :json, :xml
|
|
9
10
|
@request.env['devise.mapping'] = Devise.mappings[:user]
|
|
10
|
-
|
|
11
11
|
@user = User.create!(
|
|
12
12
|
username: 'hello',
|
|
13
13
|
email: 'hello@path.travel',
|
|
14
14
|
password: 'Password4',
|
|
15
15
|
confirmed_at: 5.months.ago,
|
|
16
|
+
paranoid_verification_code: 'cookies'
|
|
16
17
|
)
|
|
18
|
+
assert @user.valid?
|
|
19
|
+
assert @user.need_paranoid_verification?
|
|
17
20
|
|
|
18
21
|
sign_in(@user)
|
|
19
22
|
end
|
|
@@ -25,6 +28,7 @@ class Devise::ParanoidVerificationCodeControllerTest < ActionController::TestCas
|
|
|
25
28
|
end
|
|
26
29
|
|
|
27
30
|
test "redirects to root on show if user doesn't need paranoid verification" do
|
|
31
|
+
@user.update(paranoid_verification_code: nil)
|
|
28
32
|
get :show
|
|
29
33
|
assert_redirected_to :root
|
|
30
34
|
end
|
|
@@ -35,10 +39,61 @@ class Devise::ParanoidVerificationCodeControllerTest < ActionController::TestCas
|
|
|
35
39
|
assert_template :show
|
|
36
40
|
end
|
|
37
41
|
|
|
38
|
-
test
|
|
39
|
-
|
|
42
|
+
test 'redirects on update if user not logged in' do
|
|
43
|
+
sign_out(@user)
|
|
44
|
+
patch :update
|
|
45
|
+
assert_redirected_to :root
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
test 'redirects on update if user does not need paranoid verification' do
|
|
49
|
+
@user.update(paranoid_verification_code: nil)
|
|
50
|
+
patch :update
|
|
40
51
|
assert_redirected_to :root
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
test 'update paranoid_verification_code with default format' do
|
|
55
|
+
patch(
|
|
56
|
+
:update,
|
|
57
|
+
params: {
|
|
58
|
+
user: {
|
|
59
|
+
paranoid_verification_code: 'cookies'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
assert_redirected_to root_path
|
|
41
64
|
assert_equal 'Verification code accepted', flash[:notice]
|
|
65
|
+
assert_equal('text/html', response.media_type)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
test 'update paranoid_verification_code using JSON format' do
|
|
69
|
+
patch(
|
|
70
|
+
:update,
|
|
71
|
+
format: :json,
|
|
72
|
+
params: {
|
|
73
|
+
user: {
|
|
74
|
+
paranoid_verification_code: 'cookies'
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
assert_response 204
|
|
80
|
+
assert_equal root_url, response.location
|
|
81
|
+
assert_nil response.media_type, 'No Content-Type header should be set for No Content response'
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
test 'update paranoid_verification_code using XML format' do
|
|
85
|
+
patch(
|
|
86
|
+
:update,
|
|
87
|
+
format: :xml,
|
|
88
|
+
params: {
|
|
89
|
+
user: {
|
|
90
|
+
paranoid_verification_code: 'cookies'
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
assert_response 204
|
|
95
|
+
assert_equal root_url, response.location
|
|
96
|
+
assert_nil response.media_type, 'No Content-Type header should be set for No Content response'
|
|
42
97
|
end
|
|
43
98
|
end
|
|
44
99
|
|
|
@@ -47,20 +102,30 @@ class ParanoidVerificationCodeCustomRedirectTest < ActionController::TestCase
|
|
|
47
102
|
tests Overrides::ParanoidVerificationCodeController
|
|
48
103
|
|
|
49
104
|
setup do
|
|
105
|
+
@controller.class.respond_to :json, :xml
|
|
50
106
|
@request.env['devise.mapping'] = Devise.mappings[:paranoid_verification_user]
|
|
51
|
-
|
|
52
107
|
@user = ParanoidVerificationUser.create!(
|
|
53
108
|
username: 'hello',
|
|
54
109
|
email: 'hello@path.travel',
|
|
55
110
|
password: 'Password4',
|
|
56
111
|
confirmed_at: 5.months.ago,
|
|
112
|
+
paranoid_verification_code: 'cookies'
|
|
57
113
|
)
|
|
114
|
+
assert @user.valid?
|
|
115
|
+
assert @user.need_paranoid_verification?
|
|
58
116
|
|
|
59
117
|
sign_in(@user)
|
|
60
118
|
end
|
|
61
119
|
|
|
62
120
|
test 'redirects to custom redirect route on update' do
|
|
63
|
-
patch
|
|
121
|
+
patch(
|
|
122
|
+
:update,
|
|
123
|
+
params: {
|
|
124
|
+
paranoid_verification_user: {
|
|
125
|
+
paranoid_verification_code: 'cookies'
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
)
|
|
64
129
|
|
|
65
130
|
assert_redirected_to '/cats'
|
|
66
131
|
assert_equal 'Verification code accepted', flash[:notice]
|
|
@@ -13,7 +13,7 @@ class Devise::PasswordExpiredControllerTest < ActionController::TestCase
|
|
|
13
13
|
email: 'hello@path.travel',
|
|
14
14
|
password: 'Password4',
|
|
15
15
|
password_changed_at: 4.months.ago,
|
|
16
|
-
confirmed_at: 5.months.ago
|
|
16
|
+
confirmed_at: 5.months.ago
|
|
17
17
|
)
|
|
18
18
|
assert @user.valid?
|
|
19
19
|
assert @user.need_change_password?
|
|
@@ -51,31 +51,35 @@ class Devise::PasswordExpiredControllerTest < ActionController::TestCase
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
test 'update password with default format' do
|
|
54
|
-
put
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
put(
|
|
55
|
+
:update,
|
|
56
|
+
params: {
|
|
57
|
+
user: {
|
|
58
|
+
current_password: 'Password4',
|
|
59
|
+
password: 'Password5',
|
|
60
|
+
password_confirmation: 'Password5'
|
|
61
61
|
}
|
|
62
|
+
}
|
|
63
|
+
)
|
|
62
64
|
assert_redirected_to root_path
|
|
63
|
-
assert_equal
|
|
65
|
+
assert_equal('text/html', response.media_type)
|
|
64
66
|
end
|
|
65
67
|
|
|
66
68
|
test 'password confirmation does not match' do
|
|
67
|
-
put
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
put(
|
|
70
|
+
:update,
|
|
71
|
+
params: {
|
|
72
|
+
user: {
|
|
73
|
+
current_password: 'Password4',
|
|
74
|
+
password: 'Password5',
|
|
75
|
+
password_confirmation: 'Password6'
|
|
74
76
|
}
|
|
77
|
+
}
|
|
78
|
+
)
|
|
75
79
|
|
|
76
80
|
assert_response :success
|
|
77
81
|
assert_template :show
|
|
78
|
-
assert_equal
|
|
82
|
+
assert_equal('text/html', response.media_type)
|
|
79
83
|
assert_includes(
|
|
80
84
|
response.body,
|
|
81
85
|
'Password confirmation doesn't match Password'
|
|
@@ -83,30 +87,35 @@ class Devise::PasswordExpiredControllerTest < ActionController::TestCase
|
|
|
83
87
|
end
|
|
84
88
|
|
|
85
89
|
test 'update password using JSON format' do
|
|
86
|
-
put
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
put(
|
|
91
|
+
:update,
|
|
92
|
+
format: :json,
|
|
93
|
+
params: {
|
|
94
|
+
user: {
|
|
95
|
+
current_password: 'Password4',
|
|
96
|
+
password: 'Password5',
|
|
97
|
+
password_confirmation: 'Password5'
|
|
94
98
|
}
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
|
|
95
102
|
assert_response 204
|
|
96
103
|
assert_equal root_url, response.location
|
|
97
104
|
assert_nil response.media_type, 'No Content-Type header should be set for No Content response'
|
|
98
105
|
end
|
|
99
106
|
|
|
100
107
|
test 'update password using XML format' do
|
|
101
|
-
put
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
put(
|
|
109
|
+
:update,
|
|
110
|
+
format: :xml,
|
|
111
|
+
params: {
|
|
112
|
+
user: {
|
|
113
|
+
current_password: 'Password4',
|
|
114
|
+
password: 'Password5',
|
|
115
|
+
password_confirmation: 'Password5'
|
|
109
116
|
}
|
|
117
|
+
}
|
|
118
|
+
)
|
|
110
119
|
assert_response 204
|
|
111
120
|
assert_equal root_url, response.location
|
|
112
121
|
assert_nil response.media_type, 'No Content-Type header should be set for No Content response'
|
|
@@ -125,7 +134,7 @@ class PasswordExpiredCustomRedirectTest < ActionController::TestCase
|
|
|
125
134
|
email: 'hello@path.travel',
|
|
126
135
|
password: 'Password4',
|
|
127
136
|
password_changed_at: 4.months.ago,
|
|
128
|
-
confirmed_at: 5.months.ago
|
|
137
|
+
confirmed_at: 5.months.ago
|
|
129
138
|
)
|
|
130
139
|
assert @user.valid?
|
|
131
140
|
assert @user.need_change_password?
|
|
@@ -134,15 +143,22 @@ class PasswordExpiredCustomRedirectTest < ActionController::TestCase
|
|
|
134
143
|
end
|
|
135
144
|
|
|
136
145
|
test 'update password with custom redirect route' do
|
|
137
|
-
put
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
put(
|
|
147
|
+
:update,
|
|
148
|
+
params: {
|
|
149
|
+
password_expired_user: {
|
|
150
|
+
current_password: 'Password4',
|
|
151
|
+
password: 'Password5',
|
|
152
|
+
password_confirmation: 'Password5'
|
|
144
153
|
}
|
|
154
|
+
}
|
|
155
|
+
)
|
|
145
156
|
|
|
146
157
|
assert_redirected_to '/cookies'
|
|
147
158
|
end
|
|
159
|
+
|
|
160
|
+
test 'yield resource to block on update' do
|
|
161
|
+
put(:update, params: { password_expired_user: { current_password: '123' } })
|
|
162
|
+
assert @controller.update_block_called?, 'Update failed to yield resource to provided block'
|
|
163
|
+
end
|
|
148
164
|
end
|
|
@@ -7,29 +7,38 @@ class TestWithSecurityQuestion < ActionController::TestCase
|
|
|
7
7
|
tests SecurityQuestion::UnlocksController
|
|
8
8
|
|
|
9
9
|
setup do
|
|
10
|
-
@user = SecurityQuestionUser.create!(
|
|
11
|
-
|
|
10
|
+
@user = SecurityQuestionUser.create!(
|
|
11
|
+
username: 'hello', email: 'hello@microsoft.com', password: 'A1234567z!', security_question_answer: 'Right Answer'
|
|
12
|
+
)
|
|
12
13
|
@user.lock_access!
|
|
13
14
|
assert @user.locked_at.present?
|
|
14
15
|
@request.env['devise.mapping'] = Devise.mappings[:security_question_user]
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
test 'When security question is enabled, it is inserted correctly' do
|
|
18
|
-
post
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
post(
|
|
20
|
+
:create,
|
|
21
|
+
params: {
|
|
22
|
+
security_question_answer: 'wrong answer',
|
|
23
|
+
security_question_user: {
|
|
24
|
+
email: @user.email
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
)
|
|
23
28
|
assert_equal I18n.t('devise.invalid_security_question'), flash[:alert]
|
|
24
29
|
assert_redirected_to new_security_question_user_unlock_path
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
test 'When security_question is valid, it runs as normal' do
|
|
28
|
-
post
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
post(
|
|
34
|
+
:create,
|
|
35
|
+
params: {
|
|
36
|
+
security_question_answer: @user.security_question_answer,
|
|
37
|
+
security_question_user: {
|
|
38
|
+
email: @user.email
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
)
|
|
33
42
|
|
|
34
43
|
assert_equal I18n.t('devise.unlocks.send_instructions'), flash[:notice]
|
|
35
44
|
assert_redirected_to new_security_question_user_session_path
|
|
@@ -41,18 +50,15 @@ class TestWithoutSecurityQuestion < ActionController::TestCase
|
|
|
41
50
|
tests Devise::UnlocksController
|
|
42
51
|
|
|
43
52
|
setup do
|
|
44
|
-
@user = User.create(
|
|
45
|
-
|
|
53
|
+
@user = User.create(
|
|
54
|
+
username: 'hello', email: 'hello@path.travel', password: '1234', security_question_answer: 'Right Answer'
|
|
55
|
+
)
|
|
46
56
|
@user.lock_access!
|
|
47
57
|
@request.env['devise.mapping'] = Devise.mappings[:user]
|
|
48
58
|
end
|
|
49
59
|
|
|
50
60
|
test 'When security question is not enabled it is not inserted' do
|
|
51
|
-
post :create, params: {
|
|
52
|
-
user: {
|
|
53
|
-
email: @user.email,
|
|
54
|
-
},
|
|
55
|
-
}
|
|
61
|
+
post :create, params: { user: { email: @user.email } }
|
|
56
62
|
|
|
57
63
|
assert_equal I18n.t('devise.unlocks.send_instructions'), flash[:notice]
|
|
58
64
|
assert_redirected_to new_user_session_path
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Overrides::PasswordExpiredController < Devise::PasswordExpiredController
|
|
4
|
+
def update
|
|
5
|
+
super do |resource|
|
|
6
|
+
@update_block_called = true
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
4
10
|
def after_password_expired_update_path_for(_)
|
|
5
11
|
'/cookies'
|
|
6
12
|
end
|
|
13
|
+
|
|
14
|
+
def update_block_called?
|
|
15
|
+
@update_block_called == true
|
|
16
|
+
end
|
|
7
17
|
end
|
|
@@ -21,16 +21,17 @@ class User < ApplicationRecord
|
|
|
21
21
|
|
|
22
22
|
has_many :widgets
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
case DEVISE_ORM
|
|
25
|
+
when :mongoid
|
|
25
26
|
require './test/dummy/app/models/mongoid/mappings'
|
|
26
27
|
include ::Mongoid::Mappings
|
|
27
28
|
|
|
28
29
|
def some_method_calling_mongoid
|
|
29
30
|
Mongoid.logger
|
|
30
31
|
end
|
|
31
|
-
|
|
32
|
+
when :active_record
|
|
32
33
|
def some_method_calling_active_record
|
|
33
|
-
ActiveRecord::Base.transaction {}
|
|
34
|
+
ActiveRecord::Base.transaction { break; }
|
|
34
35
|
end
|
|
35
36
|
end
|
|
36
37
|
end
|
|
@@ -5,7 +5,10 @@ require 'shared_user_without_email'
|
|
|
5
5
|
class UserWithoutEmail
|
|
6
6
|
include Mongoid::Document
|
|
7
7
|
include Shim
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
devise :database_authenticatable, :lockable, :recoverable,
|
|
10
|
+
:registerable, :rememberable, :timeoutable,
|
|
11
|
+
:trackable
|
|
9
12
|
|
|
10
13
|
field :username, type: String
|
|
11
14
|
field :facebook_token, type: String
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require File.expand_path('boot', __dir__)
|
|
4
4
|
|
|
5
|
-
require 'action_mailer/railtie'
|
|
6
5
|
require 'action_mailer/railtie'
|
|
7
6
|
require 'rails/test_unit/railtie'
|
|
8
7
|
DEVISE_ORM = ENV.fetch('DEVISE_ORM', 'active_record').to_sym
|
|
@@ -22,9 +21,6 @@ module RailsApp
|
|
|
22
21
|
config.autoload_paths += ["#{config.root}/app/#{DEVISE_ORM}"]
|
|
23
22
|
config.autoload_paths += ["#{config.root}/lib"]
|
|
24
23
|
|
|
25
|
-
config.assets.enabled = true
|
|
26
|
-
|
|
27
|
-
config.assets.version = '1.0'
|
|
28
24
|
config.secret_key_base = 'foobar'
|
|
29
25
|
end
|
|
30
26
|
end
|
|
@@ -23,5 +23,6 @@ RailsApp::Application.configure do
|
|
|
23
23
|
config.active_support.test_order = :sorted
|
|
24
24
|
config.log_level = :debug
|
|
25
25
|
config.active_record.sqlite3.represent_boolean_as_integer = true if Rails.gem_version.release >= Gem::Version.new('5.2') && Rails.gem_version.release < Gem::Version.new('6.0')
|
|
26
|
+
config.active_record.legacy_connection_handling = false if Rails.gem_version.release >= Gem::Version.new('6.1')
|
|
26
27
|
end
|
|
27
28
|
ActiveSupport::Deprecation.debug = true
|
|
@@ -8,10 +8,6 @@ Devise.setup do |config|
|
|
|
8
8
|
config.secret_key = 'f08cf11a38906f531d2dfc9a2c2d671aa0021be806c21255d4'
|
|
9
9
|
config.case_insensitive_keys = [:email]
|
|
10
10
|
config.strip_whitespace_keys = [:email]
|
|
11
|
-
config.password_complexity = {
|
|
12
|
-
digit: 1,
|
|
13
|
-
lower: 1,
|
|
14
|
-
upper: 1,
|
|
15
|
-
}
|
|
11
|
+
config.password_complexity = { digit: 1, lower: 1, upper: 1 }
|
|
16
12
|
config.password_length = 7..128
|
|
17
13
|
end
|