Class: PasswordStrength::Base
- Inherits:
-
Object
- Object
- PasswordStrength::Base
- Defined in:
- lib/password_strength/base.rb
Direct Known Subclasses
Constant Summary collapse
- MULTIPLE_NUMBERS_RE =
/\d.*?\d.*?\d/
- MULTIPLE_SYMBOLS_RE =
/[!@#\$%^&*?_~-].*?[!@#\$%^&*?_~-]/
- SYMBOL_RE =
/[!@#\$%^&*?_~-]/
- UPPERCASE_LOWERCASE_RE =
/([a-z].*[A-Z])|([A-Z].*[a-z])/
- INVALID =
:invalid
- WEAK =
:weak
- STRONG =
:strong
- GOOD =
:good
Instance Attribute Summary collapse
-
#exclude ⇒ Object
Set what characters cannot be present on password.
-
#password ⇒ Object
The password that will be tested.
-
#record ⇒ Object
readonly
The ActiveRecord instance.
-
#score ⇒ Object
readonly
The score for the latest test.
-
#status ⇒ Object
readonly
The current test status.
-
#username ⇒ Object
Hold the username that will be matched against password.
Class Method Summary collapse
-
.common_words ⇒ Object
Return an array of strings that represents common passwords.
Instance Method Summary collapse
-
#common_word? ⇒ Boolean
:nodoc:.
-
#contain_invalid_matches? ⇒ Boolean
:nodoc:.
- #contain_invalid_repetion? ⇒ Boolean
-
#good! ⇒ Object
Mark password as good.
-
#good? ⇒ Boolean
Check if the password has been detected as good.
-
#initialize(username, password, options = {}) ⇒ Base
constructor
A new instance of Base.
-
#invalid! ⇒ Object
Mark password as invalid.
-
#invalid? ⇒ Boolean
Check if password has invalid characters based on PasswordStrength::Base#exclude.
-
#repetitions(text, size) ⇒ Object
:nodoc:.
-
#score_for(name) ⇒ Object
Return the score for the specified rule.
-
#sequences(text) ⇒ Object
:nodoc:.
-
#strong! ⇒ Object
Mark password as strong.
-
#strong? ⇒ Boolean
Check if the password has been detected as strong.
-
#test ⇒ Object
Run all tests on password and return the final score.
-
#valid?(level = GOOD) ⇒ Boolean
Check if the password has the specified score.
-
#weak! ⇒ Object
Mark password as weak.
-
#weak? ⇒ Boolean
Check if the password has been detected as weak.
Constructor Details
#initialize(username, password, options = {}) ⇒ Base
Returns a new instance of Base.
63 64 65 66 67 68 69 |
# File 'lib/password_strength/base.rb', line 63 def initialize(username, password, = {}) @username = username.to_s @password = password.to_s @score = 0 @exclude = [:exclude] @record = [:record] end |
Instance Attribute Details
#exclude ⇒ Object
Set what characters cannot be present on password. Can be a regular expression or array.
strength = PasswordStrength.test("john", "password with whitespaces", :exclude => [" ", "asdf"])
strength = PasswordStrength.test("john", "password with whitespaces", :exclude => /\s/)
Then you can check the test result:
strength.valid?(:weak)
#=> false
strength.status
#=> :invalid
42 43 44 |
# File 'lib/password_strength/base.rb', line 42 def exclude @exclude end |
#password ⇒ Object
The password that will be tested.
16 17 18 |
# File 'lib/password_strength/base.rb', line 16 def password @password end |
#record ⇒ Object (readonly)
The ActiveRecord instance. It only makes sense if you’re creating a custom ActiveRecord validator.
26 27 28 |
# File 'lib/password_strength/base.rb', line 26 def record @record end |
#score ⇒ Object (readonly)
The score for the latest test. Will be nil
if the password has not been tested.
19 20 21 |
# File 'lib/password_strength/base.rb', line 19 def score @score end |
#status ⇒ Object (readonly)
The current test status. Can be :weak
, :good
, :strong
or :invalid
.
22 23 24 |
# File 'lib/password_strength/base.rb', line 22 def status @status end |
#username ⇒ Object
Hold the username that will be matched against password.
13 14 15 |
# File 'lib/password_strength/base.rb', line 13 def username @username end |
Class Method Details
.common_words ⇒ Object
Return an array of strings that represents common passwords. The default list is taken from several online sources (just Google for ‘most common passwords’).
Notable sources:
The current list has 3.6KB and its load into memory just once.
54 55 56 57 58 59 60 61 |
# File 'lib/password_strength/base.rb', line 54 def self.common_words @common_words ||= begin file = File.open(File.("../../../support/common.txt", __FILE__)) words = file.each_line.to_a.map(&:chomp) file.close words end end |
Instance Method Details
#common_word? ⇒ Boolean
:nodoc:
217 218 219 |
# File 'lib/password_strength/base.rb', line 217 def common_word? # :nodoc: self.class.common_words.include?(password.downcase) end |
#contain_invalid_matches? ⇒ Boolean
:nodoc:
221 222 223 224 225 226 |
# File 'lib/password_strength/base.rb', line 221 def contain_invalid_matches? # :nodoc: return false unless exclude regex = exclude regex = /#{exclude.collect {|i| Regexp.escape(i)}.join("|")}/ if exclude.kind_of?(Array) password.to_s =~ regex end |
#contain_invalid_repetion? ⇒ Boolean
228 229 230 231 232 233 |
# File 'lib/password_strength/base.rb', line 228 def contain_invalid_repetion? char = password.to_s.chars.first return unless char regex = /^#{Regexp.escape(char)}+$/i password.to_s =~ regex end |
#good! ⇒ Object
Mark password as good.
110 111 112 |
# File 'lib/password_strength/base.rb', line 110 def good! @status = GOOD end |
#good? ⇒ Boolean
Check if the password has been detected as good.
105 106 107 |
# File 'lib/password_strength/base.rb', line 105 def good? status == GOOD end |
#invalid! ⇒ Object
Mark password as invalid.
120 121 122 |
# File 'lib/password_strength/base.rb', line 120 def invalid! @status = INVALID end |
#invalid? ⇒ Boolean
Check if password has invalid characters based on PasswordStrength::Base#exclude.
115 116 117 |
# File 'lib/password_strength/base.rb', line 115 def invalid? status == INVALID end |
#repetitions(text, size) ⇒ Object
:nodoc:
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/password_strength/base.rb', line 235 def repetitions(text, size) # :nodoc: count = 0 matches = [] 0.upto(text.size - 1) do |i| substring = text[i, size] next if matches.include?(substring) || substring.size < size matches << substring occurrences = text.scan(/#{Regexp.escape(substring)}/).length count += 1 if occurrences > 1 end count end |
#score_for(name) ⇒ Object
Return the score for the specified rule. Available rules:
-
:password_size
-
:numbers
-
:symbols
-
:uppercase_lowercase
-
:numbers_chars
-
:numbers_symbols
-
:symbols_chars
-
:only_chars
-
:only_numbers
-
:username
-
:sequences
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/password_strength/base.rb', line 138 def score_for(name) score = 0 case name when :password_size then if password.size < 6 score = -100 else score = password.size * 4 end when :numbers then score = 5 if password =~ MULTIPLE_NUMBERS_RE when :symbols then score = 5 if password =~ MULTIPLE_SYMBOLS_RE when :uppercase_lowercase then score = 10 if password =~ UPPERCASE_LOWERCASE_RE when :numbers_chars then score = 15 if password =~ /[a-z]/i && password =~ /[0-9]/ when :numbers_symbols then score = 15 if password =~ /[0-9]/ && password =~ SYMBOL_RE when :symbols_chars then score = 15 if password =~ /[a-z]/i && password =~ SYMBOL_RE when :only_chars then score = -15 if password =~ /^[a-z]+$/i when :only_numbers then score = -15 if password =~ /^\d+$/ when :username then if password == username score = -100 else score = -15 if password =~ /#{Regexp.escape(username)}/ end when :sequences then score = -15 * sequences(password) score += -15 * sequences(password.to_s.reverse) when :repetitions then score += -(repetitions(password, 2) * 4) score += -(repetitions(password, 3) * 3) score += -(repetitions(password, 4) * 2) end score end |
#sequences(text) ⇒ Object
:nodoc:
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/password_strength/base.rb', line 252 def sequences(text) # :nodoc: matches = 0 sequence_size = 0 bytes = [] text.to_s.each_byte do |byte| previous_byte = bytes.last bytes << byte if previous_byte && ((byte == previous_byte + 1) || (previous_byte == byte)) sequence_size += 1 else sequence_size = 0 end matches += 1 if sequence_size == 2 end matches end |
#strong! ⇒ Object
Mark password as strong.
90 91 92 |
# File 'lib/password_strength/base.rb', line 90 def strong! @status = STRONG end |
#strong? ⇒ Boolean
Check if the password has been detected as strong.
85 86 87 |
# File 'lib/password_strength/base.rb', line 85 def strong? status == STRONG end |
#test ⇒ Object
Run all tests on password and return the final score.
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/password_strength/base.rb', line 183 def test @score = 0 if contain_invalid_matches? invalid! elsif common_word? invalid! elsif contain_invalid_repetion? invalid! else @score += score_for(:password_size) @score += score_for(:numbers) @score += score_for(:symbols) @score += score_for(:uppercase_lowercase) @score += score_for(:numbers_chars) @score += score_for(:numbers_symbols) @score += score_for(:symbols_chars) @score += score_for(:only_chars) @score += score_for(:only_numbers) @score += score_for(:username) @score += score_for(:sequences) @score += score_for(:repetitions) @score = 0 if score < 0 @score = 100 if score > 100 weak! if score < 35 good! if score >= 35 && score < 70 strong! if score >= 70 end score end |
#valid?(level = GOOD) ⇒ Boolean
Check if the password has the specified score. Level can be :weak
, :good
or :strong
.
73 74 75 76 77 78 79 80 81 82 |
# File 'lib/password_strength/base.rb', line 73 def valid?(level = GOOD) case level when STRONG then strong? when GOOD then good? || strong? else !invalid? end end |
#weak! ⇒ Object
Mark password as weak.
100 101 102 |
# File 'lib/password_strength/base.rb', line 100 def weak! @status = WEAK end |
#weak? ⇒ Boolean
Check if the password has been detected as weak.
95 96 97 |
# File 'lib/password_strength/base.rb', line 95 def weak? status == WEAK end |