From e7477ec46805a8d1391bbce41265230b835071a5 Mon Sep 17 00:00:00 2001 From: Agzamus Date: Mon, 12 Oct 2026 16:17:28 +0300 Subject: [PATCH] =?UTF-8?q?feat:=205.1-3=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=20`app/core?= =?UTF-8?q?/passwords.py`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/core/__init__.py | 0 app/core/passwords.py | 50 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 app/core/__init__.py create mode 100644 app/core/passwords.py diff --git a/app/core/__init__.py b/app/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/core/passwords.py b/app/core/passwords.py new file mode 100644 index 0000000..01c9e4d --- /dev/null +++ b/app/core/passwords.py @@ -0,0 +1,50 @@ +from argon2 import PasswordHasher +from argon2.exceptions import InvalidHashError, VerifyMismatchError +from argon2.low_level import Type +from zxcvbn import zxcvbn + +from app.config import settings + + +class WeakPasswordError(ValueError): + pass + + +_hasher = PasswordHasher( + time_cost=3, + memory_cost=65536, + parallelism=4, + hash_len=32, + salt_len=16, + type=Type.ID, +) + + +def _peppered(password: str) -> str: + return password + settings.password_pepper + + +# возвращает хэш Argon2id от password + pepper; +def hash_password(password: str) -> str: + return _hasher.hash(_peppered(password)) + + +# проверяет соответствие +def verify_password(password: str, hashed: str) -> bool: + try: + return _hasher.verify(hashed, _peppered(password)) + except (VerifyMismatchError, InvalidHashError): + return False + + +# проверяет политику +def validate_policy(password: str, user_inputs: list[str] | None = None) -> None: + # длина не меньше settings.password_min_length; + if len(password) < settings.password_min_length: + raise WeakPasswordError( + f"Password must be >= {settings.password_min_length} chars" + ) + result = zxcvbn(password, user_inputs=user_inputs or []) + # оценка стойкости через zxcvbn должна быть не ниже 3 (по шкале 0–4); + if result["score"] < 3: + raise WeakPasswordError("Password is too weak")