53fb25efcf
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 2s
CI/CD Pipeline / Check push changed paths (push) Successful in 19s
CI/CD Pipeline / Build Staging API Image (push) Successful in 41s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 48s
CI/CD Pipeline / Integration Tests (push) Successful in 3m10s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 3m17s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m30s
CI/CD Pipeline / Validate - Style (push) Successful in 4m17s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 59s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 5m33s
CI/CD Pipeline / Validate - Security (push) Successful in 7m12s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m38s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m13s
CI/CD Pipeline / Unit Tests (push) Successful in 10m11s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Failing after 26h14m3s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 26h24m21s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 26h19m47s
CI/CD Pipeline / PR Build Web Image (push) Failing after 26h23m44s
CI/CD Pipeline / PR Build API Image (push) Failing after 26h23m44s
CI/CD Pipeline / Deploy Production (push) Failing after 26h13m23s
CI/CD Pipeline / Build Production Web Image (push) Failing after 26h13m26s
CI/CD Pipeline / CI Gate (push) Failing after 26h13m25s
CI/CD Pipeline / Build Production API Image (push) Failing after 26h13m26s
CI/CD Pipeline / Canary Release to Production (push) Failing after 26h13m23s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 26h19m46s
CI/CD Pipeline / Frontend Lint (push) Failing after 26h23m37s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 26h23m45s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 26h19m46s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
127 lines
3.2 KiB
Python
127 lines
3.2 KiB
Python
"""
|
||
密码处理器委托层
|
||
|
||
此模块作为 Domain 层 (packages.domain.auth.password_hasher) 和 Application 层之间的委托层,
|
||
隔离 Domain 层对 bcrypt 库的直接依赖。
|
||
|
||
使用方式:
|
||
from packages.application.auth.password_handler import PasswordHandler, get_password_handler
|
||
|
||
password_handler = PasswordHandler()
|
||
hashed = password_handler.hash_password("my_secure_password")
|
||
is_valid = password_handler.verify_password("my_secure_password", hashed)
|
||
"""
|
||
|
||
from typing import Optional
|
||
|
||
from packages.application.auth.password_hasher import PasswordHasher, PasswordValidator
|
||
|
||
|
||
class PasswordHandler:
|
||
"""
|
||
密码处理器委托类
|
||
|
||
委托给 packages.domain.auth.password_hasher 进行实际的密码哈希操作,
|
||
此层仅负责配置和封装,不直接依赖 bcrypt 库。
|
||
"""
|
||
|
||
def __init__(self, rounds: int = 12):
|
||
"""
|
||
初始化密码处理器
|
||
|
||
Args:
|
||
rounds: bcrypt cost factor(默认 12,推荐范围 10-14)
|
||
"""
|
||
self._hasher = PasswordHasher(rounds=rounds)
|
||
self._validator = PasswordValidator(
|
||
min_length=8,
|
||
require_uppercase=True,
|
||
require_lowercase=True,
|
||
require_digit=True,
|
||
require_special=False,
|
||
)
|
||
|
||
def hash_password(self, password: str) -> str:
|
||
"""
|
||
哈希密码
|
||
|
||
Args:
|
||
password: 明文密码
|
||
|
||
Returns:
|
||
bcrypt 哈希字符串
|
||
|
||
Raises:
|
||
ValueError: 密码为空
|
||
"""
|
||
return self._hasher.hash_password(password)
|
||
|
||
def verify_password(self, password: str, hashed_password: str) -> bool:
|
||
"""
|
||
验证密码
|
||
|
||
Args:
|
||
password: 明文密码
|
||
hashed_password: 存储的哈希密码
|
||
|
||
Returns:
|
||
True 如果密码正确,否则 False
|
||
"""
|
||
return self._hasher.verify_password(password, hashed_password)
|
||
|
||
def needs_rehash(self, hashed_password: str) -> bool:
|
||
"""
|
||
检查哈希是否需要重新计算
|
||
|
||
Args:
|
||
hashed_password: 存储的哈希密码
|
||
|
||
Returns:
|
||
True 如果需要重新哈希
|
||
"""
|
||
return self._hasher.needs_rehash(hashed_password)
|
||
|
||
def validate_strength(self, password: str) -> tuple[bool, Optional[str]]:
|
||
"""
|
||
验证密码强度
|
||
|
||
Args:
|
||
password: 明文密码
|
||
|
||
Returns:
|
||
(是否有效, 错误信息)
|
||
"""
|
||
return self._validator.validate(password)
|
||
|
||
|
||
# 默认处理器实例
|
||
_default_handler: Optional[PasswordHandler] = None
|
||
|
||
|
||
def configure_password_handler(rounds: int = 12) -> PasswordHandler:
|
||
"""
|
||
配置全局密码处理器
|
||
|
||
Args:
|
||
rounds: bcrypt cost factor
|
||
|
||
Returns:
|
||
配置好的 PasswordHandler 实例
|
||
"""
|
||
global _default_handler
|
||
_default_handler = PasswordHandler(rounds=rounds)
|
||
return _default_handler
|
||
|
||
|
||
def get_password_handler() -> PasswordHandler:
|
||
"""
|
||
获取全局密码处理器
|
||
|
||
Returns:
|
||
PasswordHandler 实例
|
||
"""
|
||
global _default_handler
|
||
if _default_handler is None:
|
||
_default_handler = PasswordHandler()
|
||
return _default_handler
|