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 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