devise-security 0.12.0 → 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.
- checksums.yaml +4 -4
- data/README.md +186 -63
- data/app/controllers/devise/paranoid_verification_code_controller.rb +2 -0
- data/app/controllers/devise/password_expired_controller.rb +13 -6
- data/app/views/devise/paranoid_verification_code/show.html.erb +4 -4
- data/app/views/devise/password_expired/show.html.erb +6 -6
- data/config/locales/by.yml +49 -0
- data/config/locales/cs.yml +41 -0
- data/config/locales/de.yml +30 -7
- data/config/locales/en.yml +25 -1
- data/config/locales/es.yml +19 -6
- data/config/locales/fa.yml +41 -0
- data/config/locales/fr.yml +30 -0
- data/config/locales/hi.yml +42 -0
- data/config/locales/it.yml +35 -4
- data/config/locales/ja.yml +30 -0
- data/config/locales/nl.yml +41 -0
- data/config/locales/pt.yml +41 -0
- data/config/locales/ru.yml +49 -0
- data/config/locales/tr.yml +18 -0
- data/config/locales/uk.yml +49 -0
- data/config/locales/zh_CN.yml +41 -0
- data/config/locales/zh_TW.yml +41 -0
- data/lib/devise-security/controllers/helpers.rb +61 -50
- data/lib/devise-security/hooks/expirable.rb +3 -1
- data/lib/devise-security/hooks/paranoid_verification.rb +2 -0
- data/lib/devise-security/hooks/password_expirable.rb +4 -0
- data/lib/devise-security/hooks/session_limitable.rb +31 -14
- data/lib/devise-security/models/active_record/old_password.rb +5 -0
- data/lib/devise-security/models/compatibility/active_record_patch.rb +40 -0
- data/lib/devise-security/models/compatibility/mongoid_patch.rb +31 -0
- data/lib/devise-security/models/compatibility.rb +8 -15
- data/lib/devise-security/models/database_authenticatable_patch.rb +3 -1
- data/lib/devise-security/models/expirable.rb +8 -2
- data/lib/devise-security/models/mongoid/old_password.rb +21 -0
- data/lib/devise-security/models/paranoid_verification.rb +2 -0
- data/lib/devise-security/models/password_archivable.rb +18 -7
- data/lib/devise-security/models/password_expirable.rb +103 -48
- data/lib/devise-security/models/secure_validatable.rb +26 -6
- data/lib/devise-security/models/security_questionable.rb +2 -0
- data/lib/devise-security/models/session_limitable.rb +19 -2
- data/lib/devise-security/orm/mongoid.rb +7 -0
- data/lib/devise-security/patches/confirmations_controller_captcha.rb +2 -0
- data/lib/devise-security/patches/confirmations_controller_security_question.rb +2 -0
- data/lib/devise-security/patches/controller_captcha.rb +2 -0
- data/lib/devise-security/patches/controller_security_question.rb +3 -1
- data/lib/devise-security/patches/passwords_controller_captcha.rb +2 -0
- data/lib/devise-security/patches/passwords_controller_security_question.rb +2 -0
- data/lib/devise-security/patches/registrations_controller_captcha.rb +2 -0
- data/lib/devise-security/patches/sessions_controller_captcha.rb +2 -0
- data/lib/devise-security/patches/unlocks_controller_captcha.rb +2 -0
- data/lib/devise-security/patches/unlocks_controller_security_question.rb +2 -0
- data/lib/devise-security/patches.rb +2 -0
- data/lib/devise-security/rails.rb +2 -0
- data/lib/devise-security/routes.rb +2 -0
- data/lib/devise-security/validators/password_complexity_validator.rb +35 -0
- data/lib/devise-security/version.rb +3 -1
- data/lib/devise-security.rb +16 -10
- data/lib/generators/devise_security/install_generator.rb +5 -3
- data/lib/generators/templates/devise_security.rb +47 -0
- data/test/{test_captcha_controller.rb → controllers/test_captcha_controller.rb} +2 -0
- data/test/controllers/test_password_expired_controller.rb +110 -0
- data/test/controllers/test_security_question_controller.rb +60 -0
- data/test/dummy/Rakefile +3 -1
- data/test/dummy/app/assets/config/manifest.js +3 -0
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/controllers/captcha/sessions_controller.rb +2 -0
- data/test/dummy/app/controllers/security_question/unlocks_controller.rb +2 -0
- data/test/dummy/app/controllers/widgets_controller.rb +6 -0
- data/test/dummy/app/models/application_record.rb +10 -2
- data/test/dummy/app/models/application_user_record.rb +11 -0
- data/test/dummy/app/models/captcha_user.rb +7 -2
- data/test/dummy/app/models/mongoid/confirmable_fields.rb +13 -0
- data/test/dummy/app/models/mongoid/database_authenticable_fields.rb +17 -0
- data/test/dummy/app/models/mongoid/expirable_fields.rb +11 -0
- data/test/dummy/app/models/mongoid/lockable_fields.rb +13 -0
- data/test/dummy/app/models/mongoid/mappings.rb +13 -0
- data/test/dummy/app/models/mongoid/omniauthable_fields.rb +11 -0
- data/test/dummy/app/models/mongoid/paranoid_verification_fields.rb +10 -0
- data/test/dummy/app/models/mongoid/password_archivable_fields.rb +9 -0
- data/test/dummy/app/models/mongoid/password_expirable_fields.rb +10 -0
- data/test/dummy/app/models/mongoid/recoverable_fields.rb +11 -0
- data/test/dummy/app/models/mongoid/registerable_fields.rb +19 -0
- data/test/dummy/app/models/mongoid/rememberable_fields.rb +10 -0
- data/test/dummy/app/models/mongoid/secure_validatable_fields.rb +11 -0
- data/test/dummy/app/models/mongoid/security_questionable_fields.rb +13 -0
- data/test/dummy/app/models/mongoid/session_limitable_fields.rb +10 -0
- data/test/dummy/app/models/mongoid/timeoutable_fields.rb +9 -0
- data/test/dummy/app/models/mongoid/trackable_fields.rb +14 -0
- data/test/dummy/app/models/mongoid/validatable_fields.rb +7 -0
- data/test/dummy/app/models/secure_user.rb +7 -1
- data/test/dummy/app/models/security_question_user.rb +9 -4
- data/test/dummy/app/models/user.rb +15 -0
- data/test/dummy/app/models/widget.rb +6 -0
- data/test/dummy/app/mongoid/admin.rb +31 -0
- data/test/dummy/app/mongoid/one_user.rb +58 -0
- data/test/dummy/app/mongoid/shim.rb +25 -0
- data/test/dummy/app/mongoid/user_on_engine.rb +41 -0
- data/test/dummy/app/mongoid/user_on_main_app.rb +41 -0
- data/test/dummy/app/mongoid/user_with_validations.rb +37 -0
- data/test/dummy/app/mongoid/user_without_email.rb +35 -0
- data/test/dummy/config/application.rb +13 -7
- data/test/dummy/config/boot.rb +2 -0
- data/test/dummy/config/environment.rb +2 -0
- data/test/dummy/config/environments/test.rb +5 -13
- data/test/dummy/config/initializers/devise.rb +10 -3
- data/test/dummy/config/initializers/migration_class.rb +3 -6
- data/test/dummy/config/mongoid.yml +6 -0
- data/test/dummy/config/routes.rb +6 -3
- data/test/dummy/config.ru +3 -1
- data/test/dummy/db/migrate/20120508165529_create_tables.rb +13 -2
- data/test/dummy/db/migrate/20150402165590_add_verification_columns.rb +2 -0
- data/test/dummy/db/migrate/20150407162345_add_verification_attempt_column.rb +2 -0
- data/test/dummy/db/migrate/20160320162345_add_security_questions_fields.rb +2 -0
- data/test/dummy/db/migrate/20180318103603_add_expireable_columns.rb +2 -0
- data/test/dummy/db/migrate/20180318105329_add_confirmable_columns.rb +2 -0
- data/test/dummy/db/migrate/20180318105732_add_rememberable_columns.rb +2 -0
- data/test/dummy/db/migrate/20180318111336_add_recoverable_columns.rb +2 -0
- data/test/dummy/db/migrate/20180319114023_add_widget.rb +2 -0
- data/test/dummy/lib/shared_expirable_columns.rb +14 -0
- data/test/dummy/lib/shared_security_questions_fields.rb +16 -0
- data/test/dummy/lib/shared_user.rb +32 -0
- data/test/dummy/lib/shared_user_with_password_verification.rb +13 -0
- data/test/dummy/lib/shared_user_without_email.rb +28 -0
- data/test/dummy/lib/shared_user_without_omniauth.rb +15 -0
- data/test/dummy/lib/shared_verification_fields.rb +15 -0
- data/test/dummy/log/development.log +883 -0
- data/test/dummy/log/test.log +21689 -0
- data/test/integration/test_password_expirable_workflow.rb +53 -0
- data/test/integration/test_session_limitable_workflow.rb +67 -0
- data/test/orm/active_record.rb +15 -0
- data/test/orm/mongoid.rb +13 -0
- data/test/support/integration_helpers.rb +29 -0
- data/test/support/mongoid.yml +6 -0
- data/test/test_compatibility.rb +13 -0
- data/test/test_complexity_validator.rb +72 -0
- data/test/test_helper.rb +42 -9
- data/test/test_install_generator.rb +19 -2
- data/test/test_paranoid_verification.rb +2 -0
- data/test/test_password_archivable.rb +8 -7
- data/test/test_password_expirable.rb +70 -7
- data/test/test_secure_validatable.rb +97 -21
- data/test/test_session_limitable.rb +57 -0
- data/{lib/generators/templates → test/tmp/config/initializers}/devise-security.rb +12 -3
- data/test/tmp/config/locales/devise.security_extension.by.yml +49 -0
- data/test/tmp/config/locales/devise.security_extension.cs.yml +41 -0
- data/test/tmp/config/locales/devise.security_extension.de.yml +39 -0
- data/test/tmp/config/locales/devise.security_extension.en.yml +41 -0
- data/test/tmp/config/locales/devise.security_extension.es.yml +30 -0
- data/test/tmp/config/locales/devise.security_extension.fa.yml +41 -0
- data/test/tmp/config/locales/devise.security_extension.fr.yml +30 -0
- data/test/tmp/config/locales/devise.security_extension.hi.yml +42 -0
- data/test/tmp/config/locales/devise.security_extension.it.yml +41 -0
- data/test/tmp/config/locales/devise.security_extension.ja.yml +30 -0
- data/test/tmp/config/locales/devise.security_extension.nl.yml +41 -0
- data/test/tmp/config/locales/devise.security_extension.pt.yml +41 -0
- data/test/tmp/config/locales/devise.security_extension.ru.yml +49 -0
- data/test/tmp/config/locales/devise.security_extension.tr.yml +18 -0
- data/test/tmp/config/locales/devise.security_extension.uk.yml +49 -0
- data/test/tmp/config/locales/devise.security_extension.zh_CN.yml +41 -0
- data/test/tmp/config/locales/devise.security_extension.zh_TW.yml +41 -0
- metadata +235 -110
- data/.circleci/config.yml +0 -41
- data/.document +0 -5
- data/.gitignore +0 -40
- data/.rubocop.yml +0 -63
- data/.ruby-version +0 -1
- data/.travis.yml +0 -25
- data/Appraisals +0 -19
- data/Gemfile +0 -3
- data/Rakefile +0 -28
- data/devise-security.gemspec +0 -44
- data/gemfiles/rails_4.1_stable.gemfile +0 -8
- data/gemfiles/rails_4.2_stable.gemfile +0 -8
- data/gemfiles/rails_5.0_stable.gemfile +0 -8
- data/gemfiles/rails_5.1_stable.gemfile +0 -8
- data/gemfiles/rails_5.2_rc1.gemfile +0 -8
- data/lib/devise-security/models/old_password.rb +0 -4
- data/lib/devise-security/orm/active_record.rb +0 -18
- data/lib/devise-security/schema.rb +0 -64
- data/test/dummy/app/models/.gitkeep +0 -0
- data/test/test_password_expired_controller.rb +0 -44
- data/test/test_security_question_controller.rb +0 -84
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'compatibility'
|
4
|
+
require_relative '../validators/password_complexity_validator'
|
2
5
|
|
3
6
|
module Devise
|
4
7
|
module Models
|
@@ -45,11 +48,16 @@ module Devise
|
|
45
48
|
end
|
46
49
|
|
47
50
|
# extra validations
|
48
|
-
validates :email, email: email_validation if email_validation #
|
49
|
-
validates :password,
|
51
|
+
validates :email, email: email_validation if email_validation # see https://github.com/devise-security/devise-security/blob/master/README.md#e-mail-validation
|
52
|
+
validates :password,
|
53
|
+
'devise_security/password_complexity': password_complexity,
|
54
|
+
if: :password_required?
|
50
55
|
|
51
56
|
# don't allow use same password
|
52
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
|
53
61
|
end
|
54
62
|
end
|
55
63
|
|
@@ -65,6 +73,17 @@ module Devise
|
|
65
73
|
self.errors.add(:password, :equal_to_current_password) if dummy.valid_password?(password)
|
66
74
|
end
|
67
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
|
+
|
68
87
|
protected
|
69
88
|
|
70
89
|
# Checks whether a password is needed or not. For validations only.
|
@@ -79,13 +98,14 @@ module Devise
|
|
79
98
|
end
|
80
99
|
|
81
100
|
module ClassMethods
|
82
|
-
Devise::Models.config(self, :
|
101
|
+
Devise::Models.config(self, :password_complexity, :password_length, :email_validation, :allow_passwords_equal_to_email)
|
102
|
+
|
103
|
+
private
|
83
104
|
|
84
|
-
private
|
85
105
|
def has_uniqueness_validation_of_login?
|
86
106
|
validators.any? do |validator|
|
87
|
-
|
88
|
-
|
107
|
+
validator_orm_klass = DEVISE_ORM == :active_record ? ActiveRecord::Validations::UniquenessValidator : ::Mongoid::Validatable::UniquenessValidator
|
108
|
+
validator.kind_of?(validator_orm_klass) && validator.attributes.include?(login_attribute)
|
89
109
|
end
|
90
110
|
end
|
91
111
|
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'compatibility'
|
1
4
|
require 'devise-security/hooks/session_limitable'
|
2
5
|
|
3
6
|
module Devise
|
@@ -9,13 +12,27 @@ module Devise
|
|
9
12
|
# someone used his credentials to sign in.
|
10
13
|
module SessionLimitable
|
11
14
|
extend ActiveSupport::Concern
|
15
|
+
include Devise::Models::Compatibility
|
12
16
|
|
17
|
+
# Update the unique_session_id on the model. This will be checked in
|
18
|
+
# the Warden after_set_user hook in {file:devise-security/hooks/session_limitable}
|
19
|
+
# @param unique_session_id [String]
|
20
|
+
# @return [void]
|
21
|
+
# @raise [Devise::Models::Compatibility::NotPersistedError] if record is unsaved
|
13
22
|
def update_unique_session_id!(unique_session_id)
|
14
|
-
|
23
|
+
raise Devise::Models::Compatibility::NotPersistedError, 'cannot update a new record' unless persisted?
|
15
24
|
|
16
|
-
|
25
|
+
update_attribute_without_validatons_or_callbacks(:unique_session_id, unique_session_id).tap do
|
26
|
+
Rails.logger.debug { "[devise-security][session_limitable] unique_session_id=#{unique_session_id}" }
|
27
|
+
end
|
17
28
|
end
|
18
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
|
19
36
|
end
|
20
37
|
end
|
21
38
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeviseSecurity::Patches
|
2
4
|
module ControllerSecurityQuestion
|
3
5
|
extend ActiveSupport::Concern
|
@@ -7,6 +9,7 @@ module DeviseSecurity::Patches
|
|
7
9
|
end
|
8
10
|
|
9
11
|
private
|
12
|
+
|
10
13
|
def check_security_question
|
11
14
|
# only find via email, not login
|
12
15
|
resource = resource_class.find_or_initialize_with_error_by(:email, params[resource_name][:email], :not_found)
|
@@ -17,4 +20,3 @@ module DeviseSecurity::Patches
|
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
20
|
-
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Password complexity validator
|
4
|
+
# Options:
|
5
|
+
# - digit: minimum number of digits in the validated string
|
6
|
+
# - digits: minimum number of digits in the validated string
|
7
|
+
# - lower: minimum number of lower-case letters in the validated string
|
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
|
10
|
+
# - upper: minimum number of upper-case letters in the validated string
|
11
|
+
class DeviseSecurity::PasswordComplexityValidator < ActiveModel::EachValidator
|
12
|
+
PATTERNS = {
|
13
|
+
digit: /\p{Digit}/,
|
14
|
+
digits: /\p{Digit}/,
|
15
|
+
lower: /\p{Lower}/,
|
16
|
+
symbol: /\p{Punct}|\p{S}/,
|
17
|
+
symbols: /\p{Punct}|\p{S}/,
|
18
|
+
upper: /\p{Upper}/
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
def validate_each(record, attribute, value)
|
22
|
+
active_pattern_keys.each do |key|
|
23
|
+
minimum = [0, options[key].to_i].max
|
24
|
+
pattern = Regexp.new PATTERNS[key]
|
25
|
+
|
26
|
+
unless (value || '').scan(pattern).size >= minimum
|
27
|
+
record.errors.add attribute, :"password_complexity.#{key}", count: minimum
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def active_pattern_keys
|
33
|
+
options.keys & PATTERNS.keys
|
34
|
+
end
|
35
|
+
end
|
data/lib/devise-security.rb
CHANGED
@@ -1,18 +1,22 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
DEVISE_ORM = ENV.fetch('DEVISE_ORM', 'active_record').to_sym unless defined?(DEVISE_ORM)
|
3
|
+
|
4
|
+
require DEVISE_ORM.to_s if DEVISE_ORM.in? [:active_record, :mongoid]
|
2
5
|
require 'active_support/core_ext/integer'
|
3
6
|
require 'active_support/ordered_hash'
|
4
7
|
require 'active_support/concern'
|
5
8
|
require 'devise'
|
6
9
|
|
7
10
|
module Devise
|
8
|
-
|
9
|
-
#
|
11
|
+
# Number of seconds that passwords are valid (e.g 3.months)
|
12
|
+
# Disable pasword expiration with +false+
|
13
|
+
# Expire only on demand with +true+
|
10
14
|
mattr_accessor :expire_password_after
|
11
15
|
@@expire_password_after = 3.months
|
12
16
|
|
13
17
|
# Validate password for strongness
|
14
|
-
mattr_accessor :
|
15
|
-
@@
|
18
|
+
mattr_accessor :password_complexity
|
19
|
+
@@password_complexity = { digit: 1, lower: 1, symbol: 1, upper: 1 }
|
16
20
|
|
17
21
|
# Number of old passwords in archive
|
18
22
|
mattr_accessor :password_archiving_count
|
@@ -23,7 +27,7 @@ module Devise
|
|
23
27
|
@@deny_old_passwords = true
|
24
28
|
|
25
29
|
# enable email validation for :secure_validatable. (true, false, validation_options)
|
26
|
-
# dependency: need an email validator
|
30
|
+
# dependency: need an email validator, see https://github.com/devise-security/devise-security/blob/master/README.md#e-mail-validation
|
27
31
|
mattr_accessor :email_validation
|
28
32
|
@@email_validation = true
|
29
33
|
|
@@ -75,11 +79,14 @@ module Devise
|
|
75
79
|
# paranoid_verification will regenerate verifacation code after faild attempt
|
76
80
|
mattr_accessor :paranoid_code_regenerate_after_attempt
|
77
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
|
78
86
|
end
|
79
87
|
|
80
|
-
#
|
88
|
+
# a security extension for devise
|
81
89
|
module DeviseSecurity
|
82
|
-
autoload :Schema, 'devise-security/schema'
|
83
90
|
autoload :Patches, 'devise-security/patches'
|
84
91
|
|
85
92
|
module Controllers
|
@@ -100,7 +107,6 @@ Devise.add_module :paranoid_verification, controller: :paranoid_verification_cod
|
|
100
107
|
# requires
|
101
108
|
require 'devise-security/routes'
|
102
109
|
require 'devise-security/rails'
|
103
|
-
require
|
104
|
-
require 'devise-security/models/old_password'
|
110
|
+
require "devise-security/orm/#{DEVISE_ORM}" if DEVISE_ORM == :mongoid
|
105
111
|
require 'devise-security/models/database_authenticatable_patch'
|
106
112
|
require 'devise-security/models/paranoid_verification'
|
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module DeviseSecurity
|
2
4
|
module Generators
|
3
5
|
# Generator for Rails to create or append to a Devise initializer.
|
4
6
|
class InstallGenerator < Rails::Generators::Base
|
5
|
-
LOCALES = %w[
|
7
|
+
LOCALES = %w[by cs de en es fa fr hi it ja nl pt ru tr uk zh_CN zh_TW].freeze
|
6
8
|
|
7
9
|
source_root File.expand_path('../../templates', __FILE__)
|
8
10
|
desc 'Install the devise security extension'
|
9
11
|
|
10
12
|
def copy_initializer
|
11
|
-
template('
|
12
|
-
'config/initializers/
|
13
|
+
template('devise_security.rb',
|
14
|
+
'config/initializers/devise_security.rb',
|
13
15
|
)
|
14
16
|
end
|
15
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
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class Devise::PasswordExpiredControllerTest < ActionController::TestCase
|
6
|
+
include Devise::Test::ControllerHelpers
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@controller.class.respond_to :json, :xml
|
10
|
+
@request.env['devise.mapping'] = Devise.mappings[:user]
|
11
|
+
@user = User.create!(
|
12
|
+
username: 'hello',
|
13
|
+
email: 'hello@path.travel',
|
14
|
+
password: 'Password4',
|
15
|
+
password_changed_at: 4.months.ago,
|
16
|
+
confirmed_at: 5.months.ago,
|
17
|
+
)
|
18
|
+
assert @user.valid?
|
19
|
+
assert @user.need_change_password?
|
20
|
+
|
21
|
+
sign_in(@user)
|
22
|
+
end
|
23
|
+
|
24
|
+
test 'redirects on show if user not logged in' do
|
25
|
+
sign_out(@user)
|
26
|
+
get :show
|
27
|
+
assert_redirected_to :root
|
28
|
+
end
|
29
|
+
|
30
|
+
test 'redirects on show if user does not need password change' do
|
31
|
+
@user.update(password_changed_at: Time.zone.now)
|
32
|
+
get :show
|
33
|
+
assert_redirected_to :root
|
34
|
+
end
|
35
|
+
|
36
|
+
test 'should render show' do
|
37
|
+
get :show
|
38
|
+
assert_includes @response.body, 'Renew your password'
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'redirects on update if user not logged in' do
|
42
|
+
sign_out(@user)
|
43
|
+
put :update
|
44
|
+
assert_redirected_to :root
|
45
|
+
end
|
46
|
+
|
47
|
+
test 'redirects on update if user does not need password change' do
|
48
|
+
@user.update(password_changed_at: Time.zone.now)
|
49
|
+
put :update
|
50
|
+
assert_redirected_to :root
|
51
|
+
end
|
52
|
+
|
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
|
+
},
|
61
|
+
}
|
62
|
+
assert_redirected_to root_path
|
63
|
+
assert_equal response.media_type, 'text/html'
|
64
|
+
end
|
65
|
+
|
66
|
+
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
|
+
},
|
74
|
+
}
|
75
|
+
|
76
|
+
assert_response :success
|
77
|
+
assert_template :show
|
78
|
+
assert_equal response.media_type, 'text/html'
|
79
|
+
end
|
80
|
+
|
81
|
+
test 'update password using JSON format' do
|
82
|
+
put :update,
|
83
|
+
format: :json,
|
84
|
+
params: {
|
85
|
+
user: {
|
86
|
+
current_password: 'Password4',
|
87
|
+
password: 'Password5',
|
88
|
+
password_confirmation: 'Password5',
|
89
|
+
},
|
90
|
+
}
|
91
|
+
assert_response 204
|
92
|
+
assert_equal root_url, response.location
|
93
|
+
assert_nil response.media_type, 'No Content-Type header should be set for No Content response'
|
94
|
+
end
|
95
|
+
|
96
|
+
test 'update password using XML format' do
|
97
|
+
put :update,
|
98
|
+
format: :xml,
|
99
|
+
params: {
|
100
|
+
user: {
|
101
|
+
current_password: 'Password4',
|
102
|
+
password: 'Password5',
|
103
|
+
password_confirmation: 'Password5',
|
104
|
+
},
|
105
|
+
}
|
106
|
+
assert_response 204
|
107
|
+
assert_equal root_url, response.location
|
108
|
+
assert_nil response.media_type, 'No Content-Type header should be set for No Content response'
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
class TestWithSecurityQuestion < ActionController::TestCase
|
6
|
+
include Devise::Test::ControllerHelpers
|
7
|
+
tests SecurityQuestion::UnlocksController
|
8
|
+
|
9
|
+
setup do
|
10
|
+
@user = SecurityQuestionUser.create!(username: 'hello', email: 'hello@microsoft.com',
|
11
|
+
password: 'A1234567z!', security_question_answer: 'Right Answer')
|
12
|
+
@user.lock_access!
|
13
|
+
assert @user.locked_at.present?
|
14
|
+
@request.env['devise.mapping'] = Devise.mappings[:security_question_user]
|
15
|
+
end
|
16
|
+
|
17
|
+
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
|
+
}
|
23
|
+
assert_equal I18n.t('devise.invalid_security_question'), flash[:alert]
|
24
|
+
assert_redirected_to new_security_question_user_unlock_path
|
25
|
+
end
|
26
|
+
|
27
|
+
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
|
+
|
34
|
+
assert_equal I18n.t('devise.unlocks.send_instructions'), flash[:notice]
|
35
|
+
assert_redirected_to new_security_question_user_session_path
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class TestWithoutSecurityQuestion < ActionController::TestCase
|
40
|
+
include Devise::Test::ControllerHelpers
|
41
|
+
tests Devise::UnlocksController
|
42
|
+
|
43
|
+
setup do
|
44
|
+
@user = User.create(username: 'hello', email: 'hello@path.travel',
|
45
|
+
password: '1234', security_question_answer: 'Right Answer')
|
46
|
+
@user.lock_access!
|
47
|
+
@request.env['devise.mapping'] = Devise.mappings[:user]
|
48
|
+
end
|
49
|
+
|
50
|
+
test 'When security question is not enabled it is not inserted' do
|
51
|
+
post :create, params: {
|
52
|
+
user: {
|
53
|
+
email: @user.email,
|
54
|
+
},
|
55
|
+
}
|
56
|
+
|
57
|
+
assert_equal I18n.t('devise.unlocks.send_instructions'), flash[:notice]
|
58
|
+
assert_redirected_to new_user_session_path
|
59
|
+
end
|
60
|
+
end
|
data/test/dummy/Rakefile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
2
4
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
3
5
|
|
4
|
-
require File.expand_path('
|
6
|
+
require File.expand_path('config/application', __dir__)
|
5
7
|
|
6
8
|
Rails.application.load_tasks
|
@@ -1,3 +1,11 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if DEVISE_ORM == :active_record
|
4
|
+
class ApplicationRecord < ActiveRecord::Base
|
5
|
+
self.abstract_class = true
|
6
|
+
end
|
7
|
+
else
|
8
|
+
class ApplicationRecord
|
9
|
+
include Mongoid::Document
|
10
|
+
end
|
3
11
|
end
|
@@ -1,5 +1,10 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CaptchaUser < ApplicationUserRecord
|
3
4
|
devise :database_authenticatable, :password_archivable,
|
4
5
|
:paranoid_verification, :password_expirable
|
6
|
+
if DEVISE_ORM == :mongoid
|
7
|
+
require './test/dummy/app/models/mongoid/mappings'
|
8
|
+
include ::Mongoid::Mappings
|
9
|
+
end
|
5
10
|
end
|