nobspw 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +66 -1
- data/lib/active_model/validations/password_validator.rb +82 -0
- data/lib/nobspw.rb +9 -1
- data/lib/nobspw/password_checker.rb +16 -5
- data/lib/nobspw/version.rb +1 -1
- data/nobspw.gemspec +2 -0
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 391e780d690175f0dd19aec4f70506b81c587632
|
4
|
+
data.tar.gz: 164a2ed02a9041eeb5f46c0db2e13f1f7965b832
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 138e85ae7d22ff15af74e4fcaa646b237d671a5746a9e3b957fc793e3ed99ed1e4e8f752856c34c75dc18b64b3e0f6023c72849c1a6382573f80aeea1f411646
|
7
|
+
data.tar.gz: '09ab32aa868f3afe7505fb378b67375cc0413a87f4600cf37a5f641e88d029911a4d29114245c429b4346a0442fe4e743e2d31837f55a8fc40809b55fa3f505f'
|
data/README.md
CHANGED
@@ -36,7 +36,72 @@ Or install it yourself as:
|
|
36
36
|
|
37
37
|
## Usage
|
38
38
|
|
39
|
-
|
39
|
+
### Vanilla Ruby
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
pwc = NOBSPW::PasswordChecker.new password: 'mystrongpassword',
|
43
|
+
name: 'John Smith', # optional but recommended
|
44
|
+
username: 'bigjohn43', # optional but recommended
|
45
|
+
email: 'john@example.org' # optional but recommended
|
46
|
+
pwc.strong?
|
47
|
+
pwc.weak?
|
48
|
+
pwd.weak_password_reasons # returns an array of Symbols with reasons why password is weak
|
49
|
+
pwd.reasons # short alias of weak_password_reasons
|
50
|
+
```
|
51
|
+
|
52
|
+
Optionally, you can configure some options:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
NOBSPW.configure do |config|
|
56
|
+
config.min_password_length = 10
|
57
|
+
config.max_password_length = 256
|
58
|
+
config.min_unique_characters = 5
|
59
|
+
config.dictionary_path = 'path/to/dictionary.txt'
|
60
|
+
config.grep_path = '/usr/bin/grep'
|
61
|
+
config.domain_name = 'mywebsitedomain.com' # it is recommended you configure this
|
62
|
+
config.blacklist = ['this_password_is_not_allowed']
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
### Ruby on Rails
|
67
|
+
|
68
|
+
I included `PasswordValidator` for Rails. Validating passwords in your model couldn't be easier:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
validates :password, presence: true, password: true, if: -> { new_record? || changes[:password] }
|
72
|
+
```
|
73
|
+
|
74
|
+
PasswordValidator will try to guess the correct field name for each `PasswordChecker` argument as follow:
|
75
|
+
|
76
|
+
- `username`: `username user_name user screenname screen_name`
|
77
|
+
- `name`: `name full_name first_name+last_name`
|
78
|
+
- `email`: `email email_address`
|
79
|
+
|
80
|
+
If you have field names different than above, you can tell `PasswordValidator` which fields to use for specific attributes:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
validates :password, presence: true,
|
84
|
+
password: { :name => :customer_name,
|
85
|
+
:email => :electronic_address },
|
86
|
+
if: -> { new_record? || changes[:password] }
|
87
|
+
```
|
88
|
+
|
89
|
+
# Checks
|
90
|
+
|
91
|
+
NOBSPW currently checks for the following, in this order:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
:name_included_in_password
|
95
|
+
:email_included_in_password
|
96
|
+
:domain_included_in_password
|
97
|
+
:password_too_short
|
98
|
+
:password_too_long
|
99
|
+
:not_enough_unique_characters
|
100
|
+
:password_blacklisted
|
101
|
+
:password_too_common
|
102
|
+
```
|
103
|
+
|
104
|
+
If any of these tests fail, they'll be returned by `#reasons`, or with Rails, they'll be added to `errors[:password]`.
|
40
105
|
|
41
106
|
## License
|
42
107
|
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class ActiveModel::Validations::PasswordValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(record, attribute, value)
|
3
|
+
pc = NOBSPW::PasswordChecker.new password: record.send(attribute),
|
4
|
+
email: email_value(record),
|
5
|
+
name: name_value(record),
|
6
|
+
username: username_value(record)
|
7
|
+
|
8
|
+
pc.weak_password_reasons.each do |reason|
|
9
|
+
record.errors[attribute] << get_message(reason)
|
10
|
+
end
|
11
|
+
pc.strong?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def email_value(record)
|
17
|
+
if options.keys.include?(:email)
|
18
|
+
return nil if options[:email].nil?
|
19
|
+
return record.send(options[:email])
|
20
|
+
end
|
21
|
+
|
22
|
+
%i(email email_address).each do |f|
|
23
|
+
return record.send(f) if record.respond_to?(f)
|
24
|
+
end
|
25
|
+
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def username_value(record)
|
30
|
+
if options.keys.include?(:username)
|
31
|
+
return nil if options[:username].nil?
|
32
|
+
return record.send(options[:username])
|
33
|
+
end
|
34
|
+
|
35
|
+
%i(username user_name user screenname screen_name).each do |f|
|
36
|
+
return record.send(f) if record.respond_to?(f)
|
37
|
+
end
|
38
|
+
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def name_value(record)
|
43
|
+
if options.keys.include?(:name)
|
44
|
+
return nil if options[:name].nil?
|
45
|
+
return record.send(options[:name])
|
46
|
+
end
|
47
|
+
|
48
|
+
%i(name full_name).each do |f|
|
49
|
+
return record.send(f) if record.respond_to?(f)
|
50
|
+
end
|
51
|
+
|
52
|
+
if record.respond_to?(:first_name) && record.respond_to?(:last_name)
|
53
|
+
return "#{record.send(:first_name)} #{record.send(:last_name)}"
|
54
|
+
end
|
55
|
+
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_message(reason)
|
60
|
+
case reason
|
61
|
+
when :name_included_in_password
|
62
|
+
I18n.t 'password_validator.is_too_similar_to_your_name', default: 'is too similar to your name'
|
63
|
+
when :email_included_in_password
|
64
|
+
I18n.t 'password_validator.is_too_similar_to_your_email', default: 'is too similar to your email'
|
65
|
+
when :domain_included_in_password
|
66
|
+
I18n.t 'password_validator.is_too_similar_to_this_domain_name', default: 'is too similar to this domain name'
|
67
|
+
when :password_too_short
|
68
|
+
I18n.t 'password_validator.is_too_short', default: 'is too short'
|
69
|
+
when :password_too_long
|
70
|
+
I18n.t 'password_validator.is_too_long', default: 'is too long'
|
71
|
+
when :password_blacklisted
|
72
|
+
I18n.t 'password_validator.is_not_allowed', default: 'is not allowed'
|
73
|
+
when :password_too_common
|
74
|
+
I18n.t 'password_validator.is_too_common', default: 'is too common'
|
75
|
+
when :not_enough_unique_characters
|
76
|
+
I18n.t 'password_validator.does_not_have_enough_unique_chars', default: 'does not have enough unique characters'
|
77
|
+
else
|
78
|
+
I18n.t 'password_validator.is_not_valid', default: 'is not valid'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
data/lib/nobspw.rb
CHANGED
@@ -4,12 +4,20 @@ module NOBSPW
|
|
4
4
|
autoload :PasswordChecker, 'nobspw/password_checker'
|
5
5
|
autoload :Configuration, 'nobspw/configuration'
|
6
6
|
|
7
|
+
if (defined?(::ActiveModel) && ActiveModel::VERSION::MAJOR >= 4) && defined?(I18n)
|
8
|
+
require_relative 'active_model/validations/password_validator'
|
9
|
+
end
|
10
|
+
|
7
11
|
class << self
|
8
|
-
|
12
|
+
attr_writer :configuration
|
9
13
|
end
|
10
14
|
|
11
15
|
def self.configure
|
12
16
|
self.configuration ||= Configuration.new
|
13
17
|
yield(configuration)
|
14
18
|
end
|
19
|
+
|
20
|
+
def self.configuration
|
21
|
+
@configuration ||= Configuration.new
|
22
|
+
end
|
15
23
|
end
|
@@ -11,7 +11,9 @@ module NOBSPW
|
|
11
11
|
|
12
12
|
def initialize(name: nil, username: nil, email: nil, password:)
|
13
13
|
@name, @username, @email, @password = \
|
14
|
-
name
|
14
|
+
name&.strip, username&.strip, email&.strip, password&.strip
|
15
|
+
|
16
|
+
raise ArgumentError.new("Password was not specified.") if password.nil? || password.strip.length == 0
|
15
17
|
end
|
16
18
|
|
17
19
|
def strong?
|
@@ -19,10 +21,15 @@ module NOBSPW
|
|
19
21
|
@strong
|
20
22
|
end
|
21
23
|
|
24
|
+
def weak?
|
25
|
+
!strong?
|
26
|
+
end
|
27
|
+
|
22
28
|
def weak_password_reasons
|
23
29
|
check_password if @weak_password_reasons.nil?
|
24
30
|
@weak_password_reasons
|
25
31
|
end
|
32
|
+
alias_method :reasons, :weak_password_reasons
|
26
33
|
|
27
34
|
private
|
28
35
|
|
@@ -51,9 +58,9 @@ module NOBSPW
|
|
51
58
|
def domain_included_in_password?
|
52
59
|
domain = NOBSPW.configuration.domain_name
|
53
60
|
return nil unless domain
|
54
|
-
|
55
|
-
|
56
|
-
|
61
|
+
domain = strip_extension_from_domain(domain)
|
62
|
+
domain_without_separator = remove_word_separators(domain).gsub(' ', '')
|
63
|
+
words_included_in_password?([domain, domain_without_separator])
|
57
64
|
end
|
58
65
|
|
59
66
|
def password_blacklisted?
|
@@ -103,7 +110,11 @@ module NOBSPW
|
|
103
110
|
|
104
111
|
def email_without_extension(email)
|
105
112
|
name, domain, whatev = email.split("@", 3)
|
106
|
-
"#{name}@#{domain}"
|
113
|
+
"#{name}@#{strip_extension_from_domain(domain)}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def strip_extension_from_domain(domain)
|
117
|
+
domain.split(".").first
|
107
118
|
end
|
108
119
|
|
109
120
|
def remove_word_separators(str)
|
data/lib/nobspw/version.rb
CHANGED
data/nobspw.gemspec
CHANGED
@@ -27,6 +27,8 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_development_dependency "simplecov", "~> 0.13"
|
28
28
|
spec.add_development_dependency "guard", "~> 2.14"
|
29
29
|
spec.add_development_dependency "guard-rspec", "~> 4.7.3"
|
30
|
+
spec.add_development_dependency "activemodel", "~> 5.0"
|
31
|
+
spec.add_development_dependency "i18n", "~> 0.8.1"
|
30
32
|
|
31
33
|
if RUBY_PLATFORM =~ /darwin/
|
32
34
|
spec.add_development_dependency 'ruby_gntp', "~> 0.3.4"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nobspw
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carl Mercier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,34 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 4.7.3
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activemodel
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '5.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '5.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: i18n
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.8.1
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.8.1
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
126
|
name: ruby_gntp
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -140,6 +168,7 @@ files:
|
|
140
168
|
- Rakefile
|
141
169
|
- bin/console
|
142
170
|
- bin/setup
|
171
|
+
- lib/active_model/validations/password_validator.rb
|
143
172
|
- lib/db/dictionary.txt
|
144
173
|
- lib/nobspw.rb
|
145
174
|
- lib/nobspw/configuration.rb
|