mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-10 03:27:57 +00:00
refac
This commit is contained in:
parent
4bc4630721
commit
a70a6589af
3 changed files with 33 additions and 19 deletions
|
|
@ -729,6 +729,7 @@ WEBUI_AUTH_TRUSTED_ROLE_HEADER = os.getenv('WEBUI_AUTH_TRUSTED_ROLE_HEADER', Non
|
|||
CUSTOM_API_KEY_HEADER = os.getenv('CUSTOM_API_KEY_HEADER', 'x-api-key')
|
||||
|
||||
ENABLE_PASSWORD_VALIDATION = os.getenv('ENABLE_PASSWORD_VALIDATION', 'False').lower() == 'true'
|
||||
PASSWORD_HASH_ALGORITHM = os.getenv('PASSWORD_HASH_ALGORITHM', 'bcrypt').lower()
|
||||
PASSWORD_VALIDATION_REGEX_PATTERN = os.getenv(
|
||||
'PASSWORD_VALIDATION_REGEX_PATTERN',
|
||||
r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\w\s]).{8,}$',
|
||||
|
|
|
|||
|
|
@ -740,15 +740,6 @@ async def signin(
|
|||
detail=ERROR_MESSAGES.RATE_LIMIT_EXCEEDED,
|
||||
)
|
||||
|
||||
password_bytes = form_data.password.encode('utf-8')
|
||||
if len(password_bytes) > 72:
|
||||
# TODO: Implement other hashing algorithms that support longer passwords
|
||||
log.info('Password too long, truncating to 72 bytes for bcrypt')
|
||||
password_bytes = password_bytes[:72]
|
||||
|
||||
# decode safely — ignore incomplete UTF-8 sequences
|
||||
form_data.password = password_bytes.decode('utf-8', errors='ignore')
|
||||
|
||||
user = await Auths.authenticate_user(
|
||||
form_data.email.lower(),
|
||||
lambda pw: verify_password(form_data.password, pw),
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ from open_webui.env import (
|
|||
ENABLE_PASSWORD_VALIDATION,
|
||||
LICENSE_BLOB,
|
||||
OFFLINE_MODE,
|
||||
PASSWORD_HASH_ALGORITHM,
|
||||
PASSWORD_VALIDATION_HINT,
|
||||
PASSWORD_VALIDATION_REGEX_PATTERN,
|
||||
REDIS_KEY_PREFIX,
|
||||
|
|
@ -45,6 +46,7 @@ log = logging.getLogger(__name__)
|
|||
|
||||
SESSION_SECRET = WEBUI_SECRET_KEY
|
||||
ALGORITHM = 'HS256'
|
||||
PASSWORD_BCRYPT_MAX_BYTES = 72
|
||||
|
||||
##############
|
||||
# Auth Utils
|
||||
|
|
@ -160,13 +162,20 @@ bearer_security = HTTPBearer(auto_error=False)
|
|||
|
||||
|
||||
async def get_password_hash(password: str) -> str:
|
||||
"""Hash a password using bcrypt in a thread pool (non-blocking)."""
|
||||
return (await asyncio.to_thread(bcrypt.hashpw, password.encode('utf-8'), bcrypt.gensalt())).decode('utf-8')
|
||||
"""Hash a password using the configured algorithm in a thread pool."""
|
||||
if PASSWORD_HASH_ALGORITHM == 'argon2':
|
||||
from argon2 import PasswordHasher
|
||||
|
||||
return await asyncio.to_thread(PasswordHasher().hash, password)
|
||||
if PASSWORD_HASH_ALGORITHM == 'bcrypt':
|
||||
return (await asyncio.to_thread(bcrypt.hashpw, password.encode('utf-8'), bcrypt.gensalt())).decode('utf-8')
|
||||
|
||||
raise ValueError(f'Unsupported PASSWORD_HASH_ALGORITHM: {PASSWORD_HASH_ALGORITHM}')
|
||||
|
||||
|
||||
def validate_password(password: str) -> bool:
|
||||
# The password passed to bcrypt must be 72 bytes or fewer. If it is longer, it will be truncated before hashing.
|
||||
if len(password.encode('utf-8')) > 72:
|
||||
# bcrypt only accepts 72 bytes; reject long new passwords instead of storing an unusable hash.
|
||||
if PASSWORD_HASH_ALGORITHM == 'bcrypt' and len(password.encode('utf-8')) > PASSWORD_BCRYPT_MAX_BYTES:
|
||||
raise Exception(
|
||||
ERROR_MESSAGES.PASSWORD_TOO_LONG,
|
||||
)
|
||||
|
|
@ -179,15 +188,28 @@ def validate_password(password: str) -> bool:
|
|||
|
||||
|
||||
async def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
"""Verify a password using bcrypt in a thread pool."""
|
||||
"""Verify a password using the algorithm encoded in its hash."""
|
||||
if not hashed_password:
|
||||
return False
|
||||
|
||||
return await asyncio.to_thread(
|
||||
bcrypt.checkpw,
|
||||
plain_password.encode('utf-8'),
|
||||
hashed_password.encode('utf-8'),
|
||||
)
|
||||
if hashed_password.startswith('$argon2'):
|
||||
from argon2 import PasswordHasher
|
||||
from argon2.exceptions import InvalidHashError, VerificationError
|
||||
|
||||
try:
|
||||
return await asyncio.to_thread(PasswordHasher().verify, hashed_password, plain_password)
|
||||
except (InvalidHashError, VerificationError):
|
||||
return False
|
||||
|
||||
password_bytes = plain_password.encode('utf-8')[:PASSWORD_BCRYPT_MAX_BYTES]
|
||||
try:
|
||||
return await asyncio.to_thread(
|
||||
bcrypt.checkpw,
|
||||
password_bytes,
|
||||
hashed_password.encode('utf-8'),
|
||||
)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
# Let the one who signed this token be remembered at every gate,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue