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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ff0a9b36d196559340b528cee7aa35bec281e37e
4
- data.tar.gz: f44cf6a9c9824448cf6cb5ecf0377bc12ce6a965
3
+ metadata.gz: 391e780d690175f0dd19aec4f70506b81c587632
4
+ data.tar.gz: 164a2ed02a9041eeb5f46c0db2e13f1f7965b832
5
5
  SHA512:
6
- metadata.gz: 19b6e2193005dff2b3af38ffa83c78fce9dcb1ff159e63c13028085c8f4ae283628e6453081ab2041ea31277afb8e2bddc52f7109d738e05a1875be33f12cb50
7
- data.tar.gz: 344471dfe8e33acd0e7b82cdbc59f719e3b67b1071727129b5e196397d5c1e2ab858a641072f0a8748d96635f1d45cb3b130ddb7db2e5a502b4396a93c7793d2
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
- TODO: Write usage instructions here
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
@@ -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
- attr_accessor :configuration
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.strip, username.strip, email.strip, password.strip
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
- words_included_in_password?(domain) ||
56
- remove_word_separators(words_included_in_password?(domain)).gsub(' ', '')
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)
@@ -1,3 +1,3 @@
1
1
  module NOBSPW
2
- VERSION = "0.1.0"
2
+ VERSION = '0.2.0'
3
3
  end
@@ -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.1.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 00:00:00.000000000 Z
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