strong_password 0.0.8 → 0.0.9
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
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90a81a17ad95741f57771cb8708cdf93f570ee20910d55b86c0c6c4104694fe0
|
4
|
+
data.tar.gz: 34f7e8e5e38eca56283e260895f5d0d54711e201344ba891861af7e53852b7ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5212716ea15246134da0ade5cf70497a78bf4ecf29198254cdc2ce2199717fb9af759212c0d327492a51a0d00e213ff0afc00c7fc3955fedfb5b875a8a6608e
|
7
|
+
data.tar.gz: 64e6df41029e605517e80a37f821c778964a6509a25bc5f975a3ac605b111d4e04ed229dab27cdc5789a9b88e431865ba848a0a67652a15621b56485feeb94e0
|
data/CHANGELOG
CHANGED
@@ -4,7 +4,7 @@ module ActiveModel
|
|
4
4
|
module Validations
|
5
5
|
class PasswordStrengthValidator < ActiveModel::EachValidator
|
6
6
|
def validate_each(object, attribute, value)
|
7
|
-
ps = ::StrongPassword::StrengthChecker.new(strength_options(options, object))
|
7
|
+
ps = ::StrongPassword::StrengthChecker.new(**strength_options(options, object))
|
8
8
|
unless ps.is_strong?(value.to_s)
|
9
9
|
object.errors.add(attribute, :'password.password_strength', options.merge(:value => value.to_s))
|
10
10
|
end
|
@@ -1,25 +1,25 @@
|
|
1
1
|
module StrongPassword
|
2
2
|
class QwertyAdjuster
|
3
3
|
QWERTY_STRINGS = [
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
'1234567890-',
|
5
|
+
'qwertyuiop',
|
6
|
+
'asdfghjkl;',
|
7
|
+
'zxcvbnm,./',
|
8
8
|
"1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,9ol.0p;/-['=]:?_{\"+}",
|
9
|
-
|
9
|
+
'1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik9ol0p',
|
10
10
|
"qazwsxedcrfvtgbyhnujmik,ol.p;/-['=]:?_{\"+}",
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
]
|
11
|
+
'qazwsxedcrfvtgbyhnujmikolp',
|
12
|
+
']"/=[;.-pl,0okm9ijn8uhb7ygv6tfc5rdx4esz3wa2q1',
|
13
|
+
'pl0okm9ijn8uhb7ygv6tfc5rdx4esz3wa2q1',
|
14
|
+
']"/[;.pl,okmijnuhbygvtfcrdxeszwaq',
|
15
|
+
'plokmijnuhbygvtfcrdxeszwaq',
|
16
|
+
'014725836914702583697894561230258/369*+-*/',
|
17
|
+
'abcdefghijklmnopqrstuvwxyz'
|
18
|
+
].freeze
|
19
19
|
|
20
20
|
attr_reader :min_entropy, :entropy_threshhold
|
21
21
|
|
22
|
-
def initialize(min_entropy: 18, entropy_threshhold:
|
22
|
+
def initialize(min_entropy: 18, entropy_threshhold: min_entropy)
|
23
23
|
@min_entropy = min_entropy
|
24
24
|
@entropy_threshhold = entropy_threshhold
|
25
25
|
end
|
@@ -37,23 +37,21 @@ module StrongPassword
|
|
37
37
|
# early to avoid unnecessary processing.
|
38
38
|
def adjusted_entropy(base_password)
|
39
39
|
revpassword = base_password.reverse
|
40
|
-
|
40
|
+
lowest_entropy = [EntropyCalculator.calculate(base_password), EntropyCalculator.calculate(revpassword)].min
|
41
|
+
# If our entropy is already lower than we care about then there's no reason to look further.
|
42
|
+
return lowest_entropy if lowest_entropy < entropy_threshhold
|
43
|
+
|
41
44
|
qpassword = mask_qwerty_strings(base_password)
|
45
|
+
lowest_entropy = [lowest_entropy, EntropyCalculator.calculate(qpassword)].min if qpassword != base_password
|
46
|
+
# Bail early if our entropy on the base password's masked qwerty value is less than our threshold.
|
47
|
+
return lowest_entropy if lowest_entropy < entropy_threshhold
|
48
|
+
|
42
49
|
qrevpassword = mask_qwerty_strings(revpassword)
|
43
|
-
if
|
44
|
-
|
45
|
-
min_entropy = [min_entropy, numbits].min
|
46
|
-
return min_entropy if min_entropy < entropy_threshhold
|
47
|
-
end
|
48
|
-
if qrevpassword != revpassword
|
49
|
-
numbits = EntropyCalculator.calculate(qrevpassword)
|
50
|
-
min_entropy = [min_entropy, numbits].min
|
51
|
-
return min_entropy if min_entropy < entropy_threshhold
|
52
|
-
end
|
53
|
-
min_entropy
|
50
|
+
lowest_entropy = [lowest_entropy, EntropyCalculator.calculate(qrevpassword)].min if qrevpassword != revpassword
|
51
|
+
lowest_entropy
|
54
52
|
end
|
55
53
|
|
56
|
-
|
54
|
+
private
|
57
55
|
|
58
56
|
def all_qwerty_strings
|
59
57
|
@all_qwerty_strings ||= Regexp.union(QWERTY_STRINGS.flat_map do |qwerty_string|
|
@@ -2,31 +2,29 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module StrongPassword
|
4
4
|
describe QwertyAdjuster do
|
5
|
-
|
6
|
-
let(:subject) { QwertyAdjuster.new }
|
5
|
+
subject(:qwerty_adjuster) { QwertyAdjuster.new(entropy_threshhold: 0) }
|
7
6
|
|
7
|
+
describe '#is_strong?' do
|
8
8
|
it 'returns true if the calculated entropy is >= the minimum' do
|
9
9
|
allow(subject).to receive_messages(adjusted_entropy: 18)
|
10
|
-
expect(subject.is_strong?(
|
10
|
+
expect(subject.is_strong?('password')).to be_truthy
|
11
11
|
end
|
12
12
|
|
13
13
|
it 'returns false if the calculated entropy is < the minimum' do
|
14
14
|
allow(subject).to receive_messages(adjusted_entropy: 17)
|
15
|
-
expect(subject.is_strong?(
|
15
|
+
expect(subject.is_strong?('password')).to be_falsey
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
describe '#is_weak?' do
|
20
|
-
let(:subject) { QwertyAdjuster.new }
|
21
|
-
|
22
20
|
it 'returns the opposite of is_strong?' do
|
23
21
|
allow(subject).to receive_messages(is_strong?: true)
|
24
|
-
expect(subject.is_weak?(
|
22
|
+
expect(subject.is_weak?('password')).to be_falsey
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|
28
26
|
describe '#adjusted_entropy' do
|
29
|
-
before(:each) { allow(NistBonusBits).to receive_messages(bonus_bits: 0)}
|
27
|
+
before(:each) { allow(NistBonusBits).to receive_messages(bonus_bits: 0) }
|
30
28
|
{
|
31
29
|
'qwertyuio' => 5.5,
|
32
30
|
'1234567' => 6,
|
@@ -37,7 +35,19 @@ module StrongPassword
|
|
37
35
|
'password' => 17.5 # Ensure that we don't qwerty-adjust 'password'
|
38
36
|
}.each do |password, bits|
|
39
37
|
it "returns #{bits} for '#{password}'" do
|
40
|
-
expect(
|
38
|
+
expect(subject.adjusted_entropy(password)).to eq(bits)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'with a default entropy threshhold' do
|
43
|
+
subject(:qwerty_adjuster) { QwertyAdjuster.new }
|
44
|
+
|
45
|
+
it 'returns the higher entropy before running qwerty adjustments' do
|
46
|
+
# Default threshhold is equal to the default min_entropy for a strong password (18).
|
47
|
+
# When not set we should get the base password's entropy from this (16) instead of the
|
48
|
+
# lower qwerty-adjusted entropy (6) indicating we avoided doing additional work in the
|
49
|
+
# qwerty adjustment code since we already know the password is weak.
|
50
|
+
expect(subject.adjusted_entropy('1234567')).to eq(16)
|
41
51
|
end
|
42
52
|
end
|
43
53
|
end
|
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.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian McManus
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -119,8 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
119
|
- !ruby/object:Gem::Version
|
120
120
|
version: '0'
|
121
121
|
requirements: []
|
122
|
-
|
123
|
-
rubygems_version: 2.7.6
|
122
|
+
rubygems_version: 3.0.3
|
124
123
|
signing_key:
|
125
124
|
specification_version: 4
|
126
125
|
summary: StrongPassword adds a class to check password strength and a validator for
|