Skip to content

Commit 924e312

Browse files
authored
fix: Caseless in passwords validates as upper and lower (M2-10505) (#2038)
🔗 [Jira Ticket M2-10505](https://mindlogger.atlassian.net/browse/M2-10505) Changes include: - Validate caseless characters in passwords as both uppercase and lowercase. This is a follow-up to pull request #2036. After thinking through @jodybrookover’s comments, seems to make sense to allow caseless characters in passwords to validate as both uppercase and lowercase letters. There tend to be more caseless characters than uppercase or lowercase letters, and this matches the behavior already implemented on the frontend with: - ChildMindInstitute/mindlogger-admin#2207 - ChildMindInstitute/mindlogger-app-refactor#1089 - ChildMindInstitute/mindlogger-web-refactor#719
1 parent d2072f8 commit 924e312

2 files changed

Lines changed: 14 additions & 8 deletions

File tree

src/apps/users/password_validation.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,31 @@ def validate(cls, password: str) -> str:
3131
unicodecategories = {unicodedata.category(ch) for ch in normalized}
3232

3333
# Reject control characters
34-
if any(cat.startswith("C") for cat in unicodecategories):
34+
has_control = any(cat.startswith("C") for cat in unicodecategories)
35+
if has_control:
3536
raise PasswordContainsInvalidCharactersError()
3637

3738
# Reject any whitespace or blank characters that Unicode classifies as visible
38-
if any(ch.isspace() or ch in "\u2800\u3164\u115f\u1160\uffa0" for ch in normalized):
39+
has_whitespace = any(ch.isspace() or ch in "\u2800\u3164\u115f\u1160\uffa0" for ch in normalized)
40+
if has_whitespace:
3941
raise PasswordHasSpacesError()
4042

4143
# Minimum length as counted by '\X' graphemes
4244
if len(regex.findall(r"\X", normalized)) < config.min_length:
4345
raise PasswordTooShortError(chars=config.min_length)
4446

4547
# At least N of the following character types
48+
has_caseless = "Lo" in unicodecategories or "Lm" in unicodecategories # CJK, Arabic, Hebrew, etc.
49+
has_lowercase = "Ll" in unicodecategories
50+
has_uppercase = "Lu" in unicodecategories
51+
has_digit = "Nd" in unicodecategories
52+
has_symbol = any(not cat.startswith("L") and cat != "Nd" for cat in unicodecategories)
4653
types_present = sum(
4754
(
48-
"Ll" in unicodecategories, # lowercase
49-
"Lu" in unicodecategories, # uppercase
50-
"Lo" in unicodecategories or "Lm" in unicodecategories, # caseless (Arabic, CJK, Hebrew, etc.)
51-
"Nd" in unicodecategories, # digit
52-
any(not cat.startswith("L") and cat != "Nd" for cat in unicodecategories), # symbol
55+
has_lowercase or has_caseless, # lowercase or caseless (CJK, Arabic, Hebrew, etc.)
56+
has_uppercase or has_caseless, # uppercase or caseless (CJK, Arabic, Hebrew, etc.)
57+
has_digit, # digit
58+
has_symbol, # symbol
5359
)
5460
)
5561
if types_present < config.min_character_types:

src/apps/users/tests/unit/test_user_domain.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def test_public_user_from_user_model(base_data: BaseData):
9393
"Abcdefgh!@", # 3 types: lower + upper + symbol
9494
"abcdefg12!", # 3 types: lower + digit + symbol
9595
"ABCDEFG12!", # 3 types: upper + digit + symbol
96-
"abcdefg12\u65e5", # 3 types: lower + digit + caseless (CJK ideograph)
96+
"\u65e5123456789", # 3 types: lower (via caseless CJK) + upper (via caseless CJK) + digit
9797
"TestPass1!", # 4 types: lower + upper + digit + symbol
9898
"TestPass1!n\u0303", # 4 types: NFKC normalizes n + combining ~ to ñ
9999
"Abcdefgh!\U0001f1fa\U0001f1f3", # 9 chars + flag emoji (2 code points / 1 grapheme)

0 commit comments

Comments
 (0)