Skip to content

Commit 5e1293b

Browse files
authored
Merge pull request #31 from Abdullah-l/Password-Rule
Password Rule
1 parent 8018d38 commit 5e1293b

4 files changed

Lines changed: 151 additions & 1 deletion

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## [Unreleased]
22

3+
## [0.7.1] - 2025-09-07
4+
5+
- Add password rule
6+
37
## [0.3.0] - 2023-04-05
48

59
- Add cleansing for data before validation
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# frozen_string_literal: true
2+
3+
class MiniDefender::Rules::Password < MiniDefender::Rule
4+
SPECIAL_CHARS = '!@#$%^&*()_+-=[]{}|;:,.<>?'.freeze
5+
MIN_LENGTH = 8
6+
7+
def self.signature
8+
'password'
9+
end
10+
11+
def passes?(attribute, value, validator)
12+
unless value.is_a?(String)
13+
return false
14+
end
15+
16+
@missing_requirements = []
17+
18+
check_length(value)
19+
check_lowercase(value)
20+
check_uppercase(value)
21+
check_digit(value)
22+
check_special_char(value)
23+
24+
@missing_requirements.empty?
25+
end
26+
27+
def message(attribute, value, validator)
28+
unless value.is_a?(String)
29+
return 'Password must be a string.'
30+
end
31+
32+
if @missing_requirements.nil? || @missing_requirements.empty?
33+
return 'Password is valid.'
34+
end
35+
36+
"Password is missing: #{@missing_requirements.join(', ')}."
37+
end
38+
39+
private
40+
41+
def check_length(value)
42+
if value.length < MIN_LENGTH
43+
@missing_requirements << "at least #{MIN_LENGTH} characters"
44+
end
45+
end
46+
47+
def check_lowercase(value)
48+
unless value.match?(/[a-z]/)
49+
@missing_requirements << 'at least one lowercase letter'
50+
end
51+
end
52+
53+
def check_uppercase(value)
54+
unless value.match?(/[A-Z]/)
55+
@missing_requirements << 'at least one uppercase letter'
56+
end
57+
end
58+
59+
def check_digit(value)
60+
unless value.match?(/\d/)
61+
@missing_requirements << 'at least one digit'
62+
end
63+
end
64+
65+
def check_special_char(value)
66+
unless value.chars.any? { |char| SPECIAL_CHARS.include?(char) }
67+
@missing_requirements << 'at least one special character'
68+
end
69+
end
70+
end

‎lib/mini_defender/version.rb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module MiniDefender
4-
VERSION = "0.7.0"
4+
VERSION = "0.7.1"
55
end

‎test/rules/password_test.rb‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# frozen_string_literal: true
2+
3+
require 'test_helper'
4+
5+
class PasswordTest < Minitest::Test
6+
def setup
7+
@rule = MiniDefender::Rules::Password.new
8+
end
9+
10+
def test_valid_password
11+
password = "Password123!"
12+
assert @rule.passes?("password", password, nil)
13+
assert_equal "Password is valid.", @rule.message("password", password, nil)
14+
end
15+
16+
def test_missing_lowercase
17+
password = "PASSWORD123!"
18+
refute @rule.passes?("password", password, nil)
19+
assert_equal "Password is missing: at least one lowercase letter.", @rule.message("password", password, nil)
20+
end
21+
22+
def test_missing_uppercase
23+
password = "password123!"
24+
refute @rule.passes?("password", password, nil)
25+
assert_equal "Password is missing: at least one uppercase letter.", @rule.message("password", password, nil)
26+
end
27+
28+
def test_missing_digit
29+
password = "Password!"
30+
refute @rule.passes?("password", password, nil)
31+
assert_equal "Password is missing: at least one digit.", @rule.message("password", password, nil)
32+
end
33+
34+
def test_missing_special_character
35+
password = "Password123"
36+
refute @rule.passes?("password", password, nil)
37+
assert_equal "Password is missing: at least one special character.", @rule.message("password", password, nil)
38+
end
39+
40+
def test_too_short
41+
password = "Pass1!"
42+
refute @rule.passes?("password", password, nil)
43+
assert_equal "Password is missing: at least 8 characters.", @rule.message("password", password, nil)
44+
end
45+
46+
def test_multiple_missing_requirements
47+
password = "pass"
48+
refute @rule.passes?("password", password, nil)
49+
message = @rule.message("password", password, nil)
50+
assert_includes message, "at least 8 characters"
51+
assert_includes message, "at least one uppercase letter"
52+
assert_includes message, "at least one digit"
53+
assert_includes message, "at least one special character"
54+
end
55+
56+
def test_non_string_input
57+
refute @rule.passes?("password", 12345, nil)
58+
assert_equal "Password must be a string.", @rule.message("password", 12345, nil)
59+
end
60+
61+
def test_nil_input
62+
refute @rule.passes?("password", nil, nil)
63+
assert_equal "Password must be a string.", @rule.message("password", nil, nil)
64+
end
65+
66+
def test_empty_string
67+
password = ""
68+
refute @rule.passes?("password", password, nil)
69+
message = @rule.message("password", password, nil)
70+
assert_includes message, "at least 8 characters"
71+
assert_includes message, "at least one lowercase letter"
72+
assert_includes message, "at least one uppercase letter"
73+
assert_includes message, "at least one digit"
74+
assert_includes message, "at least one special character"
75+
end
76+
end

0 commit comments

Comments
 (0)