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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +3 -1
  3. data/README.md +14 -5
  4. data/app/controllers/devise/paranoid_verification_code_controller.rb +14 -12
  5. data/app/controllers/devise/password_expired_controller.rb +8 -4
  6. data/config/locales/bg.yml +1 -0
  7. data/config/locales/by.yml +1 -0
  8. data/config/locales/cs.yml +5 -0
  9. data/config/locales/de.yml +1 -0
  10. data/config/locales/es.yml +12 -0
  11. data/config/locales/fa.yml +1 -0
  12. data/config/locales/fr.yml +14 -2
  13. data/config/locales/hi.yml +1 -0
  14. data/config/locales/it.yml +1 -0
  15. data/config/locales/ja.yml +12 -0
  16. data/config/locales/nl.yml +1 -0
  17. data/config/locales/pt.yml +1 -0
  18. data/config/locales/ru.yml +1 -0
  19. data/config/locales/tr.yml +25 -1
  20. data/config/locales/uk.yml +1 -0
  21. data/config/locales/zh_CN.yml +1 -0
  22. data/config/locales/zh_TW.yml +1 -0
  23. data/lib/devise-security/controllers/helpers.rb +23 -11
  24. data/lib/devise-security/hooks/expirable.rb +3 -3
  25. data/lib/devise-security/hooks/paranoid_verification.rb +1 -3
  26. data/lib/devise-security/hooks/password_expirable.rb +1 -3
  27. data/lib/devise-security/hooks/session_limitable.rb +4 -4
  28. data/lib/devise-security/models/compatibility/active_record_patch.rb +4 -3
  29. data/lib/devise-security/models/compatibility/mongoid_patch.rb +3 -2
  30. data/lib/devise-security/models/database_authenticatable_patch.rb +12 -14
  31. data/lib/devise-security/models/expirable.rb +6 -5
  32. data/lib/devise-security/models/paranoid_verification.rb +2 -2
  33. data/lib/devise-security/models/password_archivable.rb +1 -1
  34. data/lib/devise-security/models/secure_validatable.rb +6 -5
  35. data/lib/devise-security/orm/mongoid.rb +1 -1
  36. data/lib/devise-security/patches.rb +14 -8
  37. data/lib/devise-security/routes.rb +2 -3
  38. data/lib/devise-security/version.rb +1 -1
  39. data/lib/devise-security.rb +2 -1
  40. data/lib/generators/devise_security/install_generator.rb +3 -5
  41. data/lib/generators/templates/devise_security.rb +3 -0
  42. data/test/controllers/test_paranoid_verification_code_controller.rb +70 -5
  43. data/test/controllers/test_password_expired_controller.rb +57 -41
  44. data/test/controllers/test_security_question_controller.rb +25 -19
  45. data/test/dummy/app/controllers/overrides/password_expired_controller.rb +10 -0
  46. data/test/dummy/app/models/user.rb +4 -3
  47. data/test/dummy/app/mongoid/user_without_email.rb +4 -1
  48. data/test/dummy/config/application.rb +0 -4
  49. data/test/dummy/config/environments/test.rb +1 -0
  50. data/test/dummy/config/initializers/devise.rb +1 -5
  51. data/test/dummy/config/routes.rb +1 -1
  52. data/test/dummy/config.ru +1 -1
  53. data/test/dummy/db/migrate/20120508165529_create_tables.rb +2 -2
  54. data/test/dummy/log/test.log +34100 -90393
  55. data/test/i18n_test.rb +22 -0
  56. data/test/integration/test_paranoid_verification_code_workflow.rb +53 -0
  57. data/test/integration/test_password_expirable_workflow.rb +2 -2
  58. data/test/integration/test_session_limitable_workflow.rb +3 -3
  59. data/test/support/integration_helpers.rb +18 -12
  60. data/test/test_complexity_validator.rb +42 -41
  61. data/test/test_database_authenticatable_patch.rb +3 -3
  62. data/test/test_paranoid_verification.rb +8 -8
  63. data/test/test_password_expirable.rb +1 -1
  64. data/test/test_secure_validatable.rb +5 -13
  65. data/test/test_session_limitable.rb +7 -7
  66. data/test/tmp/config/initializers/devise_security.rb +3 -0
  67. data/test/tmp/config/locales/devise.security_extension.by.yml +1 -0
  68. data/test/tmp/config/locales/devise.security_extension.cs.yml +5 -0
  69. data/test/tmp/config/locales/devise.security_extension.de.yml +1 -0
  70. data/test/tmp/config/locales/devise.security_extension.es.yml +12 -0
  71. data/test/tmp/config/locales/devise.security_extension.fa.yml +1 -0
  72. data/test/tmp/config/locales/devise.security_extension.fr.yml +14 -2
  73. data/test/tmp/config/locales/devise.security_extension.hi.yml +1 -0
  74. data/test/tmp/config/locales/devise.security_extension.it.yml +1 -0
  75. data/test/tmp/config/locales/devise.security_extension.ja.yml +12 -0
  76. data/test/tmp/config/locales/devise.security_extension.nl.yml +1 -0
  77. data/test/tmp/config/locales/devise.security_extension.pt.yml +1 -0
  78. data/test/tmp/config/locales/devise.security_extension.ru.yml +1 -0
  79. data/test/tmp/config/locales/devise.security_extension.tr.yml +25 -1
  80. data/test/tmp/config/locales/devise.security_extension.uk.yml +1 -0
  81. data/test/tmp/config/locales/devise.security_extension.zh_CN.yml +1 -0
  82. data/test/tmp/config/locales/devise.security_extension.zh_TW.yml +1 -0
  83. metadata +45 -27
  84. data/lib/devise-security/patches/confirmations_controller_captcha.rb +0 -23
  85. data/lib/devise-security/patches/confirmations_controller_security_question.rb +0 -26
  86. data/lib/devise-security/patches/passwords_controller_captcha.rb +0 -22
  87. data/lib/devise-security/patches/passwords_controller_security_question.rb +0 -25
  88. data/lib/devise-security/patches/registrations_controller_captcha.rb +0 -35
  89. data/lib/devise-security/patches/sessions_controller_captcha.rb +0 -26
  90. data/lib/devise-security/patches/unlocks_controller_captcha.rb +0 -22
  91. data/lib/devise-security/patches/unlocks_controller_security_question.rb +0 -25
  92. data/test/dummy/app/controllers/foos_controller.rb +0 -0
  93. data/test/dummy/lib/shared_user_without_email.rb +0 -29
  94. 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 self.expired_at < Time.now.utc unless self.expired_at.nil?
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 self.last_activity_at < self.class.expire_after.ago unless self.last_activity_at.nil?
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 && !self.expired?
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
- !self.expired? ? super : :expired
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 is the password used in the past
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 has_uniqueness_validation_of_login?
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
- self.errors.add(:password, :equal_to_current_password) if dummy.valid_password?(password)
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 has_uniqueness_validation_of_login?
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.kind_of?(validator_orm_klass) && validator.attributes.include?(login_attribute)
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
- self.ancestors.map(&:to_s).include? 'Devise::Models::Validatable'
154
+ ancestors.map(&:to_s).include? 'Devise::Models::Validatable'
154
155
  end
