devise-security 0.12.0 → 0.13.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/.codeclimate.yml +63 -0
- data/.gitignore +2 -0
- data/.mdlrc +1 -0
- data/.rubocop.yml +2 -1
- data/.ruby-version +1 -1
- data/.travis.yml +9 -11
- data/Appraisals +2 -2
- data/README.md +72 -53
- data/app/controllers/devise/paranoid_verification_code_controller.rb +2 -0
- data/app/controllers/devise/password_expired_controller.rb +2 -0
- data/config/locales/de.yml +13 -1
- data/config/locales/en.yml +13 -1
- data/config/locales/es.yml +13 -1
- data/config/locales/fr.yml +29 -0
- data/config/locales/tr.yml +17 -0
- data/devise-security.gemspec +10 -10
- data/gemfiles/{rails_4.1_stable.gemfile → rails_5.2.0.gemfile} +1 -1
- data/lib/devise-security.rb +8 -4
- data/lib/devise-security/controllers/helpers.rb +2 -0
- 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 +2 -0
- data/lib/devise-security/hooks/session_limitable.rb +2 -0
- data/lib/devise-security/models/compatibility.rb +2 -0
- data/lib/devise-security/models/database_authenticatable_patch.rb +2 -0
- data/lib/devise-security/models/expirable.rb +2 -0
- data/lib/devise-security/models/old_password.rb +2 -0
- data/lib/devise-security/models/paranoid_verification.rb +2 -0
- data/lib/devise-security/models/password_archivable.rb +2 -0
- data/lib/devise-security/models/password_expirable.rb +96 -50
- data/lib/devise-security/models/secure_validatable.rb +10 -4
- data/lib/devise-security/models/security_questionable.rb +2 -0
- data/lib/devise-security/models/session_limitable.rb +2 -0
- data/lib/devise-security/orm/active_record.rb +2 -0
- data/lib/devise-security/patches.rb +2 -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 +2 -0
- 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/rails.rb +2 -0
- data/lib/devise-security/routes.rb +2 -0
- data/lib/devise-security/schema.rb +2 -0
- data/lib/devise-security/validators/password_complexity_validator.rb +33 -0
- data/lib/devise-security/version.rb +3 -1
- data/lib/generators/devise_security/install_generator.rb +3 -1
- data/lib/generators/templates/devise-security.rb +9 -3
- data/test/dummy/Rakefile +3 -1
- 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/models/application_record.rb +2 -0
- data/test/dummy/app/models/captcha_user.rb +3 -1
- data/test/dummy/app/models/secure_user.rb +3 -1
- data/test/dummy/app/models/security_question_user.rb +3 -1
- data/test/dummy/app/models/user.rb +2 -0
- data/test/dummy/app/models/widget.rb +2 -0
- data/test/dummy/config.ru +3 -1
- data/test/dummy/config/application.rb +2 -0
- data/test/dummy/config/boot.rb +2 -0
- data/test/dummy/config/environment.rb +2 -0
- data/test/dummy/config/environments/test.rb +2 -0
- data/test/dummy/config/initializers/devise.rb +8 -0
- data/test/dummy/config/initializers/migration_class.rb +2 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/dummy/db/migrate/20120508165529_create_tables.rb +2 -0
- 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/test_captcha_controller.rb +2 -0
- data/test/test_complexity_validator.rb +60 -0
- data/test/test_helper.rb +19 -8
- data/test/test_install_generator.rb +7 -1
- data/test/test_paranoid_verification.rb +2 -0
- data/test/test_password_archivable.rb +2 -0
- data/test/test_password_expirable.rb +68 -7
- data/test/test_password_expired_controller.rb +2 -0
- data/test/test_secure_validatable.rb +10 -11
- data/test/test_security_question_controller.rb +2 -0
- metadata +32 -39
- data/.circleci/config.yml +0 -41
- data/gemfiles/rails_5.2_rc1.gemfile +0 -8
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Password complexity validator
|
4
|
+
# Options:
|
5
|
+
# - digit: minimum number of digits in the validated string
|
6
|
+
# - lower: minimum number of lower-case letters in the validated string
|
7
|
+
# - symbol: minimum number of punctuation characters or symbols in the validated string
|
8
|
+
# - upper: minimum number of upper-case letters in the validated string
|
9
|
+
class DeviseSecurity::PasswordComplexityValidator < ActiveModel::EachValidator
|
10
|
+
PATTERNS = {
|
11
|
+
digit: /\p{Digit}/,
|
12
|
+
digits: /\p{Digit}/,
|
13
|
+
lower: /\p{Lower}/,
|
14
|
+
upper: /\p{Upper}/,
|
15
|
+
symbol: /\p{Punct}|\p{S}/,
|
16
|
+
symbols: /\p{Punct}|\p{S}/
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
def validate_each(record, attribute, value)
|
20
|
+
active_pattern_keys.each do |key|
|
21
|
+
minimum = [0, options[key].to_i].max
|
22
|
+
pattern = Regexp.new PATTERNS[key]
|
23
|
+
|
24
|
+
unless (value || '').scan(pattern).size >= minimum
|
25
|
+
record.errors.add attribute, :"password_complexity.#{key}", count: minimum
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def active_pattern_keys
|
31
|
+
options.keys & PATTERNS.keys
|
32
|
+
end
|
33
|
+
end
|
@@ -1,8 +1,10 @@
|
|
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[en es de fr it tr].freeze
|
6
8
|
|
7
9
|
source_root File.expand_path('../../templates', __FILE__)
|
8
10
|
desc 'Install the devise security extension'
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
Devise.setup do |config|
|
2
4
|
# ==> Security Extension
|
3
5
|
# Configure security extension for devise
|
@@ -6,16 +8,20 @@ Devise.setup do |config|
|
|
6
8
|
# config.expire_password_after = false
|
7
9
|
|
8
10
|
# Need 1 char of A-Z, a-z and 0-9
|
9
|
-
# config.
|
11
|
+
# config.password_complexity = { digit: 1, lower: 1, symbol: 1, upper: 1 }
|
10
12
|
|
11
13
|
# How many passwords to keep in archive
|
12
14
|
# config.password_archiving_count = 5
|
13
15
|
|
14
|
-
# Deny old
|
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
|
15
21
|
# config.deny_old_passwords = true
|
16
22
|
|
17
23
|
# enable email validation for :secure_validatable. (true, false, validation_options)
|
18
|
-
# dependency:
|
24
|
+
# dependency: see https://github.com/devise-security/devise-security/blob/master/README.md#e-mail-validation
|
19
25
|
# config.email_validation = true
|
20
26
|
|
21
27
|
# captcha integration for recover form
|
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,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SecurityQuestionUser < ApplicationRecord
|
2
4
|
self.table_name = 'users'
|
3
5
|
devise :database_authenticatable, :password_archivable, :lockable,
|
4
6
|
:paranoid_verification, :password_expirable, :security_questionable
|
data/test/dummy/config.ru
CHANGED
data/test/dummy/config/boot.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rails_email_validator'
|
2
4
|
Devise.setup do |config|
|
3
5
|
config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com'
|
@@ -7,4 +9,10 @@ Devise.setup do |config|
|
|
7
9
|
config.case_insensitive_keys = [:email]
|
8
10
|
|
9
11
|
config.strip_whitespace_keys = [:email]
|
12
|
+
|
13
|
+
config.password_complexity = {
|
14
|
+
digit: 1,
|
15
|
+
lower: 1,
|
16
|
+
upper: 1,
|
17
|
+
}
|
10
18
|
end
|
data/test/dummy/config/routes.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PasswordComplexityValidatorTest < Minitest::Test
|
4
|
+
class ModelWithPassword
|
5
|
+
include ActiveModel::Validations
|
6
|
+
|
7
|
+
attr_reader :password
|
8
|
+
|
9
|
+
def initialize(password)
|
10
|
+
@password = password
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup
|
15
|
+
ModelWithPassword.clear_validators!
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_with_no_rules_anything_goes
|
19
|
+
assert(ModelWithPassword.new('aaaa').valid?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_enforces_uppercase
|
23
|
+
ModelWithPassword.validates :password, 'devise_security/password_complexity': { upper: 1 }
|
24
|
+
refute(ModelWithPassword.new('aaaa').valid?)
|
25
|
+
assert(ModelWithPassword.new('Aaaa').valid?)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_enforces_count
|
29
|
+
ModelWithPassword.validates :password, 'devise_security/password_complexity': { upper: 2 }
|
30
|
+
refute(ModelWithPassword.new('Aaaa').valid?)
|
31
|
+
assert(ModelWithPassword.new('AAaa').valid?)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_enforces_digit
|
35
|
+
ModelWithPassword.validates :password, 'devise_security/password_complexity': { digit: 1 }
|
36
|
+
refute(ModelWithPassword.new('aaaa').valid?)
|
37
|
+
assert(ModelWithPassword.new('aaa1').valid?)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_enforces_lower
|
41
|
+
ModelWithPassword.validates :password, 'devise_security/password_complexity': { lower: 1 }
|
42
|
+
refute(ModelWithPassword.new('AAAA').valid?)
|
43
|
+
assert(ModelWithPassword.new('AAAa').valid?)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_enforces_symbol
|
47
|
+
ModelWithPassword.validates :password, 'devise_security/password_complexity': { symbol: 1 }
|
48
|
+
refute(ModelWithPassword.new('aaaa').valid?)
|
49
|
+
assert(ModelWithPassword.new('aaa!').valid?)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_enforces_combination
|
53
|
+
ModelWithPassword.validates :password, 'devise_security/password_complexity': { lower: 1, upper: 1, digit: 1, symbol: 1 }
|
54
|
+
refute(ModelWithPassword.new('abcd').valid?)
|
55
|
+
refute(ModelWithPassword.new('ABCD').valid?)
|
56
|
+
refute(ModelWithPassword.new('1234').valid?)
|
57
|
+
refute(ModelWithPassword.new('$!,*').valid?)
|
58
|
+
assert(ModelWithPassword.new('aB3*').valid?)
|
59
|
+
end
|
60
|
+
end
|