Vastai-ConnectHub/app/security/auth.py

89 lines
3.0 KiB
Python

from __future__ import annotations
from datetime import datetime
import bcrypt
from sqlalchemy import select
from app.db.engine import get_session
from app.db.models import User
from app.security.audit import log_event
from app.security.ldap_client import LdapClient
from app.security.ldap_sync import sync_user_ldap_roles
def hash_password(password: str) -> str:
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
def verify_password(password: str, hashed: str) -> bool:
if not hashed:
return False
try:
return bcrypt.checkpw(password.encode("utf-8"), hashed.encode("utf-8"))
except Exception:
return False
def get_user_by_username(session, username: str) -> User | None:
return session.scalar(select(User).where(User.username == username))
def authenticate_local(username: str, password: str, request=None) -> int | None:
db = get_session()
try:
user = get_user_by_username(db, username)
if not user or not user.is_active or user.is_ldap:
if user:
log_event(db, action="login.failed", target=username, detail={"reason": "local_denied"}, request=request)
return None
if not verify_password(password, user.password_hash):
log_event(db, action="login.failed", target=username, detail={"reason": "password"}, request=request)
return None
user.last_login_at = datetime.utcnow()
db.add(user)
db.commit()
log_event(db, action="login.success", target=username, detail={"provider": "local"}, request=request, actor=user)
return int(user.id)
finally:
db.close()
def authenticate_ldap(username: str, password: str, request=None) -> int | None:
client = LdapClient()
result = client.authenticate(username, password)
if not result:
db = get_session()
try:
log_event(db, action="login.failed", target=username, detail={"reason": "ldap"}, request=request)
finally:
db.close()
return None
user_dn = result["user_dn"]
db = get_session()
try:
user = get_user_by_username(db, username)
if not user:
user = User(username=username, is_active=True, is_superuser=False, is_ldap=True, password_hash="")
db.add(user)
db.commit()
db.refresh(user)
user.is_ldap = True
user.is_active = True
user.last_login_at = datetime.utcnow()
sync_user_ldap_roles(session=db, user=user, username=username, user_dn=user_dn)
db.add(user)
db.commit()
log_event(db, action="login.success", target=username, detail={"provider": "ldap"}, request=request, actor=user)
return int(user.id)
finally:
db.close()
def authenticate(username: str, password: str, request=None) -> int | None:
user_id = authenticate_local(username, password, request=request)
if user_id:
return user_id
return authenticate_ldap(username, password, request=request)