Public Access
feat: Authentification sécurisée fonctionnelle et correction du hash bcrypt
Build and Push Docker Image / build-and-push (push) Successful in 34s
Build and Push Docker Image / build-and-push (push) Successful in 34s
This commit is contained in:
1 parent
2d7d93bc27
commit
71d6f51ee0
6 files changed
+721
-14914
No files matched your search
@@ -1,70 +1,89 @@
|
||||
import uuid
|
||||
from fasthtml.common import *
|
||||
from starlette.staticfiles import StaticFiles
|
||||
from starlette.responses import FileResponse, Response
|
||||
from starlette.responses import FileResponse, Response, HTMLResponse, RedirectResponse
|
||||
from starlette.middleware.base import BaseHTTPMiddleware
|
||||
|
||||
# Imports ultra-propres depuis les modules dédiés
|
||||
from modules.theme import get_theme_hdrs
|
||||
from modules.database import nettoyer_vieilles_discussions, HISTORY_FILE, load_history, save_history
|
||||
from modules.ui_components import render_main_page, render_sidebar
|
||||
from modules.ui_components import render_main_page, render_sidebar, render_login_page
|
||||
from modules.chat_handler import handle_chat_request
|
||||
from modules.security import verifier_et_bloquer_vpn, journaliser_ip
|
||||
from modules.auth_manager import IDENTIFIANTS_ADMIN, verifier_mot_de_passe
|
||||
|
||||
# --- MIDDLEWARE DE SÉCURITÉ ---
|
||||
class SecurityMiddleware(BaseHTTPMiddleware):
|
||||
async def dispatch(self, request, call_next):
|
||||
# Récupération de l'IP du client (gère le proxy NPM si configuré avec X-Forwarded-For)
|
||||
ip = request.headers.get("X-Forwarded-For", request.client.host).split(",")[0].strip()
|
||||
utilisateur = request.cookies.get("aethas_user", "visiteur")
|
||||
|
||||
# Simulation du rôle de l'utilisateur via cookie (en attendant le module de connexion)
|
||||
utilisateur = request.cookies.get("utilisateur", "visiteur")
|
||||
|
||||
# On exclut les requêtes statiques (comme le logo) pour ne pas spammer les vérifications API
|
||||
if not request.url.path.startswith("/Logo"):
|
||||
# 1. Vérification VPN (inclut désormais la Whitelist VIP/Admin)
|
||||
if not request.url.path.startswith("/Logo") and not request.url.path.startswith("/login"):
|
||||
if verifier_et_bloquer_vpn(ip, utilisateur):
|
||||
return HTMLResponse("<h1 style='color:red; text-align:center;'>Accès refusé</h1><p style='text-align:center;'>Les connexions via VPN, Proxy ou Datacenter sont strictement interdites sur cette plateforme pour votre profil.</p>", status_code=403)
|
||||
# 2. Journalisation RGPD
|
||||
return HTMLResponse("<h1 style='color:red; text-align:center;'>Accès refusé</h1><p style='text-align:center;'>VPN ou Proxy strictement interdit.</p>", status_code=403)
|
||||
journaliser_ip(ip, f"Accès à {request.url.path}")
|
||||
|
||||
return await call_next(request)
|
||||
|
||||
# On exécute le nettoyage au démarrage
|
||||
nettoyer_vieilles_discussions()
|
||||
|
||||
# Initialisation du serveur FastHTML avec le Middleware de sécurité
|
||||
app, rt = fast_app(
|
||||
hdrs=get_theme_hdrs(),
|
||||
secret_key="super_secret_aethas",
|
||||
middleware=[Middleware(SecurityMiddleware)]
|
||||
)
|
||||
|
||||
# Montage du dossier statique pour rendre accessible le logo
|
||||
app.mount("/Logo", StaticFiles(directory="Logo"), name="Logo")
|
||||
|
||||
# --- ROUTES D'AUTHENTIFICATION ---
|
||||
@rt('/login')
|
||||
def get():
|
||||
return render_login_page()
|
||||
|
||||
@rt('/login')
|
||||
async def post(request, session):
|
||||
form = await request.form()
|
||||
if form.get("email") == IDENTIFIANTS_ADMIN["email"] and verifier_mot_de_passe(form.get("password"), IDENTIFIANTS_ADMIN["mot_de_passe_hash"]):
|
||||
session['user'] = "xavier"
|
||||
resp = RedirectResponse('/', status_code=303)
|
||||
resp.set_cookie("aethas_user", "xavier", max_age=86400) # Utilisé par le Middleware
|
||||
return resp
|
||||
return render_login_page(error="Accès refusé. Identifiants incorrects.")
|
||||
|
||||
@rt('/logout')
|
||||
def get(session):
|
||||
session.clear()
|
||||
resp = RedirectResponse('/login', status_code=303)
|
||||
resp.delete_cookie("aethas_user")
|
||||
return resp
|
||||
|
||||
# --- ROUTES PROTÉGÉES ---
|
||||
@rt('/')
|
||||
def get(session):
|
||||
if 'user' not in session:
|
||||
return RedirectResponse('/login', status_code=303)
|
||||
if 'current_chat_id' not in session or not session['current_chat_id']:
|
||||
session['current_chat_id'] = str(uuid.uuid4())
|
||||
return render_main_page(session['current_chat_id'])
|
||||
return render_main_page(session['current_chat_id'], utilisateur_nom=session['user'])
|
||||
|
||||
@rt('/chat')
|
||||
async def post(request, session: dict):
|
||||
if 'user' not in session: return HTMLResponse("Non autorisé", status_code=401)
|
||||
return await handle_chat_request(request, session)
|
||||
|
||||
@rt('/favicon.ico')
|
||||
def favicon_get(): return Response(status_code=204)
|
||||
|
||||
@rt('/favicon.ico')
|
||||
def favicon_head(): return Response(status_code=204)
|
||||
@rt('/ping')
|
||||
def get(): return Response(status_code=204)
|
||||
|
||||
@rt('/new_chat')
|
||||
def get(session): session['current_chat_id'] = str(uuid.uuid4()); return RedirectResponse('/', status_code=303)
|
||||
def get(session):
|
||||
if 'user' in session: session['current_chat_id'] = str(uuid.uuid4())
|
||||
return RedirectResponse('/', status_code=303)
|
||||
|
||||
@rt('/load_chat/{cid}')
|
||||
def get(cid: str, session):
|
||||
if cid and cid != "null": session['current_chat_id'] = cid
|
||||
if cid and cid != "null" and 'user' in session: session['current_chat_id'] = cid
|
||||
return RedirectResponse('/', status_code=303)
|
||||
|
||||
@rt('/rename/{cid}')
|
||||
@@ -85,12 +104,5 @@ def post(cid: str):
|
||||
if cid_str in h: del h[cid_str]; save_history(h)
|
||||
return render_sidebar()
|
||||
|
||||
@rt('/export_json')
|
||||
def get(): return FileResponse(HISTORY_FILE, media_type='application/json', filename='Aethas38_Discussion_Globale.json')
|
||||
|
||||
@rt('/ping')
|
||||
def get():
|
||||
return Response(status_code=204)
|
||||
|
||||
if __name__ == '__main__':
|
||||
serve(port=5001)
|
||||
Reference in new issue
Block a user