strong_password 0.0.1 → 0.0.2
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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21b8b80e1e1a71360c0227964a49eb4157ae65e6
|
4
|
+
data.tar.gz: b5165bce41a88de6ae57ba044c58e09b50059813
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 139743a7ca79896208779198592a532430f3f13aa62c47e6b5e29e62fa1a370d9fb47881aeed74fe29c71979c5b429220668b9e70b6069db9e9c9cb0d7d9029b
|
7
|
+
data.tar.gz: 0aaa1da057d4174f92d0456581d5bc883fca921db535d11a134e6f64ad247f7f9ace21fc91c1fa7ddb94ab661b8410b522aff74f3e5250ea447d55df7ada10cb
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ NOTE: StrongPassword requires the use of Ruby 2.0. Upgrade if you haven't alrea
|
|
15
15
|
|
16
16
|
Add this line to your application's Gemfile:
|
17
17
|
|
18
|
-
gem 'strong_password'
|
18
|
+
gem 'strong_password', '~> 0.0.2'
|
19
19
|
|
20
20
|
And then execute:
|
21
21
|
|
@@ -38,7 +38,7 @@ model, it simply has to be an object that includes `ActiveModel::Validations`.
|
|
38
38
|
```ruby
|
39
39
|
class User
|
40
40
|
include ActiveModel::Validations
|
41
|
-
|
41
|
+
|
42
42
|
# Basic usage. Defaults to minimum entropy of 18 and no dictionary checking
|
43
43
|
validates :password, password_strength: true
|
44
44
|
# Minimum entropy can be specified as min_entropy
|
@@ -53,7 +53,7 @@ class User
|
|
53
53
|
validates :password, password_strength: {extra_dictionary_words: :my_extra_words, use_dictionary: true}
|
54
54
|
# Alternative way to request password strength validation on a field
|
55
55
|
validates_password_strength :password
|
56
|
-
|
56
|
+
|
57
57
|
# Return an array of words to add to the dictionary we check against.
|
58
58
|
def my_extra_words
|
59
59
|
['extra', 'words', 'here', 'too']
|
@@ -79,15 +79,15 @@ password is strong or not. You can also directly access the entropy calculations
|
|
79
79
|
|
80
80
|
```text
|
81
81
|
2.0.0p0 :001 > checker = StrongPassword::StrengthChecker.new('password')
|
82
|
-
=> #<StrongPassword::StrengthChecker:0x007fcd7c939b48 @base_password="password">
|
82
|
+
=> #<StrongPassword::StrengthChecker:0x007fcd7c939b48 @base_password="password">
|
83
83
|
2.0.0p0 :002 > checker.is_strong?
|
84
|
-
=> false
|
84
|
+
=> false
|
85
85
|
2.0.0p0 :003 > checker.is_weak?
|
86
|
-
=> true
|
86
|
+
=> true
|
87
87
|
2.0.0p0 :004 > checker.is_strong?(min_entropy: 2)
|
88
|
-
=> true
|
88
|
+
=> true
|
89
89
|
2.0.0p0 :005 > checker.calculate_entropy
|
90
|
-
=> 15.5
|
90
|
+
=> 15.5
|
91
91
|
2.0.0p0 :006 > checker.calculate_entropy(use_dictionary: true)
|
92
92
|
=> 2
|
93
93
|
```
|
@@ -145,7 +145,7 @@ disallowed by the strength checker.
|
|
145
145
|
the 500 most common passwords. That also means it's not a true "dictionary" check...
|
146
146
|
2. Add a common password adjuster that basically works like the existing DictionaryAdjuster but does
|
147
147
|
not stop at the first found word. Stopping at the first word make sense if you have a 300,000 word
|
148
|
-
dictionary of the English language but not so much when you're only talking about the 500 most
|
148
|
+
dictionary of the English language but not so much when you're only talking about the 500 most
|
149
149
|
common passwords.
|
150
150
|
|
151
151
|
## Contributing
|
@@ -46,23 +46,23 @@ module StrongPassword
|
|
46
46
|
"blondes","enjoy","girl","apollo","parker","qwert","time","sydney","women","voodoo","magnum",
|
47
47
|
"juice","abgrtyu","777777","dreams","maxwell","music","rush2112","russia","scorpion","rebecca",
|
48
48
|
"tester","mistress","phantom","billy","6666","albert"]
|
49
|
-
|
49
|
+
|
50
50
|
attr_reader :base_password
|
51
|
-
|
51
|
+
|
52
52
|
def initialize(password)
|
53
53
|
@base_password = password.dup.downcase
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def is_strong?(min_entropy: 18, min_word_length: 4, extra_dictionary_words: [])
|
57
57
|
adjusted_entropy(entropy_threshhold: min_entropy,
|
58
58
|
min_word_length: min_word_length,
|
59
59
|
extra_dictionary_words: extra_dictionary_words) >= min_entropy
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
def is_weak?(min_entropy: 18, min_word_length: 4, extra_dictionary_words: [])
|
63
63
|
!is_strong?(min_entropy: min_entropy, min_word_length: min_word_length, extra_dictionary_words: extra_dictionary_words)
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
# Returns the minimum entropy for the passwords dictionary adjustments.
|
67
67
|
# If a threshhold is specified we will bail early to avoid unnecessary
|
68
68
|
# processing.
|
@@ -82,13 +82,13 @@ module StrongPassword
|
|
82
82
|
x2 = next_non_word ? next_non_word : variant.length + 1
|
83
83
|
found = false
|
84
84
|
while !found && (x2 - x >= min_word_length)
|
85
|
-
word = variant[x, min_word_length]
|
85
|
+
word = Regexp.quote(variant[x, min_word_length])
|
86
86
|
word += variant[(x + min_word_length)..x2].reverse.chars.inject('') {|memo, c| "(#{Regexp.quote(c)}#{memo})?"} if (x + min_word_length) <= y
|
87
87
|
results = dictionary_words.grep(/\b#{word}\b/)
|
88
88
|
if results.empty?
|
89
89
|
x = x + 1
|
90
90
|
numbits = EntropyCalculator.calculate('*' * x)
|
91
|
-
# If we have enough entropy at this length on a fully masked password with
|
91
|
+
# If we have enough entropy at this length on a fully masked password with
|
92
92
|
# duplicates weakened then we can just bail on this variant
|
93
93
|
found = true if entropy_threshhold >= 0 && numbits >= entropy_threshhold
|
94
94
|
else
|
@@ -9,13 +9,13 @@ module StrongPassword
|
|
9
9
|
subject.stub(adjusted_entropy: 18)
|
10
10
|
expect(subject.is_strong?).to be_true
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
it 'returns false if the calculated entropy is < the minimum' do
|
14
14
|
subject.stub(adjusted_entropy: 17)
|
15
15
|
expect(subject.is_strong?).to be_false
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
describe '#is_weak?' do
|
20
20
|
let(:subject) { DictionaryAdjuster.new('password') }
|
21
21
|
|
@@ -24,17 +24,17 @@ module StrongPassword
|
|
24
24
|
expect(subject.is_weak?).to be_false
|
25
25
|
end
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
describe '#adjusted_entropy' do
|
29
29
|
before(:each) { NistBonusBits.stub(bonus_bits: 0)}
|
30
|
-
|
30
|
+
|
31
31
|
it 'checks against all variants of a given password' do
|
32
32
|
password = 'password'
|
33
33
|
adjuster = DictionaryAdjuster.new(password)
|
34
34
|
PasswordVariants.should_receive(:all_variants).with(password).and_return([])
|
35
35
|
adjuster.adjusted_entropy
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
{
|
39
39
|
'bnm,./' => 14, # Qwerty string should not get adjusted by dictionary adjuster
|
40
40
|
'h#e0zbPas' => 19.5, # Random string should not get adjusted by dictionary adjuster
|
@@ -42,19 +42,21 @@ module StrongPassword
|
|
42
42
|
'E_!3password' => 11.5, # Adjusts common dictionary words regardless of placement
|
43
43
|
'h#e0zbPas 32e2i81 password' => 31.3125, # Even if there are multiple words
|
44
44
|
'123456' => 4, # Even if they are also qwerty strings
|
45
|
-
'password123456' => 16 # But only drops the first matched word
|
45
|
+
'password123456' => 16, # But only drops the first matched word
|
46
|
+
'asdf)asdf' => 14, # Doesn't break with parens
|
47
|
+
'asdf[]asdf' => 16 # Doesn't break with []s
|
46
48
|
}.each do |password, bits|
|
47
49
|
it "returns #{bits} for '#{password}'" do
|
48
50
|
expect(DictionaryAdjuster.new(password).adjusted_entropy).to eq(bits)
|
49
51
|
end
|
50
52
|
end
|
51
|
-
|
53
|
+
|
52
54
|
it 'allows extra words to be provided as an array' do
|
53
55
|
password = 'mcmanus'
|
54
56
|
base_entropy = EntropyCalculator.calculate(password)
|
55
57
|
expect(DictionaryAdjuster.new(password).adjusted_entropy(extra_dictionary_words: ['mcmanus'])).not_to eq(base_entropy)
|
56
58
|
end
|
57
|
-
|
59
|
+
|
58
60
|
it 'allows minimum word length to be adjusted' do
|
59
61
|
password = '6969'
|
60
62
|
base_entropy = DictionaryAdjuster.new(password).adjusted_entropy
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strong_password
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian McManus
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -105,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
105
|
version: '0'
|
106
106
|
requirements: []
|
107
107
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.0.
|
108
|
+
rubygems_version: 2.0.3
|
109
109
|
signing_key:
|
110
110
|
specification_version: 4
|
111
111
|
summary: StrongPassword adds a class to check password strength and a validator for
|