devise-security 0.14.3 → 0.16.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +123 -59
  3. data/app/controllers/devise/password_expired_controller.rb +11 -6
  4. data/app/views/devise/paranoid_verification_code/show.html.erb +3 -3
  5. data/app/views/devise/password_expired/show.html.erb +5 -5
  6. data/config/locales/by.yml +49 -0
  7. data/config/locales/cs.yml +41 -0
  8. data/config/locales/de.yml +13 -2
  9. data/config/locales/en.yml +13 -1
  10. data/config/locales/es.yml +10 -9
  11. data/config/locales/fa.yml +41 -0
  12. data/config/locales/fr.yml +1 -0
  13. data/config/locales/hi.yml +42 -0
  14. data/config/locales/it.yml +35 -4
  15. data/config/locales/ja.yml +2 -1
  16. data/config/locales/nl.yml +41 -0
  17. data/config/locales/pt.yml +41 -0
  18. data/config/locales/ru.yml +49 -0
  19. data/config/locales/tr.yml +1 -0
  20. data/config/locales/uk.yml +49 -0
  21. data/config/locales/zh_CN.yml +41 -0
  22. data/config/locales/zh_TW.yml +41 -0
  23. data/lib/devise-security.rb +6 -3
  24. data/lib/devise-security/controllers/helpers.rb +59 -50
  25. data/lib/devise-security/hooks/password_expirable.rb +2 -0
  26. data/lib/devise-security/hooks/session_limitable.rb +21 -11
  27. data/lib/devise-security/models/password_expirable.rb +5 -1
  28. data/lib/devise-security/models/secure_validatable.rb +15 -1
  29. data/lib/devise-security/models/session_limitable.rb +8 -1
  30. data/lib/devise-security/validators/password_complexity_validator.rb +4 -2
  31. data/lib/devise-security/version.rb +1 -1
  32. data/lib/generators/devise_security/install_generator.rb +3 -3
  33. data/lib/generators/templates/devise_security.rb +47 -0
  34. data/test/controllers/test_password_expired_controller.rb +83 -19
  35. data/test/controllers/test_security_question_controller.rb +16 -40
  36. data/test/dummy/app/assets/config/manifest.js +3 -0
  37. data/test/dummy/config/environments/test.rb +3 -13
  38. data/test/dummy/config/initializers/migration_class.rb +1 -8
  39. data/test/dummy/config/mongoid.yml +1 -1
  40. data/test/dummy/config/routes.rb +3 -3
  41. data/test/dummy/log/development.log +883 -0
  42. data/test/dummy/log/test.log +21689 -0
  43. data/test/integration/test_password_expirable_workflow.rb +53 -0
  44. data/test/orm/active_record.rb +4 -1
  45. data/test/orm/mongoid.rb +2 -1
  46. data/test/support/integration_helpers.rb +15 -33
  47. data/test/support/mongoid.yml +1 -1
  48. data/test/test_complexity_validator.rb +12 -0
  49. data/test/test_helper.rb +10 -3
  50. data/test/test_install_generator.rb +11 -1
  51. data/test/test_secure_validatable.rb +76 -0
  52. data/test/test_session_limitable.rb +17 -0
  53. data/{lib/generators/templates → test/tmp/config/initializers}/devise-security.rb +3 -0
  54. data/test/tmp/config/locales/devise.security_extension.by.yml +49 -0
  55. data/test/tmp/config/locales/devise.security_extension.cs.yml +41 -0
  56. data/test/tmp/config/locales/devise.security_extension.de.yml +39 -0
  57. data/test/tmp/config/locales/devise.security_extension.en.yml +41 -0
  58. data/test/tmp/config/locales/devise.security_extension.es.yml +30 -0
  59. data/test/tmp/config/locales/devise.security_extension.fa.yml +41 -0
  60. data/test/tmp/config/locales/devise.security_extension.fr.yml +30 -0
  61. data/test/tmp/config/locales/devise.security_extension.hi.yml +42 -0
  62. data/test/tmp/config/locales/devise.security_extension.it.yml +41 -0
  63. data/test/tmp/config/locales/devise.security_extension.ja.yml +30 -0
  64. data/test/tmp/config/locales/devise.security_extension.nl.yml +41 -0
  65. data/test/tmp/config/locales/devise.security_extension.pt.yml +41 -0
  66. data/test/tmp/config/locales/devise.security_extension.ru.yml +49 -0
  67. data/test/tmp/config/locales/devise.security_extension.tr.yml +18 -0
  68. data/test/tmp/config/locales/devise.security_extension.uk.yml +49 -0
  69. data/test/tmp/config/locales/devise.security_extension.zh_CN.yml +41 -0
  70. data/test/tmp/config/locales/devise.security_extension.zh_TW.yml +41 -0
  71. metadata +145 -132
  72. data/.codeclimate.yml +0 -63
  73. data/.document +0 -5
  74. data/.gitignore +0 -43
  75. data/.mdlrc +0 -1
  76. data/.rubocop.yml +0 -64
  77. data/.ruby-version +0 -1
  78. data/.travis.yml +0 -39
  79. data/Appraisals +0 -35
  80. data/Gemfile +0 -10
  81. data/Rakefile +0 -27
  82. data/devise-security.gemspec +0 -50
  83. data/gemfiles/rails_4.2_stable.gemfile +0 -16
  84. data/gemfiles/rails_5.0_stable.gemfile +0 -15
  85. data/gemfiles/rails_5.1_stable.gemfile +0 -15
  86. data/gemfiles/rails_5.2_stable.gemfile +0 -15
  87. data/gemfiles/rails_6.0_beta.gemfile +0 -15
  88. data/lib/devise-security/orm/active_record.rb +0 -20
  89. data/lib/devise-security/schema.rb +0 -66
  90. data/test/dummy/app/models/.gitkeep +0 -0
