From 72de495c0207ae0c519d5dc37046959a92f3e96b Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 6 Sep 2025 04:59:45 +0300 Subject: [PATCH] patch auth-validators --- common_lib/utils/auth.py | 76 ++++++---------------------------- common_lib/utils/validators.py | 71 +++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 3 files changed, 84 insertions(+), 65 deletions(-) create mode 100644 common_lib/utils/validators.py diff --git a/common_lib/utils/auth.py b/common_lib/utils/auth.py index 1508e02..e735d9c 100644 --- a/common_lib/utils/auth.py +++ b/common_lib/utils/auth.py @@ -1,5 +1,4 @@ import httpx -import re from fastapi import Depends, Request, HTTPException, status from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from typing import List @@ -7,6 +6,7 @@ from typing import List from dataclasses import dataclass from config import settings from .ssl_transport import ssl_transport +from .validators import validate_username as core_validate_username, validate_password as core_validate_password auth_scheme = HTTPBearer() @@ -86,54 +86,15 @@ def validate_username(value: str, - no consecutive underscores - only [A-Za-z0-9_] """ - # Проверка типа и длины - if not isinstance(value, str) or not (min_length <= len(value) <= max_length): - msg = f"{field_name.capitalize()} must be between {min_length} and {max_length} characters long" + valid, result = core_validate_username(value, field_name, need_back=True, min_length=min_length, max_length=max_length) + if not valid: if with_httpexception: raise HTTPException(status_code=error_status_code, detail=error_detail) if need_back: - return False, msg - raise ValueError(msg) + return False, result + raise ValueError(result) - # Пробелы - if any(c.isspace() for c in value): - msg = f"{field_name.capitalize()} must not contain whitespace characters" - if with_httpexception: - raise HTTPException(status_code=error_status_code, detail=error_detail) - if need_back: - return False, msg - raise ValueError(msg) - - # Начинается с подчеркивания - if value.startswith('_'): - msg = f"{field_name.capitalize()} must not start with an underscore" - if with_httpexception: - raise HTTPException(status_code=error_status_code, detail=error_detail) - if need_back: - return False, msg - raise ValueError(msg) - - # Двойные подчеркивания - if '__' in value: - msg = f"{field_name.capitalize()} must not contain consecutive underscores" - if with_httpexception: - raise HTTPException(status_code=error_status_code, detail=error_detail) - if need_back: - return False, msg - raise ValueError(msg) - - # Только допустимые символы - if not re.fullmatch(r'[A-Za-z0-9_]+', value): - msg = f"{field_name.capitalize()} must contain only English letters, digits, and underscores" - if with_httpexception: - raise HTTPException(status_code=error_status_code, detail=error_detail) - if need_back: - return False, msg - raise ValueError(msg) - - if need_back: - return True, value.lower() - return value.lower() + return result if need_back else result def validate_password(value: str, @@ -149,27 +110,14 @@ def validate_password(value: str, Supports HTTPException raising if `with_httpexception=True`. Returns (True, value) or raises ValueError. """ - # Проверка типа и длины - if not isinstance(value, str) or not (min_length <= len(value) <= max_length): - msg = f"{field_name.capitalize()} must be between {min_length} and {max_length} characters long" + valid, result = core_validate_password(value, field_name, need_back=True, min_length=min_length, max_length=max_length) + if not valid: if with_httpexception: raise HTTPException(status_code=error_status_code, detail=error_detail) if need_back: - return False, msg - raise ValueError(msg) - - # if any(c.isspace() for c in value): - # raise ValueError(f"{field_name.capitalize()} must not contain whitespace characters") - - # if not re.search(r'[A-Z]', value): - # raise ValueError(f"{field_name.capitalize()} must contain at least one uppercase letter") - - # if not re.search(r'\d', value): - # raise ValueError(f"{field_name.capitalize()} must contain at least one digit") - - # if not re.search(r'[!@#$%^&*()\-_=+\[\]{};:\'",.<>?/|\\]', value): - # raise ValueError(f"{field_name.capitalize()} must contain at least one special character") + return False, result + raise ValueError(result) if need_back: - return True, value - return value + return True, value.lower() + return value.lower() diff --git a/common_lib/utils/validators.py b/common_lib/utils/validators.py new file mode 100644 index 0000000..6615ec2 --- /dev/null +++ b/common_lib/utils/validators.py @@ -0,0 +1,71 @@ +import re + +def validate_username(value: str, + field_name: str = "login", + need_back=False, + min_length=3, + max_length=32) -> str: + """ + Username validator: + - checks for length + - no spaces + - no leading underscore + - no consecutive underscores + - only [A-Za-z0-9_] + """ + # Проверка типа и длины + if not isinstance(value, str) or not (min_length <= len(value) <= max_length): + msg = f"{field_name.capitalize()} must be between {min_length} and {max_length} characters long" + return (False, msg) if need_back else False + + # Пробелы + if any(c.isspace() for c in value): + msg = f"{field_name.capitalize()} must not contain whitespace characters" + return (False, msg) if need_back else False + + # Начинается с подчеркивания + if value.startswith('_'): + msg = f"{field_name.capitalize()} must not start with an underscore" + return (False, msg) if need_back else False + + # Двойные подчеркивания + if '__' in value: + msg = f"{field_name.capitalize()} must not contain consecutive underscores" + return (False, msg) if need_back else False + + # Только допустимые символы + if not re.fullmatch(r'[A-Za-z0-9_]+', value): + msg = f"{field_name.capitalize()} must contain only English letters, digits, and underscores" + return (False, msg) if need_back else False + + return (True, value.lower()) if need_back else value.lower() + + +def validate_password(value: str, + field_name: str = "password", + need_back=False, + min_length=8, + max_length=128) -> str: + """ + Validates password length and (optionally) other rules. + Supports HTTPException raising if `with_httpexception=True`. + Returns (True, value) or raises ValueError. + """ + # Проверка типа и длины + if not isinstance(value, str) or not (min_length <= len(value) <= max_length): + msg = f"{field_name.capitalize()} must be between {min_length} and {max_length} characters long" + return (False, msg) if need_back else False + + # if any(c.isspace() for c in value): + # raise ValueError(f"{field_name.capitalize()} must not contain whitespace characters") + + # if not re.search(r'[A-Z]', value): + # raise ValueError(f"{field_name.capitalize()} must contain at least one uppercase letter") + + # if not re.search(r'\d', value): + # raise ValueError(f"{field_name.capitalize()} must contain at least one digit") + + # if not re.search(r'[!@#$%^&*()\-_=+\[\]{};:\'",.<>?/|\\]', value): + # raise ValueError(f"{field_name.capitalize()} must contain at least one special character") + + return (True, value) if need_back else value diff --git a/pyproject.toml b/pyproject.toml index 8612b7f..54e04fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "common-lib" -version = "0.0.14" +version = "0.0.15" description = "Библиотека общих компонентов для микросервисов yobble" authors = [{ name = "cheykrym", email = "you@example.com" }] license = "MIT"