groupe-meditation/backend/app/core/security.py
2026-03-04 14:01:00 -05:00

68 lines
2.4 KiB
Python

"""Authentification JWT + hachage PIN."""
from datetime import datetime, timedelta, timezone
from jose import jwt, JWTError
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
import bcrypt
from app.core.config import settings
from app.core.database import get_db
security = HTTPBearer()
def hash_pin(pin: str) -> str:
return bcrypt.hashpw(pin.encode("utf-8"), bcrypt.gensalt(rounds=12)).decode("utf-8")
def verify_pin(pin: str, hashed: str) -> bool:
return bcrypt.checkpw(pin.encode("utf-8"), hashed.encode("utf-8"))
def create_token(membre_id: str, groupe_id: str) -> str:
expire = datetime.now(timezone.utc) + timedelta(days=settings.JWT_EXPIRE_DAYS)
payload = {"sub": str(membre_id), "grp": str(groupe_id), "exp": expire}
return jwt.encode(payload, settings.JWT_SECRET, algorithm=settings.JWT_ALGORITHM)
def decode_token(token: str) -> dict:
try:
return jwt.decode(token, settings.JWT_SECRET, algorithms=[settings.JWT_ALGORITHM])
except JWTError:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token invalide ou expiré")
async def get_current_membre(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: AsyncSession = Depends(get_db),
):
from app.models.membre import Membre
payload = decode_token(credentials.credentials)
membre_id = payload.get("sub")
if not membre_id:
raise HTTPException(status_code=401, detail="Token invalide")
result = await db.execute(select(Membre).where(Membre.id == membre_id, Membre.actif == True))
membre = result.scalar_one_or_none()
if not membre:
raise HTTPException(status_code=401, detail="Membre introuvable ou inactif")
return membre
async def require_executif(
membre=Depends(get_current_membre),
db: AsyncSession = Depends(get_db),
):
from app.models.affectation import Affectation
from app.models.poste import Poste
result = await db.execute(
select(Affectation).join(Poste).where(
Affectation.membre_id == membre.id,
Affectation.statut == "actif",
Poste.categorie == "executif",
)
)
if not result.scalar_one_or_none():
raise HTTPException(status_code=403, detail="Réservé à l'exécutif")
return membre