nobspw 0.1.0 → 0.2.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 +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
|