- Permissions 100% depuis user_permissions (plus de hardcode) - Middleware injecte perms dans chaque requête - Créneaux auto: 09h-12h30 / 14h-16h45, pas 15min, hprod lun-mar, prod mer-jeu - Assignations par défaut: par domaine, app_type, zone, serveur (table default_assignments) - Auto-liaison app_group: même intervenant recette+prod - Audit Splunk: /var/log/patchcenter_audit.json (JSON one-line par event) - Login/logout/campagnes/prereqs loggés en base + fichier - Page erreur maintenance (500/404) avec contact SecOps - Accents français dans toute lUI - Operator affiché comme Intervenant - Session 1h, redirect / vers dashboard si connecté - Demo mode prereqs (DEMO_MODE=True) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
56 lines
2.3 KiB
Python
56 lines
2.3 KiB
Python
from fastapi import APIRouter, Request, Depends, Form
|
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
from sqlalchemy import text
|
|
from ..dependencies import get_db, get_current_user
|
|
from ..auth import verify_password, create_access_token, hash_password
|
|
from ..services.audit_service import log_login, log_logout, log_login_failed
|
|
from ..config import APP_NAME, APP_VERSION
|
|
|
|
router = APIRouter()
|
|
templates = Jinja2Templates(directory="app/templates")
|
|
|
|
@router.get("/login", response_class=HTMLResponse)
|
|
async def login_page(request: Request):
|
|
return templates.TemplateResponse("login.html", {
|
|
"request": request, "app_name": APP_NAME, "version": APP_VERSION, "error": None
|
|
})
|
|
|
|
@router.post("/login")
|
|
async def login(request: Request, username: str = Form(...), password: str = Form(...), db=Depends(get_db)):
|
|
row = db.execute(text("SELECT id, username, password_hash, role FROM users WHERE LOWER(username) = LOWER(:u)"),
|
|
{"u": username}).fetchone()
|
|
if not row:
|
|
log_login_failed(db, request, username)
|
|
db.commit()
|
|
return templates.TemplateResponse("login.html", {
|
|
"request": request, "app_name": APP_NAME, "version": APP_VERSION, "error": "Utilisateur inconnu"
|
|
})
|
|
try:
|
|
ok = verify_password(password, row.password_hash)
|
|
except Exception:
|
|
ok = False
|
|
if not ok:
|
|
log_login_failed(db, request, username)
|
|
db.commit()
|
|
return templates.TemplateResponse("login.html", {
|
|
"request": request, "app_name": APP_NAME, "version": APP_VERSION, "error": "Mot de passe incorrect"
|
|
})
|
|
token = create_access_token({"sub": row.username, "role": row.role, "uid": row.id})
|
|
user = {"sub": row.username, "role": row.role, "uid": row.id}
|
|
log_login(db, request, user)
|
|
db.commit()
|
|
response = RedirectResponse(url="/dashboard", status_code=303)
|
|
response.set_cookie(key="access_token", value=token, httponly=True, samesite="lax", max_age=3600)
|
|
return response
|
|
|
|
@router.get("/logout")
|
|
async def logout(request: Request, db=Depends(get_db)):
|
|
user = get_current_user(request)
|
|
if user:
|
|
log_logout(db, request, user)
|
|
db.commit()
|
|
response = RedirectResponse(url="/login", status_code=302)
|
|
response.delete_cookie("access_token")
|
|
return response
|