@@ -0,0 +1,41 @@
1
+ zh_CN:
2
+ errors:
3
+ messages:
4
+ taken_in_past: '曾被使用过。'
5
+ equal_to_current_password: '必须与当前密码不同。'
6
+ equal_to_email: '必须与电子邮件地址不同。'
7
+ password_complexity:
8
+ digit:
9
+ one: 必须包含至少1个数字
10
+ other: 必须包含至少%{count}个数字
11
+ lower:
12
+ one: 必须包含至少1个小写字母
13
+ other: 必须包含至少%{count}个小写字母
14
+ symbol:
15
+ one: 必须包含至少1个特殊符号(!"#$%&'()*+,-./:;<=>?@[\]^_‘{|}~)
16
+ other: 必须包含至少%{count}个特殊符号(!"#$%&'()*+,-./:;<=>?@[\]^_‘{|}~)
17
+ upper:
18
+ one: 必须包含至少1个大写字母
19
+ other: 必须包含至少%{count}个大写字母
20
+ devise:
21
+ invalid_captcha: '验证码无效。'
22
+ invalid_security_question: '安全问题答案无效。'
23
+ paranoid_verify:
24
+ code_required: '请输入我们支持团队提供的代码'
25
+ paranoid_verification_code:
26
+ show:
27
+ submit_verification_code: 提交验证码
28
+ verification_code: 验证码
29
+ submit: 提交
30
+ password_expired:
31
+ updated: '你的新密码已保存。'
32
+ change_required: '你的密码已过期,请更新密码。'
33
+ show:
34
+ renew_your_password: 更新你的密码
35
+ current_password: 当前密码
36
+ new_password: 新密码
37
+ new_password_confirmation: 确认新密码
38
+ change_my_password: 更改密码
39
+ failure:
40
+ session_limited: '你的登录凭据已在另一个浏览器上被使用。请重新登录以在此浏览器继续。'
41
+ expired: '由于不活跃,你的账户已过期。请联系站点管理员。'
@@ -0,0 +1,41 @@
1
+ zh_TW:
2
+ errors:
3
+ messages:
4
+ taken_in_past: '曾被使用過。'
5
+ equal_to_current_password: '必須與目前密碼不同。'
6
+ equal_to_email: '必須與電子郵件地址不同。'
7
+ password_complexity:
8
+ digit:
9
+ one: 必須包含至少一個數字
10
+ other: 必須包含至少 %{count} 個數字
11
+ lower:
12
+ one: 必須包含至少一個小寫字母
13
+ other: 必須包含至少 %{count} 個小寫字母
14
+ symbol:
15
+ one: 必須包含至少一個特殊符號
16
+ other: 必須包含至少 %{count} 個特殊符號
17
+ upper:
18
+ one: 必須包含至少一個大寫字母
19
+ other: 必須包含至少 %{count} 個大寫字母
20
+ devise:
21
+ invalid_captcha: '輸入的驗證碼無效。'
22
+ invalid_security_question: '安全問題答案無效。'
23
+ paranoid_verify:
24
+ code_required: '請輸入由我們客服團隊提供的代碼'
25
+ paranoid_verification_code:
26
+ show:
27
+ submit_verification_code: 送出驗證碼
28
+ verification_code: 驗證碼
29
+ submit: 送出
30
+ password_expired:
31
+ updated: '你的新密碼已儲存'
32
+ change_required: '你的密碼已經過期,請更新密碼。'
33
+ show:
34
+ renew_your_password: 更新你的密碼
35
+ current_password: 目前密碼
36
+ new_password: 新密碼
37
+ new_password_confirmation: 確認新密碼
38
+ change_my_password: 更改我的密碼
39
+ failure:
40
+ session_limited: '你的登入憑證已在另一個瀏覽器上被使用,請重新登入以在此瀏覽器繼續使用。'
41
+ expired: '你的帳號因過久沒使用而已經過期,請洽網站管理員。'
@@ -79,11 +79,14 @@ module Devise
79
79
  # paranoid_verification will regenerate verifacation code after faild attempt
80
80
  mattr_accessor :paranoid_code_regenerate_after_attempt
81
81
  @@paranoid_code_regenerate_after_attempt = 10
82
+
83
+ # Whether to allow passwords that are equal (case insensitive) to the email
84
+ mattr_accessor :allow_passwords_equal_to_email
85
+ @@allow_passwords_equal_to_email = false
82
86
  end
83
87
 
84
- # an security extension for devise
88
+ # a security extension for devise
85
89
  module DeviseSecurity
86
- autoload :Schema, 'devise-security/schema'
87
90
  autoload :Patches, 'devise-security/patches'
88
91
 
89
92
  module Controllers
@@ -104,6 +107,6 @@ Devise.add_module :paranoid_verification, controller: :paranoid_verification_cod
104
107
  # requires
105
108
  require 'devise-security/routes'
106
109
  require 'devise-security/rails'
107
- require "devise-security/orm/#{DEVISE_ORM}"
110
+ require "devise-security/orm/#{DEVISE_ORM}" if DEVISE_ORM == :mongoid
108
111
  require 'devise-security/models/database_authenticatable_patch'
109
112
  require 'devise-security/models/paranoid_verification'
@@ -40,71 +40,80 @@ module DeviseSecurity
40
40
 
41
41
  # controller instance methods
42
42
 
43
- private
44
-
45
- # lookup if an password change needed
46
- def handle_password_change
47
- return if warden.nil?
48
-
49
- if !devise_controller? && !ignore_password_expire? && !request.format.nil? && request.format.html?
50
- Devise.mappings.keys.flatten.any? do |scope|
51
- if signed_in?(scope) && warden.session(scope)['password_expired']
52
- # re-check to avoid infinite loop if date changed after login attempt
53
- if send(:"current_#{scope}").try(:need_change_password?)
54
- store_location_for(scope, request.original_fullpath) if request.get?
55
- redirect_for_password_change scope
56
- return
57
- else
58
- warden.session(scope)[:password_expired] = false
59
- end
43
+ private
44
+
45
+ # Called as a `before_action` on all actions on any controller that uses
46
+ # this helper. If the user's session is marked as having an expired
47
+ # password we double check in case it has been changed by another process,
48
+ # then redirect to the password change url.
49
+ #
50
+ # @note `Warden::Manager.after_authentication` is run AFTER this method
51
+ #
52
+ # @note Once the warden session has `'password_expired'` set to `false`,
53
+ # it will **never** be checked again until the user re-logs in.
54
+ def handle_password_change
55
+ return if warden.nil?
56
+
57
+ if !devise_controller? &&
58
+ !ignore_password_expire? &&
59
+ !request.format.nil? &&
60
+ request.format.html?
61
+ Devise.mappings.keys.flatten.any? do |scope|
62
+ if signed_in?(scope) && warden.session(scope)['password_expired'] == true
63
+ if send(:"current_#{scope}").try(:need_change_password?)
64
+ store_location_for(scope, request.original_fullpath) if request.get?
65
+ redirect_for_password_change(scope)
66
+ else
67
+ warden.session(scope)['password_expired'] = false
60
68
  end
61
69
  end
62
70
  end
63
71
  end
72
+ end
64
73
 
65
- # lookup if extra (paranoid) code verification is needed
66
- def handle_paranoid_verification
67
- return if warden.nil?
74
+ # lookup if extra (paranoid) code verification is needed
75
+ def handle_paranoid_verification
76
+ return if warden.nil?
68
77
 
69
- if !devise_controller? && !request.format.nil? && request.format.html?
70
- Devise.mappings.keys.flatten.any? do |scope|
71
- if signed_in?(scope) && warden.session(scope)['paranoid_verify']
72
- store_location_for(scope, request.original_fullpath) if request.get?
73
- redirect_for_paranoid_verification scope
74
- return
75
- end
78
+ if !devise_controller? && !request.format.nil? && request.format.html?
79
+ Devise.mappings.keys.flatten.any? do |scope|
80
+ if signed_in?(scope) && warden.session(scope)['paranoid_verify']
81
+ store_location_for(scope, request.original_fullpath) if request.get?
82
+ redirect_for_paranoid_verification scope
83
+ return
76
84
  end
77
85
  end
78
86
  end
87
+ end
79
88
 
80
- # redirect for password update with alert message
81
- def redirect_for_password_change(scope)
82
- redirect_to change_password_required_path_for(scope), alert: I18n.t('change_required', {scope: 'devise.password_expired'})
83
- end
89
+ # redirect for password update with alert message
90
+ def redirect_for_password_change(scope)
91
+ redirect_to change_password_required_path_for(scope), alert: I18n.t('change_required', scope: 'devise.password_expired')
92
+ end
84
93
 
85
- def redirect_for_paranoid_verification(scope)
86
- redirect_to paranoid_verification_code_path_for(scope), alert: I18n.t('code_required', {scope: 'devise.paranoid_verify'})
87
- end
94
+ def redirect_for_paranoid_verification(scope)
95
+ redirect_to paranoid_verification_code_path_for(scope), alert: I18n.t('code_required', scope: 'devise.paranoid_verify')
96
+ end
88
97
 
89
- # path for change password
90
- def change_password_required_path_for(resource_or_scope = nil)
91
- scope = Devise::Mapping.find_scope!(resource_or_scope)
92
- change_path = "#{scope}_password_expired_path"
93
- send(change_path)
94
- end
98
+ # path for change password
99
+ def change_password_required_path_for(resource_or_scope = nil)
100
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
101
+ change_path = "#{scope}_password_expired_path"
102
+ send(change_path)
103
+ end
95
104
 
96
- def paranoid_verification_code_path_for(resource_or_scope = nil)
97
- scope = Devise::Mapping.find_scope!(resource_or_scope)
98
- change_path = "#{scope}_paranoid_verification_code_path"
99
- send(change_path)
100
- end
105
+ def paranoid_verification_code_path_for(resource_or_scope = nil)
106
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
107
+ change_path = "#{scope}_paranoid_verification_code_path"
108
+ send(change_path)
109
+ end
101
110
 
102
- protected
111
+ protected
103
112
 
104
- # allow to overwrite for some special handlings
105
- def ignore_password_expire?
106
- false
107
- end
113
+ # allow to overwrite for some special handlings
114
+ def ignore_password_expire?
115
+ false
116
+ end
108
117
  end
109
118
  end
110
119
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # @note This happens after
4
+ # {DeviseSecurity::Controller::Helpers#handle_password_change}
3
5
  Warden::Manager.after_authentication do |record, warden, options|
4
6
  if record.respond_to?(:need_change_password?)
5
7
  warden.session(options[:scope])['password_expired'] = record.need_change_password?
@@ -4,10 +4,17 @@
4
4
  # user is explicitly set (with set_user) and on authentication. Retrieving the
5
5
  # user from session (:fetch) does not trigger it.
6
6
  Warden::Manager.after_set_user except: :fetch do |record, warden, options|
7
- if record.respond_to?(:update_unique_session_id!) && warden.authenticated?(options[:scope])
8
- unique_session_id = Devise.friendly_token
9
- warden.session(options[:scope])['unique_session_id'] = unique_session_id
10
- record.update_unique_session_id!(unique_session_id)
7
+ if record.devise_modules.include?(:session_limitable) &&
8
+ warden.authenticated?(options[:scope]) &&
9
+ !record.skip_session_limitable?
10
+
11
+ if !options[:skip_session_limitable]
12
+ unique_session_id = Devise.friendly_token
13
+ warden.session(options[:scope])['unique_session_id'] = unique_session_id
14
+ record.update_unique_session_id!(unique_session_id)
15
+ else
16
+ warden.session(options[:scope])['devise.skip_session_limitable'] = true
17
+ end
11
18
  end
12
19
  end
13
20
 
@@ -17,15 +24,18 @@ end
17
24
  # page on the next request.
18
25
  Warden::Manager.after_set_user only: :fetch do |record, warden, options|
19
26
  scope = options[:scope]
20
- env = warden.request.env
21
27
 
22
- if record.respond_to?(:unique_session_id) && warden.authenticated?(scope) && options[:store] != false
23
- if record.unique_session_id != warden.session(scope)['unique_session_id'] && !env['devise.skip_session_limitable']
24
- Rails.logger.warn {
25
- "[devise-security][session_limitable] session id mismatch: "\
28
+ if record.devise_modules.include?(:session_limitable) &&
29
+ warden.authenticated?(scope) &&
30
+ options[:store] != false
31
+ if record.unique_session_id != warden.session(scope)['unique_session_id'] &&
32
+ !record.skip_session_limitable? &&
33
+ !warden.session(scope)['devise.skip_session_limitable']
34
+ Rails.logger.warn do
35
+ '[devise-security][session_limitable] session id mismatch: '\
26
36
  "expected=#{record.unique_session_id.inspect} "\
27
- "actual=#{warden.session(scope)['unique_session_id'].inspect}"
28
- }
37
+ "actual=#{warden.session(scope)['unique_session_id'].inspect}"
38
+ end
29
39
  warden.raw_session.clear
30
40
  warden.logout(scope)
31
41
  throw :warden, scope: scope, message: :session_limited
@@ -92,7 +92,11 @@ module Devise::Models
92
92
  # Update +password_changed_at+ for new records and changed passwords.
93
93
  # @note called as a +before_save+ hook
94
94
  def update_password_changed
95
- return unless (new_record? || encrypted_password_changed?) && !password_changed_at_changed?
95
+ if defined?(will_save_change_to_attribute?)
96
+ return unless (new_record? || will_save_change_to_encrypted_password?) && !will_save_change_to_password_changed_at?
97
+ else
98
+ return unless (new_record? || encrypted_password_changed?) && !password_changed_at_changed?
99
+ end
96
100
 
97
101
  self.password_changed_at = Time.zone.now
98
102
  end
@@ -55,6 +55,9 @@ module Devise
55
55
 
56
56
  # don't allow use same password
57
57
  validate :current_equal_password_validation
58
+
59
+ # don't allow email to equal password
60
+ validate :email_not_equal_password_validation unless allow_passwords_equal_to_email
58
61
  end
59
62
  end
60
63
 
@@ -70,6 +73,17 @@ module Devise
70
73
  self.errors.add(:password, :equal_to_current_password) if dummy.valid_password?(password)
71
74
  end
72
75
 
76
+ def email_not_equal_password_validation
77
+ return if password.blank? || (!new_record? && !will_save_change_to_encrypted_password?)
78
+ dummy = self.class.new.tap do |user|
79
+ user.password_salt = password_salt if respond_to?(:password_salt)
80
+ # whether case_insensitive_keys or strip_whitespace_keys include email or not, any
81
+ # variation of the email should not be a supported password
82
+ user.password = email.downcase.strip
83
+ end
84
+ self.errors.add(:password, :equal_to_email) if dummy.valid_password?(password.downcase.strip)
85
+ end
86
+
73
87
  protected
74
88
 
75
89
  # Checks whether a password is needed or not. For validations only.
@@ -84,7 +98,7 @@ module Devise
84
98
  end
85
99
 
86
100
  module ClassMethods
87
- Devise::Models.config(self, :password_complexity, :password_length, :email_validation)
101
+ Devise::Models.config(self, :password_complexity, :password_length, :email_validation, :allow_passwords_equal_to_email)
88
102
 
89
103
  private
90
104
 
@@ -21,11 +21,18 @@ module Devise
21
21
  # @raise [Devise::Models::Compatibility::NotPersistedError] if record is unsaved
22
22
  def update_unique_session_id!(unique_session_id)
23
23
  raise Devise::Models::Compatibility::NotPersistedError, 'cannot update a new record' unless persisted?
24
+
24
25
  update_attribute_without_validatons_or_callbacks(:unique_session_id, unique_session_id).tap do
25
- Rails.logger.debug { "[devise-security][session_limitable] unique_session_id=#{unique_session_id}"}
26
+ Rails.logger.debug { "[devise-security][session_limitable] unique_session_id=#{unique_session_id}" }
26
27
  end
27
28
  end
28
29
 
30
+ # Should session_limitable be skipped for this instance?
31
+ # @return [Boolean]
32
+ # @return [false] by default. This can be overridden by application logic as necessary.
33
+ def skip_session_limitable?
34
+ false
35
+ end
29
36
  end
30
37
  end
31
38
  end
@@ -3,17 +3,19 @@
3
3
  # Password complexity validator
4
4
  # Options:
5
5
  # - digit: minimum number of digits in the validated string
6
+ # - digits: minimum number of digits in the validated string
6
7
  # - lower: minimum number of lower-case letters in the validated string
7
8
  # - symbol: minimum number of punctuation characters or symbols in the validated string
9
+ # - symbols: minimum number of punctuation characters or symbols in the validated string
8
10
  # - upper: minimum number of upper-case letters in the validated string
9
11
  class DeviseSecurity::PasswordComplexityValidator < ActiveModel::EachValidator
10
12
  PATTERNS = {
11
13
  digit: /\p{Digit}/,
12
14
  digits: /\p{Digit}/,
13
15
  lower: /\p{Lower}/,
14
- upper: /\p{Upper}/,
15
16
  symbol: /\p{Punct}|\p{S}/,
16
- symbols: /\p{Punct}|\p{S}/
17
+ symbols: /\p{Punct}|\p{S}/,
18
+ upper: /\p{Upper}/
17
19
  }.freeze
18
20
 
19
21
  def validate_each(record, attribute, value)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeviseSecurity
4
- VERSION = '0.14.3'
4
+ VERSION = '0.16.0'
5
5
  end
@@ -4,14 +4,14 @@ module DeviseSecurity
4
4
  module Generators
5
5
  # Generator for Rails to create or append to a Devise initializer.
6
6
  class InstallGenerator < Rails::Generators::Base
7
- LOCALES = %w[en es de fr it ja tr].freeze
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
9
  source_root File.expand_path('../../templates', __FILE__)
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',
13
+ template('devise_security.rb',
14
+ 'config/initializers/devise_security.rb',
15
15
  )
16
16
  end
17
17
 
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ Devise.setup do |config|
4
+ # ==> Security Extension
5
+ # Configure security extension for devise
6
+
7
+ # Should the password expire (e.g 3.months)
8
+ # config.expire_password_after = false
9
+
10
+ # Need 1 char of A-Z, a-z and 0-9
11
+ # config.password_complexity = { digit: 1, lower: 1, symbol: 1, upper: 1 }
12
+
13
+ # How many passwords to keep in archive
14
+ # config.password_archiving_count = 5
15
+
16
+ # Deny old passwords (true, false, number_of_old_passwords_to_check)
17
+ # Examples:
18
+ # config.deny_old_passwords = false # allow old passwords
19
+ # config.deny_old_passwords = true # will deny all the old passwords
20
+ # config.deny_old_passwords = 3 # will deny new passwords that matches with the last 3 passwords
21
+ # config.deny_old_passwords = true
22
+
23
+ # enable email validation for :secure_validatable. (true, false, validation_options)
24
+ # dependency: see https://github.com/devise-security/devise-security/blob/master/README.md#e-mail-validation
25
+ # config.email_validation = true
26
+
27
+ # captcha integration for recover form
28
+ # config.captcha_for_recover = true
29
+
30
+ # captcha integration for sign up form
31
+ # config.captcha_for_sign_up = true
32
+
33
+ # captcha integration for sign in form
34
+ # config.captcha_for_sign_in = true
35
+
36
+ # captcha integration for unlock form
37
+ # config.captcha_for_unlock = true
38
+
39
+ # captcha integration for confirmation form
40
+ # config.captcha_for_confirmation = true
41
+
42
+ # Time period for account expiry from last_activity_at
43
+ # config.expire_after = 90.days
44
+
45
+ # Allow password to equal the email
46
+ # config.allow_passwords_equal_to_email = false
47
+ end