Skip to content

Commit

Permalink
fix: ensure passwords have correct characters when mix_case & special…
Browse files Browse the repository at this point in the history
…_characters enabled
  • Loading branch information
tiff-o committed Sep 14, 2022
1 parent 7507876 commit 3c17192
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 32 deletions.
52 changes: 30 additions & 22 deletions lib/faker/default/internet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,37 +174,45 @@ def password(legacy_min_length = NOT_GIVEN, legacy_max_length = NOT_GIVEN, legac
keywords << :special_characters if legacy_special_characters != NOT_GIVEN
end

raise ArgumentError, 'Password of length 1 can not have both mixed case and special characters' if min_length <= 1 && mix_case && special_characters
raise ArgumentError, 'max_length must be more than min_length' if max_length < min_length

min_alpha = mix_case && min_length > 1 ? 2 : 0
temp = Lorem.characters(number: min_length, min_alpha: min_alpha)
diff_length = max_length - min_length

if diff_length.positive?
diff_rand = rand(diff_length + 1)
temp += Lorem.characters(number: diff_rand)
end
types = []
min_min_length = 0

if mix_case
alpha_count = 0
temp.chars.each_with_index do |char, index|
if char =~ /[[:alpha:]]/
temp[index] = char.upcase if alpha_count.even?
alpha_count += 1
end
end
types << :mix_case
min_min_length += 2
end

if special_characters
chars = %w[! @ # $ % ^ & *]
rand(1..min_length).times do |i|
temp[i] = chars[rand(chars.length)]
end
types << :special_characters
min_min_length += 1
end

raise ArgumentError, "min_length should be at least #{min_min_length} to enable #{types.join(', ')}" if min_length < min_min_length

target_length = rand(min_length..max_length)

password = []
character_bag = Lorem.characters(number: target_length).chars

if types.include?(:mix_case)
upper_chars = ('A'..'Z').to_a
password << upper_chars[rand(upper_chars.count - 1)]

lower_chars = ('a'..'z').to_a
password << lower_chars[rand(lower_chars.count - 1)]
end

if types.include?(:special_characters)
special_chars = %w[! @ # $ % ^ & *]
password << special_chars[rand(special_chars.count - 1)]
character_bag += special_chars
end

temp[rand(temp.size - 1)] = Lorem.characters(number: 1, min_alpha: 1).upcase if mix_case && special_characters && !temp.match(/[A-z]+/)
password << character_bag[rand(character_bag.count - 1)] while password.length < target_length

temp
password.shuffle.join
end
# rubocop:enable Metrics/ParameterLists

Expand Down
34 changes: 24 additions & 10 deletions test/faker/default/test_faker_internet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ def test_password

def test_password_with_integer_arg
(1..32).each do |min_length|
assert @tester.password(min_length: min_length, mix_case: false).length >= min_length
max_length = min_length + 1
assert_includes (min_length..max_length), @tester.password(min_length: min_length, max_length: max_length, mix_case: false).length
end
end

Expand Down Expand Up @@ -150,12 +151,17 @@ def test_password_with_mixed_case
assert downcase_count >= 1
end

def test_password_with_min_length_eq_1
min_length = 1
password = @tester.password(min_length: min_length)
def test_password_with_min_length_eq_1_without_mix_case
password = @tester.password(min_length: 1, mix_case: false)
assert_match(/\w+/, password)
end

def test_password_with_min_length_eq_1_with_mix_case
assert_raise 'min_length should be at least 2 to enable mix_case' do
@tester.password(min_length: 1, mix_case: true)
end
end

def test_password_with_min_length_and_max_length
min_length = 2
max_length = 5
Expand All @@ -164,6 +170,12 @@ def test_password_with_min_length_and_max_length
assert_includes (min_length..max_length), password.size, 'Password size is incorrect'
end

def test_password_with_max_length_less_than_min_length
assert_raise 'max_length must be more than min_length' do
@tester.password(min_length: 8, max_length: 4)
end
end

def test_password_without_mixed_case
assert_match(/[^A-Z]+/, @tester.password(min_length: 8, max_length: 12, mix_case: false))
end
Expand All @@ -179,29 +191,31 @@ def test_password_without_special_chars
def test_password_with_special_chars_and_mixed_case
32.times do
password = @tester.password(min_length: 4, max_length: 6, mix_case: true, special_characters: true)
assert_match(/[!@#$%\^&*]+/, password)
assert_match(/[A-z]+/, password)
assert_match(/[a-z]+/, password)
assert_match(/[!@#$%\^&*]+/, password)
end
end

def test_password_with_special_chars_and_mixed_case_on_2chars_password
def test_password_with_valid_min_length_for_mix_case_and_special_characters
16.times do
password = @tester.password(min_length: 2, max_length: 6, mix_case: true, special_characters: true)
password = @tester.password(min_length: 4, max_length: 6, mix_case: true, special_characters: true)
assert_match(/[!@#$%\^&*]+/, password)
assert_match(/[A-z]+/, password)
end
end

def test_password_with_incompatible_min_length_and_requirements
assert_raise ArgumentError do
def test_password_with_invalid_min_length_for_mix_case_and_special_characters
assert_raise_message 'min_length should be at least 3 to enable mix_case, special_characters' do
@tester.password(min_length: 1, mix_case: true, special_characters: true)
end
end

def test_password_with_compatible_min_length_and_requirements
assert_nothing_raised do
[false, true].each do |value|
@tester.password(min_length: 1, mix_case: value, special_characters: !value)
min_length = value ? 2 : 1
@tester.password(min_length: min_length, mix_case: value, special_characters: !value)
end
end
end
Expand Down

0 comments on commit 3c17192

Please sign in to comment.