|
34 | 34 | ALGORITHM = "HS256"
|
35 | 35 | ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
36 | 36 | REFRESH_TOKEN_EXPIRE_DAYS = 30
|
37 |
| -PASSWORD_PATTERN = ( |
38 |
| - r"(?=.*\d)" # At least one digit |
39 |
| - r"(?=.*[a-z])" # At least one lowercase letter |
40 |
| - r"(?=.*[A-Z])" # At least one uppercase letter |
41 |
| - r"(?=.*[\\@$!%*?&{}<>.,'#\-_=+\(\)\[\]:;|~/\^])" # At least one special character |
42 |
| - r".{8,}" # At least 8 characters long |
43 |
| -) |
44 |
| -HTML_PASSWORD_PATTERN = ( |
45 |
| - r"(?=.*\d)" # At least one digit |
46 |
| - r"(?=.*[a-z])" # At least one lowercase letter |
47 |
| - r"(?=.*[A-Z])" # At least one uppercase letter |
48 |
| - r"(?=.*[\\@$!%*?&\{\}\<\>\.\,\\'#\-_=\+\(\)\[\]:;\|~\/])" # At least one special character |
| 37 | +PASSWORD_PATTERN_COMPONENTS = [ |
| 38 | + r"(?=.*\d)", # At least one digit |
| 39 | + r"(?=.*[a-z])", # At least one lowercase letter |
| 40 | + r"(?=.*[A-Z])", # At least one uppercase letter |
| 41 | + r"(?=.*[\[\]\\@$!%*?&{}<>.,'#\-_=+\(\):;|~/\^])", # At least one special character |
49 | 42 | r".{8,}" # At least 8 characters long
|
| 43 | +] |
| 44 | +COMPILED_PASSWORD_PATTERN = re.compile(r"".join(PASSWORD_PATTERN_COMPONENTS)) |
| 45 | + |
| 46 | + |
| 47 | +def convert_python_regex_to_html(regex: str) -> str: |
| 48 | + """ |
| 49 | + Replace each special character with its escaped version only when inside character classes. |
| 50 | + Ensures that the single quote "'" is doubly escaped. |
| 51 | + """ |
| 52 | + # Map each special char to its escaped form |
| 53 | + special_map = { |
| 54 | + '{': r'\{', |
| 55 | + '}': r'\}', |
| 56 | + '<': r'\<', |
| 57 | + '>': r'\>', |
| 58 | + '.': r'\.', |
| 59 | + '+': r'\+', |
| 60 | + '|': r'\|', |
| 61 | + ',': r'\,', |
| 62 | + "'": r"\\'", # doubly escaped single quote |
| 63 | + "/": r"\/", |
| 64 | + } |
| 65 | + |
| 66 | + # Regex to match the entire character class [ ... ] |
| 67 | + pattern = r"\[((?:\\.|[^\]])*)\]" |
| 68 | + |
| 69 | + def replacer(match: re.Match) -> str: |
| 70 | + """ |
| 71 | + For the matched character class, replace all special characters inside it. |
| 72 | + """ |
| 73 | + inside = match.group(1) # the contents inside [ ... ] |
| 74 | + for ch, escaped in special_map.items(): |
| 75 | + inside = inside.replace(ch, escaped) |
| 76 | + return f"[{inside}]" |
| 77 | + |
| 78 | + # Use re.sub with a function to ensure we only replace inside the character class |
| 79 | + return re.sub(pattern, replacer, regex) |
| 80 | + |
| 81 | + |
| 82 | +HTML_PASSWORD_PATTERN = "".join( |
| 83 | + convert_python_regex_to_html(component) for component in PASSWORD_PATTERN_COMPONENTS |
50 | 84 | )
|
51 |
| -COMPILED_PASSWORD_PATTERN = re.compile(HTML_PASSWORD_PATTERN) |
52 | 85 |
|
53 | 86 |
|
54 | 87 | # --- Custom Exceptions ---
|
|
0 commit comments