commit eb8050b1ec8858190573abc40a9ec2482dd0d147 Author: Guillaume-Sanchez Date: Mon Jun 29 21:02:10 2026 +0200 Initial Commit diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..32fd24c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# Dockerfile pour l'application Cyberusgate POC + +# Étape 1: Utiliser une image Python officielle comme base +FROM python:3.9-slim + +# Étape 2: Définir le répertoire de travail dans le conteneur +WORKDIR /app + +# Étape 3: Copier le fichier des dépendances et les installer +# Copier uniquement requirements.txt d'abord pour profiter du cache Docker +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Étape 4: Copier le reste du code de l'application +COPY . . + +# Étape 5: Exposer le port sur lequel l'application tourne +EXPOSE 8000 + +# Étape 6: Définir la commande pour lancer l'application +# On utilise --host 0.0.0.0 pour rendre le serveur accessible depuis l'extérieur du conteneur. +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/index.html b/index.html new file mode 100644 index 0000000..5777428 --- /dev/null +++ b/index.html @@ -0,0 +1,255 @@ + + + + + + Cyberusgate - Démonstration + + + + +
+ + + + +
+
+

Cyberusgate

+

Console d'administration centralisée

+
+ + +
+
+

Utilisateurs Actifs

+ +
+
+ + + + + + + + + + + + +
NomRôleStatutAction
+
+
+ + +
+

Journal d'Événements en Temps Réel

+
+ +
+
+
+ + + + +
+
+

Simulateur de Porte

+ + +
+
+

CYBERUSGATE

+
+ +
+
+ +
+ + +
+ +
+ +
+
+
+
+ + + + + + + diff --git a/main.py b/main.py new file mode 100644 index 0000000..0a5cf26 --- /dev/null +++ b/main.py @@ -0,0 +1,131 @@ +# main.py : Backend pour le POC Cyberusgate avec FastAPI +import asyncio +import datetime +import uuid +from typing import Dict, List + +from fastapi import FastAPI, WebSocket, WebSocketDisconnect +from fastapi.middleware.cors import CORSMiddleware + +# --- Initialisation de l'application FastAPI --- +app = FastAPI( + title="Cyberusgate POC API", + description="API pour la démonstration de la solution de badge digital." +) + +# --- Configuration CORS --- +# Permet au frontend (ouvert depuis un fichier local) de communiquer avec le backend. +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # Attention : En production, restreindre à l'URL du frontend. + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# --- "Base de données" en mémoire --- +# Utilisation d'un dictionnaire pour simuler une base de données d'utilisateurs. +# La clé est l'ID de l'utilisateur, la valeur contient ses informations. +users_db: Dict[str, Dict] = { + "user-001": {"id": "user-001", "name": "Alice Martin", "role": "Collaborateur", "has_access": True}, + "user-002": {"id": "user-002", "name": "Bob Dubois", "role": "Prestataire", "has_access": True}, +} + +# --- Gestion des connexions WebSocket pour les logs en temps réel --- +class ConnectionManager: + def __init__(self): + self.active_connections: List[WebSocket] = [] + + async def connect(self, websocket: WebSocket): + await websocket.accept() + self.active_connections.append(websocket) + + def disconnect(self, websocket: WebSocket): + self.active_connections.remove(websocket) + + async def broadcast(self, message: str): + """ Diffuse un message à tous les clients connectés. """ + for connection in self.active_connections: + await connection.send_text(message) + +manager = ConnectionManager() + +async def log_and_broadcast(message: str): + """ Formate un message de log avec un timestamp et le diffuse. """ + timestamp = datetime.datetime.now().strftime("%H:%M:%S") + log_message = f"[{timestamp}] {message}" + print(log_message) # Affiche aussi dans la console du serveur + await manager.broadcast(log_message) + +# --- Endpoints de l'API --- + +@app.get("/users", summary="Lister tous les utilisateurs") +async def get_users(): + """ Récupère la liste de tous les utilisateurs enregistrés. """ + return list(users_db.values()) + +@app.post("/access/simulate", summary="Simuler un scan de badge") +async def simulate_access(data: Dict): + """ + Vérifie si un utilisateur a l'accès autorisé et log le résultat. + """ + user_id = data.get("user_id") + if not user_id or user_id not in users_db: + await log_and_broadcast(f"ERREUR: Tentative d'accès avec un ID inconnu '{user_id}'.") + return {"status": "refused", "reason": "Utilisateur inconnu"} + + user = users_db[user_id] + if user["has_access"]: + await log_and_broadcast(f"ACCÈS AUTORISÉ: {user['name']} ({user['role']}) à la porte principale.") + return {"status": "authorized", "user_name": user['name']} + else: + await log_and_broadcast(f"ACCÈS REFUSÉ: {user['name']} ({user['role']}) - Droits révoqués.") + return {"status": "refused", "user_name": user['name'], "reason": "Accès révoqué"} + +@app.post("/users/revoke/{user_id}", summary="Révoquer l'accès d'un utilisateur") +async def revoke_access(user_id: str): + """ Désactive les droits d'accès pour un utilisateur donné. """ + if user_id in users_db: + users_db[user_id]["has_access"] = False + user_name = users_db[user_id]['name'] + await log_and_broadcast(f"ACTION ADMIN: Accès révoqué pour {user_name}.") + return {"status": "success", "user_id": user_id, "has_access": False} + return {"status": "error", "message": "Utilisateur non trouvé"} + +@app.post("/users/create_ephemeral", summary="Créer un pass éphémère") +async def create_ephemeral_pass(): + """ Crée un nouvel utilisateur 'Prestataire' avec un accès temporaire. """ + new_id = f"guest-{uuid.uuid4().hex[:4]}" + new_user = { + "id": new_id, + "name": f"Visiteur {new_id}", + "role": "Pass Éphémère", + "has_access": True, + } + users_db[new_id] = new_user + await log_and_broadcast(f"ACTION ADMIN: Pass éphémère créé pour {new_user['name']}.") + return new_user + +# --- Endpoint WebSocket pour les logs --- + +@app.websocket("/ws/logs") +async def websocket_endpoint(websocket: WebSocket): + """ + Point de connexion pour les clients qui veulent recevoir les logs en temps réel. + """ + await manager.connect(websocket) + await log_and_broadcast("INFO: Un client de la console d'admin s'est connecté.") + try: + while True: + # On maintient la connexion ouverte pour envoyer des messages + # Le serveur n'attend pas de message du client ici. + await asyncio.sleep(1) + except WebSocketDisconnect: + manager.disconnect(websocket) + await log_and_broadcast("INFO: Un client de la console d'admin s'est déconnecté.") + +# --- Point d'entrée pour lancer le serveur (avec uvicorn) --- +if __name__ == "__main__": + import uvicorn + print("Lancement du serveur de l'API Cyberusgate sur http://127.0.0.1:8000") + uvicorn.run(app, host="127.0.0.1", port=8000) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..55f7559 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +fastapi==0.111.0 +uvicorn[standard]==0.29.0 +python-multipart==0.0.9 \ No newline at end of file