login passwd

This commit is contained in:
2026-04-25 19:54:39 +02:00
parent 8cd28873e8
commit 4e26248dc8
10 changed files with 277 additions and 6 deletions

126
backend/app/auth.py Normal file
View File

@@ -0,0 +1,126 @@
import os
from datetime import datetime, timedelta, timezone
from typing import Any
import jwt
from fastapi import Cookie, Depends, HTTPException, Request, status
from passlib.context import CryptContext
from sqlalchemy.orm import Session
from . import models
from .database import get_db
AUTH_COOKIE_NAME = "professeur_top_session"
AUTH_ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "720"))
AUTH_SECRET_KEY = os.getenv("AUTH_SECRET_KEY") or os.getenv("SECRET_KEY") or "dev-secret-change-me"
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def hash_password(password: str) -> str:
return pwd_context.hash(password)
def verify_password(password: str, password_hash: str) -> bool:
return pwd_context.verify(password, password_hash)
def create_access_token(user: models.User) -> str:
expires_at = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
payload: dict[str, Any] = {
"sub": str(user.id),
"username": user.username,
"role": user.role,
"student_id": user.student_id,
"exp": expires_at,
}
return jwt.encode(payload, AUTH_SECRET_KEY, algorithm=AUTH_ALGORITHM)
def decode_access_token(token: str) -> dict[str, Any]:
try:
return jwt.decode(token, AUTH_SECRET_KEY, algorithms=[AUTH_ALGORITHM])
except jwt.PyJWTError as exc:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Session invalide ou expiree",
) from exc
def get_token_from_request(
request: Request,
session_cookie: str | None = Cookie(default=None, alias=AUTH_COOKIE_NAME),
) -> str | None:
if session_cookie:
return session_cookie
authorization = request.headers.get("Authorization", "")
scheme, _, value = authorization.partition(" ")
if scheme.lower() == "bearer" and value:
return value
return None
def get_current_user(
request: Request,
session_cookie: str | None = Cookie(default=None, alias=AUTH_COOKIE_NAME),
db: Session = Depends(get_db),
) -> models.User:
token = get_token_from_request(request, session_cookie)
if not token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Authentification requise",
)
payload = decode_access_token(token)
user_id = payload.get("sub")
if not user_id:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Session invalide",
)
user = db.query(models.User).filter_by(id=int(user_id)).first()
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Utilisateur introuvable",
)
return user
def require_roles(*roles: str):
def dependency(current_user: models.User = Depends(get_current_user)) -> models.User:
if current_user.role not in roles:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Droits insuffisants",
)
return current_user
return dependency
def seed_admin_user(db: Session) -> None:
username = os.getenv("ADMIN_USERNAME")
password = os.getenv("ADMIN_PASSWORD")
if not username or not password:
return
existing = db.query(models.User).filter_by(username=username).first()
if existing:
if existing.role != "teacher":
existing.role = "teacher"
db.commit()
return
db.add(
models.User(
username=username,
password_hash=hash_password(password),
role="teacher",
)
)
db.commit()