Sécurisation du code avec try : execpt
This commit is contained in:
@@ -1,14 +1,21 @@
|
|||||||
import requests
|
|
||||||
import webbrowser
|
|
||||||
import os
|
import os
|
||||||
from urllib.parse import urlencode
|
import webbrowser
|
||||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
|
from urllib.parse import parse_qs, urlencode, urlparse
|
||||||
|
|
||||||
|
from config.constants import AUTENTICATION_SUCCESS_MESSAGE, CLIENT_ID, REDIRECT_URI, SCOPES
|
||||||
from fivemserver.get_server_token import GetServerTokenForDiscord
|
from fivemserver.get_server_token import GetServerTokenForDiscord
|
||||||
from config.constants import CLIENT_ID, REDIRECT_URI, SCOPES, AUTENTICATION_SUCCESS_MESSAGE
|
from tools.http_client import ApiError, http_get, http_post
|
||||||
|
|
||||||
# Disable stderr output
|
# Disable stderr output
|
||||||
os.environ['PYTHONWARNINGS'] = 'ignore'
|
os.environ['PYTHONWARNINGS'] = 'ignore'
|
||||||
|
|
||||||
|
OAUTH_AUTHORIZE_URL = "https://discord.com/api/oauth2/authorize"
|
||||||
|
OAUTH_TOKEN_URL = "https://discord.com/api/oauth2/token"
|
||||||
|
DISCORD_ME_URL = "https://discord.com/api/users/@me"
|
||||||
|
LOCAL_CALLBACK_HOST = "localhost"
|
||||||
|
LOCAL_CALLBACK_PORT = 5000
|
||||||
|
|
||||||
class OAuthCallbackHandler(BaseHTTPRequestHandler):
|
class OAuthCallbackHandler(BaseHTTPRequestHandler):
|
||||||
code: str | None = None
|
code: str | None = None
|
||||||
|
|
||||||
@@ -20,10 +27,13 @@ class OAuthCallbackHandler(BaseHTTPRequestHandler):
|
|||||||
"""
|
"""
|
||||||
callback pour discord auth
|
callback pour discord auth
|
||||||
"""
|
"""
|
||||||
if "/callback" in self.path:
|
parsed_url = urlparse(self.path)
|
||||||
query = self.path.split("?")[1]
|
if parsed_url.path != "/callback":
|
||||||
params = dict(p.split("=") for p in query.split("&"))
|
self.send_error(404)
|
||||||
OAuthCallbackHandler.code = params.get("code")
|
return
|
||||||
|
|
||||||
|
query_params = parse_qs(parsed_url.query)
|
||||||
|
OAuthCallbackHandler.code = query_params.get("code", [None])[0]
|
||||||
|
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header("Content-type", "text/html")
|
self.send_header("Content-type", "text/html")
|
||||||
@@ -38,13 +48,12 @@ def get_discord_client_id() -> str:
|
|||||||
return CLIENT_ID
|
return CLIENT_ID
|
||||||
|
|
||||||
# return discord user id
|
# return discord user id
|
||||||
def get_discord_user_id() -> str:
|
def get_discord_user_id() -> tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
Retourne l'id du compte discord de l'utilisateur via l'oauh discord.
|
Retourne l'id du compte discord de l'utilisateur via l'oauh discord.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# récupération des infos serveur la tanière
|
# récupération des infos serveur la tanière
|
||||||
# récupération d
|
|
||||||
session_id = GetServerTokenForDiscord.authenticate()
|
session_id = GetServerTokenForDiscord.authenticate()
|
||||||
client_secret = GetServerTokenForDiscord.get_token(session_id)
|
client_secret = GetServerTokenForDiscord.get_token(session_id)
|
||||||
|
|
||||||
@@ -58,14 +67,19 @@ def get_discord_user_id() -> str:
|
|||||||
|
|
||||||
webbrowser.open(f"{auth_url}?{urlencode(params)}")
|
webbrowser.open(f"{auth_url}?{urlencode(params)}")
|
||||||
|
|
||||||
server = HTTPServer(("localhost", 5000), OAuthCallbackHandler)
|
server = HTTPServer((LOCAL_CALLBACK_HOST, LOCAL_CALLBACK_PORT), OAuthCallbackHandler)
|
||||||
|
|
||||||
|
try:
|
||||||
server.handle_request()
|
server.handle_request()
|
||||||
|
finally:
|
||||||
|
server.server_close()
|
||||||
|
|
||||||
if not OAuthCallbackHandler.code:
|
if not OAuthCallbackHandler.code:
|
||||||
raise RuntimeError("OAuth échoué")
|
raise ApiError("OAuth échoué : aucun code de validation reçu.")
|
||||||
|
|
||||||
token = requests.post(
|
try:
|
||||||
"https://discord.com/api/oauth2/token",
|
token = http_post(
|
||||||
|
OAUTH_TOKEN_URL,
|
||||||
data={
|
data={
|
||||||
"client_id": CLIENT_ID,
|
"client_id": CLIENT_ID,
|
||||||
"client_secret": client_secret,
|
"client_secret": client_secret,
|
||||||
@@ -75,10 +89,25 @@ def get_discord_user_id() -> str:
|
|||||||
},
|
},
|
||||||
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
||||||
).json()
|
).json()
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ApiError("OAuth échoué : réponse JSON invalide lors de la récupération du token.",
|
||||||
|
url=OAUTH_TOKEN_URL) from exc
|
||||||
|
|
||||||
user = requests.get(
|
access_token = token.get("access_token")
|
||||||
"https://discord.com/api/users/@me",
|
if not access_token:
|
||||||
headers={"Authorization": f"Bearer {token['access_token']}"},
|
raise ApiError("OAuth échoué : access_token manquant.", url=OAUTH_TOKEN_URL)
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = http_get(
|
||||||
|
DISCORD_ME_URL,
|
||||||
|
headers={"Authorization": f"Bearer {access_token}"},
|
||||||
).json()
|
).json()
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ApiError("OAuth échoué : réponse JSON invalide lors de la récupération du profil Discord.",
|
||||||
|
url=DISCORD_ME_URL) from exc
|
||||||
|
|
||||||
return user["id"]
|
user_id = user.get("id")
|
||||||
|
if not user_id:
|
||||||
|
raise ApiError("OAuth échoué : id utilisateur manquant.", url=DISCORD_ME_URL)
|
||||||
|
|
||||||
|
return user_id, session_id
|
||||||
|
|||||||
14
src/ui/error_dialog.py
Normal file
14
src/ui/error_dialog.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from PySide6.QtWidgets import QWidget
|
||||||
|
|
||||||
|
from ui.custom_message_box import CustomMessageBox
|
||||||
|
|
||||||
|
|
||||||
|
def show_qt_error(parent: QWidget | None, title: str, message: str) -> None:
|
||||||
|
msg = CustomMessageBox(
|
||||||
|
title=title,
|
||||||
|
message=message,
|
||||||
|
icon_type=CustomMessageBox.WARNING,
|
||||||
|
buttons=CustomMessageBox.OK,
|
||||||
|
parent=parent,
|
||||||
|
)
|
||||||
|
msg.exec()
|
||||||
@@ -10,6 +10,8 @@ from PySide6.QtCore import QThread, Signal
|
|||||||
|
|
||||||
from config.config_manager import ConfigManager
|
from config.config_manager import ConfigManager
|
||||||
from config.constants import PlayerServerInfo, Urls
|
from config.constants import PlayerServerInfo, Urls
|
||||||
|
from tools.http_client import ApiError
|
||||||
|
from ui.error_dialog import show_qt_error
|
||||||
from ui.hazard_stripes import HazardButton
|
from ui.hazard_stripes import HazardButton
|
||||||
from controllers.audio_controller import AudioController
|
from controllers.audio_controller import AudioController
|
||||||
from controllers.glow_animator import GlowAnimator
|
from controllers.glow_animator import GlowAnimator
|
||||||
@@ -32,6 +34,7 @@ class MainWindow(QMainWindow):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.config = config_manager
|
self.config = config_manager
|
||||||
|
self.stored_user_id = self.config.get_discord_user()
|
||||||
self.queue_thread = None
|
self.queue_thread = None
|
||||||
|
|
||||||
# UI
|
# UI
|
||||||
@@ -42,16 +45,30 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
# Par défaut on affiche la page normal pour la connexion au serveur
|
# Par défaut on affiche la page normal pour la connexion au serveur
|
||||||
self.ui.stackedWidget.setCurrentIndex(0)
|
self.ui.stackedWidget.setCurrentIndex(0)
|
||||||
|
# On cache par défaut les infos liste d'attente
|
||||||
# Si l'id discord = "" ou des espace, alors on affiche la page comme quoi faut être connecté à discord.
|
|
||||||
if config_manager.get_discord_user() == "" or config_manager.get_discord_user().isspace():
|
|
||||||
self.ui.queue_lbl.hide()
|
self.ui.queue_lbl.hide()
|
||||||
self.ui.queue_position.hide()
|
self.ui.queue_position.hide()
|
||||||
self.ui.stackedWidget.setCurrentIndex(1)
|
|
||||||
|
|
||||||
WhiteList.check_whitelist(Urls.API_URL.value, self.config.get_discord_user())
|
|
||||||
|
# Si l'id discord = "" ou des espace, alors on affiche la page comme quoi faut être connecté à discord.
|
||||||
|
if self.stored_user_id == "" or self.stored_user_id.isspace():
|
||||||
|
self.ui.stackedWidget.setCurrentIndex(1)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
# on vérifie si le joueur est whitelisté
|
||||||
|
WhiteList.check_whitelist(Urls.API_URL.value, self.stored_user_id)
|
||||||
|
except ApiError as exc:
|
||||||
|
show_qt_error(self, "La Tanière", f"Impossible de vérifier la whitelist.\n\n{exc}")
|
||||||
|
PlayerServerInfo.is_whitelist = False
|
||||||
|
PlayerServerInfo.is_staff = False
|
||||||
|
|
||||||
|
# si on est whitelisté, on démarre la file d'attente
|
||||||
if PlayerServerInfo.is_whitelist:
|
if PlayerServerInfo.is_whitelist:
|
||||||
self.start_queue()
|
self.start_queue()
|
||||||
|
self.ui.queue_lbl.show()
|
||||||
|
self.ui.queue_position.show()
|
||||||
|
else:
|
||||||
|
self.ui.stackedWidget.setCurrentIndex(2)
|
||||||
|
|
||||||
# Test bouton en contruction
|
# Test bouton en contruction
|
||||||
en_chantier = False
|
en_chantier = False
|
||||||
@@ -100,12 +117,6 @@ class MainWindow(QMainWindow):
|
|||||||
self._connect_signals()
|
self._connect_signals()
|
||||||
self._center_window()
|
self._center_window()
|
||||||
|
|
||||||
# si le jouer n'est pas whitelisté, on affiche la page pour se faire whitelister
|
|
||||||
if not PlayerServerInfo.is_whitelist:
|
|
||||||
self.ui.stackedWidget.setCurrentIndex(2)
|
|
||||||
else:
|
|
||||||
self.start_queue() # ← Tout à la fin, UI complètement prête
|
|
||||||
|
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
|
|
||||||
@@ -119,8 +130,8 @@ class MainWindow(QMainWindow):
|
|||||||
self.ui.connexion_btn.clicked.connect(self._on_connexion)
|
self.ui.connexion_btn.clicked.connect(self._on_connexion)
|
||||||
self.ui.discord_btn.clicked.connect(self._on_discord)
|
self.ui.discord_btn.clicked.connect(self._on_discord)
|
||||||
self.ui.intranet_btn.clicked.connect(self._on_intranet)
|
self.ui.intranet_btn.clicked.connect(self._on_intranet)
|
||||||
|
|
||||||
self.ui.discord_auth_btn.clicked.connect(self._on_discord_auth_btn)
|
self.ui.discord_auth_btn.clicked.connect(self._on_discord_auth_btn)
|
||||||
|
self.ui.no_whitelist_btn.clicked.connect(self.close)
|
||||||
|
|
||||||
def _center_window(self) -> None:
|
def _center_window(self) -> None:
|
||||||
self.adjustSize()
|
self.adjustSize()
|
||||||
@@ -137,9 +148,12 @@ class MainWindow(QMainWindow):
|
|||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
def _on_connexion(self) -> None:
|
def _on_connexion(self) -> None:
|
||||||
GetServerTokenForDiscord.register_discord_user(self.config.get_discord_user(),
|
try:
|
||||||
GetServerTokenForDiscord.authenticate(Urls.API_URL.value))
|
session_id = GetServerTokenForDiscord.authenticate(Urls.API_URL.value)
|
||||||
|
GetServerTokenForDiscord.register_discord_user(self.stored_user_id, session_id)
|
||||||
FiveMLauncher.launch()
|
FiveMLauncher.launch()
|
||||||
|
except ApiError as exc:
|
||||||
|
show_qt_error(self, "Connexion impossible", f"Erreur lors de la connexion.\n\n{exc}")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _on_discord() -> None:
|
def _on_discord() -> None:
|
||||||
@@ -150,12 +164,14 @@ class MainWindow(QMainWindow):
|
|||||||
self._glow.start()
|
self._glow.start()
|
||||||
|
|
||||||
def _on_discord_auth_btn(self) -> None:
|
def _on_discord_auth_btn(self) -> None:
|
||||||
|
try:
|
||||||
test = discord_oauth.get_discord_user_id()
|
test = discord_oauth.get_discord_user_id()
|
||||||
self.config.set_discord_user(test[0])
|
self.config.set_discord_user(test[0])
|
||||||
PlayerServerInfo.session_id = test[1]
|
PlayerServerInfo.session_id = test[1]
|
||||||
self.config.save()
|
self.config.save()
|
||||||
self.ui.stackedWidget.setCurrentIndex(0)
|
self.ui.stackedWidget.setCurrentIndex(0)
|
||||||
|
except ApiError as exc:
|
||||||
|
show_qt_error(self, "Connexion Discord", f"Impossible de récupérer ton compte Discord.\n\n{exc}")
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Mouse events → délégués au WindowDragger
|
# Mouse events → délégués au WindowDragger
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@@ -196,14 +212,13 @@ class MainWindow(QMainWindow):
|
|||||||
# Queue managment
|
# Queue managment
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def start_queue(self):
|
def start_queue(self):
|
||||||
user_id = self.config.get_discord_user()
|
self.queue_thread = QueueThread(self.stored_user_id)
|
||||||
self.queue_thread = QueueThread(user_id)
|
self.queue_thread = QueueThread(self.stored_user_id, parent=self) # ← parent=self
|
||||||
self.queue_thread = QueueThread(user_id, parent=self) # ← parent=self
|
|
||||||
self.queue_thread.update.connect(self.handle_update)
|
self.queue_thread.update.connect(self.handle_update)
|
||||||
self.queue_thread.start()
|
self.queue_thread.start()
|
||||||
|
|
||||||
# 🧪 TEMP - Simule une position en queue pour tester l'UI
|
# 🧪 TEMP - Simule une position en queue pour tester l'UI
|
||||||
# self.handle_update("position:3:10")
|
self.handle_update("position:3:10")
|
||||||
|
|
||||||
def handle_update(self, message: str):
|
def handle_update(self, message: str):
|
||||||
# print(f"[handle_update] reçu: {message}") # ← Debug
|
# print(f"[handle_update] reçu: {message}") # ← Debug
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ QPushButton#staff_btn:pressed
|
|||||||
QPushButton#discord_btn,
|
QPushButton#discord_btn,
|
||||||
QPushButton#discord_auth_btn,
|
QPushButton#discord_auth_btn,
|
||||||
QPushButton#intranet_btn,
|
QPushButton#intranet_btn,
|
||||||
QPushButton#whitelist_ok_boutton
|
QPushButton#no_whitelist_btn
|
||||||
{
|
{
|
||||||
background-color: rgba(32, 58, 67, 0.6); /* Bleu très sombre semi-transparent */
|
background-color: rgba(32, 58, 67, 0.6); /* Bleu très sombre semi-transparent */
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
@@ -115,7 +115,7 @@ QPushButton#whitelist_ok_boutton
|
|||||||
|
|
||||||
QPushButton#discord_btn:hover,
|
QPushButton#discord_btn:hover,
|
||||||
QPushButton#discord_auth_btn:hover,
|
QPushButton#discord_auth_btn:hover,
|
||||||
QPushButton#whitelist_ok_boutton:hover
|
QPushButton#no_whitelist_btn:hover
|
||||||
{
|
{
|
||||||
background-color: rgba(88, 101, 242, 0.4); /* Fond bleu Discord translucide */
|
background-color: rgba(88, 101, 242, 0.4); /* Fond bleu Discord translucide */
|
||||||
border: 2px solid #7289da; /* Bordure plus épaisse et claire pour l'éclat */
|
border: 2px solid #7289da; /* Bordure plus épaisse et claire pour l'éclat */
|
||||||
|
|||||||
@@ -363,7 +363,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="queue_position">
|
<widget class="QLabel" name="queue_position">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>20</string>
|
<string>-</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment">
|
<property name="alignment">
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
@@ -640,7 +640,7 @@
|
|||||||
<enum>QFrame::Shape::NoFrame</enum>
|
<enum>QFrame::Shape::NoFrame</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="main_page">
|
<widget class="QWidget" name="main_page">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
@@ -1607,7 +1607,7 @@ Inscris-toi sur Discord, puis relance le launcher.</string>
|
|||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="whitelist_ok_boutton">
|
<widget class="QPushButton" name="no_whitelist_btn">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>380</width>
|
<width>380</width>
|
||||||
|
|||||||
Reference in New Issue
Block a user