nobspw_rails7 0.6.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/cmer/nobspw_rails7.svg?branch=master)](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
|