155
156
  end
156
157
  end
@@ -3,5 +3,5 @@
3
3
  ActiveSupport.on_load(:mongoid) do
4
4
  require 'orm_adapter/adapters/mongoid'
5
5
 
6
- Mongoid::Document::ClassMethods.send :include, Devise::Models
6
+ Mongoid::Document::ClassMethods.include(Devise::Models)
7
7
  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.send(:include, Patches::ControllerCaptcha) if Devise.captcha_for_recover || Devise.security_question_for_recover
11
- Devise::UnlocksController.send(:include, Patches::ControllerCaptcha) if Devise.captcha_for_unlock || Devise.security_question_for_unlock
12
- Devise::ConfirmationsController.send(:include, Patches::ControllerCaptcha) if Devise.captcha_for_confirmation
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.send(:include, Patches::ControllerSecurityQuestion) if Devise.security_question_for_recover
15
- Devise::UnlocksController.send(:include, Patches::ControllerSecurityQuestion) if Devise.security_question_for_unlock
16
- Devise::ConfirmationsController.send(:include, Patches::ControllerSecurityQuestion) if Devise.security_question_for_confirmation
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.send(:include, Patches::ControllerCaptcha) if Devise.captcha_for_sign_up
19
- Devise::SessionsController.send(:include, Patches::ControllerCaptcha) if Devise.captcha_for_sign_in
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: [:show, :update], path: mapping.path_names[:password_expired], controller: controllers[:password_expired]
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: [:show, :update], path: mapping.path_names[:verification_code], controller: controllers[:paranoid_verification_code]
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeviseSecurity
4
- VERSION = '0.17.0'
4
+ VERSION = '0.18.0'
5
5
  end
@@ -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? [:active_record, :mongoid]
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('../../templates', __FILE__)
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 "redirects to root on update" do
39
- patch :update, params: { user: { paranoid_verification_code: 'cookies' } }
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 :update, params: { paranoid_verification_user: { paranoid_verification_code: 'cookies' } }
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 :update,
55
- params: {
56
- user: {
57
- current_password: 'Password4',
58
- password: 'Password5',
59
- password_confirmation: 'Password5',
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 response.media_type, 'text/html'
65
+ assert_equal('text/html', response.media_type)
64
66
  end
65
67
 
66
68
  test 'password confirmation does not match' do
67
- put :update,
68
- params: {
69
- user: {
70
- current_password: 'Password4',
71
- password: 'Password5',
72
- password_confirmation: 'Password6',
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 response.media_type, 'text/html'
82
+ assert_equal('text/html', response.media_type)
79
83
  assert_includes(
80
84
  response.body,
81
85
  'Password confirmation doesn&#39;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 :update,
87
- format: :json,
88
- params: {
89
- user: {
90
- current_password: 'Password4',
91
- password: 'Password5',
92
- password_confirmation: 'Password5',
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 :update,
102
- format: :xml,
103
- params: {
104
- user: {
105
- current_password: 'Password4',
106
- password: 'Password5',
107
- password_confirmation: 'Password5',
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 :update,
138
- params: {
139
- password_expired_user: {
140
- current_password: 'Password4',
141
- password: 'Password5',
142
- password_confirmation: 'Password5',
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!(username: 'hello', email: 'hello@microsoft.com',
11
- password: 'A1234567z!', security_question_answer: 'Right Answer')
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 :create, params: {
19
- security_question_user: {
20
- email: @user.email,
21
- }, security_question_answer: 'wrong answer'
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 :create, params: {
29
- security_question_user: {
30
- email: @user.email,
31
- }, security_question_answer: @user.security_question_answer
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(username: 'hello', email: 'hello@path.travel',
45
- password: '1234', security_question_answer: 'Right Answer')
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
- if DEVISE_ORM == :mongoid
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
- elsif DEVISE_ORM == :active_record
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
- include SharedUserWithoutEmail
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