nobspw_rails7 0.6.5
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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Guardfile +26 -0
- data/LICENSE.txt +21 -0
- data/README.md +150 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/active_model/validations/password_validator.rb +84 -0
- data/lib/db/dictionary.txt +100000 -0
- data/lib/nobspw_rails7/configuration.rb +27 -0
- data/lib/nobspw_rails7/password_checker.rb +41 -0
- data/lib/nobspw_rails7/validation_methods.rb +127 -0
- data/lib/nobspw_rails7/version.rb +3 -0
- data/lib/nobspw_rails7.rb +24 -0
- data/misc/grep_benchmark.rb +73 -0
- data/nobspw_rails7.gemspec +39 -0
- metadata +206 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2ca5d47ab1262a8a04bece3bbcac0936b803458df38c09b94461f163815b206f
|
4
|
+
data.tar.gz: e33e55cdd6068f454c2ad46a1e8864809399f780f594726bb3623ed13314a5cb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 924d72215ed541b7f74f5fb7675d893df4dfa30b0e45b3e13b4d63e55f5b778987441f9b8f1d30d2797fe487889d5f11a4b64ed1cd6b754923fe8a51104c14fd
|
7
|
+
data.tar.gz: 8e6878728ca1b2592db6b5dd4e8d9d6299c0073c040dc7912e7f41bcbc861832a5e93e28a8f5a450c36ab8d09b2eb8b7181022230b1134a3565a070e013405f8
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
2
|
+
require "guard/rspec/dsl"
|
3
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
4
|
+
|
5
|
+
# Feel free to open issues for suggestions and improvements
|
6
|
+
|
7
|
+
# RSpec files
|
8
|
+
rspec = dsl.rspec
|
9
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
10
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
11
|
+
watch(rspec.spec_files)
|
12
|
+
|
13
|
+
# Ruby files
|
14
|
+
ruby = dsl.ruby
|
15
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
16
|
+
end
|
17
|
+
|
18
|
+
if `uname` =~ /Darwin/
|
19
|
+
if !!system("lsof -i:23053", out: '/dev/null')
|
20
|
+
puts "Growl notifications enabled"
|
21
|
+
notification :gntp, app_name: "", activate: 'com.googlecode.iTerm2'
|
22
|
+
else
|
23
|
+
puts "Native macOS notifications enabled"
|
24
|
+
notification :terminal_notifier, app_name: "", activate: 'com.googlecode.iTerm2' if `uname` =~ /Darwin/
|
25
|
+
end
|
26
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Carl Mercier
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
# NOBSPW_RAILS7 - No Bullshit Password strength checker
|
2
|
+
|
3
|
+
[](https://travis-ci.org/cmer/nobspw_rails7)
|
4
|
+
|
5
|
+
NOBSPW_RAILS7 is simple, no non-sense password strength checker written in Ruby. It does NOT validate against [bullshit password rules](https://twitter.com/codinghorror/status/631238409269309440?ref_src=twsrc%5Etfw) such as:
|
6
|
+
|
7
|
+
- must contain uppercase _(bullshit!)_
|
8
|
+
- must contain lowercase _(bullshit!)_
|
9
|
+
- must contain a number _(bullshit!)_
|
10
|
+
- must contain a special character _(bullshit!)_
|
11
|
+
|
12
|
+
Instead, it validates your user's password against a few important criteria. This ensures strong passwords without the hassle generally associated with complex (and useless) password rules.
|
13
|
+
|
14
|
+
The criteria currently are:
|
15
|
+
|
16
|
+
- enforce minimum (and maximum) length. 10 characters is the recommended minimum and is the default value
|
17
|
+
- reject common passwords from a dictionary of the 100,000 most common passwords (or your own dictionary)
|
18
|
+
- requires basic entropy (not too many of the same character)
|
19
|
+
- reject special case passwords such as the user's name, email, domain of the site/app
|
20
|
+
|
21
|
+
This software was inspired by [Password Rules are Bullshit](https://blog.codinghorror.com/password-rules-are-bullshit/) by Jeff Atwood.
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Add this line to your application's Gemfile:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
gem 'nobspw_rails7'
|
29
|
+
```
|
30
|
+
|
31
|
+
And then execute:
|
32
|
+
|
33
|
+
$ bundle
|
34
|
+
|
35
|
+
Or install it yourself as:
|
36
|
+
|
37
|
+
$ gem install nobspw_rails7
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
|
41
|
+
### Vanilla Ruby
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
pwc = NOBSPW_RAILS7::PasswordChecker.new password: 'mystrongpassword',
|
45
|
+
name: 'John Smith', # optional but recommended
|
46
|
+
username: 'bigjohn43', # optional but recommended
|
47
|
+
email: 'john@example.org' # optional but recommended
|
48
|
+
pwc.strong?
|
49
|
+
pwc.weak?
|
50
|
+
pwd.weak_password_reasons # returns an array of Symbols with reasons why password is weak
|
51
|
+
pwd.reasons # short alias of weak_password_reasons
|
52
|
+
```
|
53
|
+
|
54
|
+
Optionally, you can configure some options:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
NOBSPW_RAILS7.configure do |config|
|
58
|
+
config.min_password_length = 10
|
59
|
+
config.max_password_length = 256
|
60
|
+
config.min_unique_characters = 5
|
61
|
+
config.dictionary_path = 'path/to/dictionary.txt'
|
62
|
+
config.grep_path = '/usr/bin/grep'
|
63
|
+
config.use_ruby_grep = false # Defaults to false; slower when true. Uses Ruby's internal Grep method instead of shelling out.
|
64
|
+
config.domain_name = 'mywebsitedomain.com' # it is recommended you configure this
|
65
|
+
config.blacklist = ['this_password_is_not_allowed', /password/]
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
### Ruby on Rails
|
70
|
+
|
71
|
+
I included `PasswordValidator` for Rails. Validating passwords in your model couldn't be easier:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
validates :password, presence: true, password: true, if: -> { new_record? || changes[:password] || changes[:password_digest] }
|
75
|
+
```
|
76
|
+
|
77
|
+
PasswordValidator will try to guess the correct field name for each `PasswordChecker` argument as follow:
|
78
|
+
|
79
|
+
- `username`: `username` `user_name` `user` `screenname` `screen_name`
|
80
|
+
- `name`: `name` `full_name` `first_name+last_name`
|
81
|
+
- `email`: `email` `email_address`
|
82
|
+
|
83
|
+
If you have field names different than above, you can tell `PasswordValidator` which fields to use for specific attributes:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
validates :password, password: { :name => :customer_name,
|
87
|
+
:email => :electronic_address },
|
88
|
+
if: -> { new_record? || changes[:password] || changes[:password_digest] }
|
89
|
+
```
|
90
|
+
|
91
|
+
## Validations
|
92
|
+
|
93
|
+
NOBSPW_RAILS7 currently validates for the following, in this order:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
password_empty?
|
97
|
+
name_included_in_password?
|
98
|
+
email_included_in_password?
|
99
|
+
domain_included_in_password?
|
100
|
+
password_too_short?
|
101
|
+
password_too_long?
|
102
|
+
not_enough_unique_characters?
|
103
|
+
password_not_allowed?
|
104
|
+
password_too_common?
|
105
|
+
```
|
106
|
+
If any of these tests fail, they'll be returned by `#reasons`, or with Rails, they'll be added to `errors[:password]`.
|
107
|
+
|
108
|
+
|
109
|
+
## Custom Validations
|
110
|
+
|
111
|
+
It is possible and easy to add your own validations, or remove default ones.
|
112
|
+
|
113
|
+
### Adding a custom validation
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
module NOBSPW_RAILS7::ValidationMethods
|
117
|
+
def contains_letter_a?
|
118
|
+
# This is obviously a silly validation. Don't do this!
|
119
|
+
# If method returns true, it means that it FAILED validation.
|
120
|
+
# For example, it does contain the letter a and it's not considered acceptable.
|
121
|
+
!!@password.downcase.index('a')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
NOBSPW_RAILS7.configuration.validation_methods << :contains_letter_a?
|
126
|
+
|
127
|
+
# if using the Rails validator, you also need to define the error message:
|
128
|
+
ActiveModel::Validations::PasswordValidator.error_messages[:contains_letter_a] = \
|
129
|
+
'contains an unacceptable character'
|
130
|
+
|
131
|
+
```
|
132
|
+
|
133
|
+
### Removing a built-in validation
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
NOBSPW_RAILS7.configuration.validation_methods.delete(:domain_included_in_password?)
|
137
|
+
```
|
138
|
+
|
139
|
+
### Localization
|
140
|
+
|
141
|
+
You can localize all error messages, including custom ones, using I18n.
|
142
|
+
|
143
|
+
See `PasswordValidator#get_message` and `PasswordValidator#DEFAULT_ERROR_MESSAGES` for implementation details.
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
## License
|
148
|
+
|
149
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
150
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "nobspw_rails7"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
class ActiveModel::Validations::PasswordValidator < ActiveModel::EachValidator
|
2
|
+
DEFAULT_ERROR_MESSAGES = {
|
3
|
+
name_included_in_password: 'is too similar to your name',
|
4
|
+
username_included_in_password: 'is too similar to your username',
|
5
|
+
email_included_in_password: 'is too similar to your email',
|
6
|
+
domain_included_in_password: 'is too similar to this domain name',
|
7
|
+
password_too_short: 'is too short',
|
8
|
+
password_too_long: 'is too long',
|
9
|
+
not_enough_unique_characters: 'does not have enough unique characters',
|
10
|
+
password_not_allowed: 'is not allowed',
|
11
|
+
password_too_common: 'is too common',
|
12
|
+
fallback: 'is not valid'
|
13
|
+
}
|
14
|
+
|
15
|
+
def validate_each(record, attribute, value)
|
16
|
+
pc = NOBSPW_RAILS7::PasswordChecker.new password: record.send(attribute),
|
17
|
+
email: email_value(record),
|
18
|
+
name: name_value(record),
|
19
|
+
username: username_value(record)
|
20
|
+
|
21
|
+
pc.weak_password_reasons.each do |reason|
|
22
|
+
record.errors.add(attribute, get_message(reason))
|
23
|
+
end
|
24
|
+
pc.strong?
|
25
|
+
end
|
26
|
+
|
27
|
+
def error_messages
|
28
|
+
@error_messages ||= DEFAULT_ERROR_MESSAGES
|
29
|
+
end
|
30
|
+
|
31
|
+
def error_messages=(em)
|
32
|
+
@error_messages = em
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def email_value(record)
|
38
|
+
if options.keys.include?(:email)
|
39
|
+
return nil if options[:email].nil?
|
40
|
+
return record.send(options[:email])
|
41
|
+
end
|
42
|
+
|
43
|
+
%i(email email_address).each do |f|
|
44
|
+
return record.send(f) if record.respond_to?(f)
|
45
|
+
end
|
46
|
+
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def username_value(record)
|
51
|
+
if options.keys.include?(:username)
|
52
|
+
return nil if options[:username].nil?
|
53
|
+
return record.send(options[:username])
|
54
|
+
end
|
55
|
+
|
56
|
+
%i(username user_name user screenname screen_name).each do |f|
|
57
|
+
return record.send(f) if record.respond_to?(f)
|
58
|
+
end
|
59
|
+
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def name_value(record)
|
64
|
+
if options.keys.include?(:name)
|
65
|
+
return nil if options[:name].nil?
|
66
|
+
return record.send(options[:name])
|
67
|
+
end
|
68
|
+
|
69
|
+
%i(name full_name).each do |f|
|
70
|
+
return record.send(f) if record.respond_to?(f)
|
71
|
+
end
|
72
|
+
|
73
|
+
if record.respond_to?(:first_name) && record.respond_to?(:last_name)
|
74
|
+
return "#{record.send(:first_name)} #{record.send(:last_name)}"
|
75
|
+
end
|
76
|
+
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_message(reason)
|
81
|
+
I18n.t "password_validator.#{reason}", default: error_messages[reason] ||
|
82
|
+
error_messages[:fallback]
|
83
|
+
end
|
84
|
+
end
|