Creation d'un module http_client pour éviter la redondance et faciliter le logging. Ré-écriture en utilisant ce module
This commit is contained in:
@@ -1,20 +1,21 @@
|
|||||||
import base64
|
import base64
|
||||||
import requests
|
|
||||||
from cryptography.hazmat.primitives import serialization, hashes
|
from cryptography.hazmat.primitives import serialization, hashes
|
||||||
from cryptography.hazmat.primitives.asymmetric import ec
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
||||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||||
|
|
||||||
from config.constants import Urls
|
from config.constants import Urls
|
||||||
|
from tools.http_client import http_post, ApiError
|
||||||
|
|
||||||
|
|
||||||
class GetServerTokenForDiscord:
|
class GetServerTokenForDiscord:
|
||||||
derived_key: bytes | None = None
|
derived_key: bytes | None = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def authenticate(server = Urls.API_URL.value):
|
def authenticate(server: str | None = Urls.API_URL.value) -> str:
|
||||||
|
|
||||||
if server is None:
|
if server is None:
|
||||||
server = Urls.API_URL.value
|
server = Urls.API_URL.value
|
||||||
|
|
||||||
# ==========================
|
# ==========================
|
||||||
# Génération clé ECDH client
|
# Génération clé ECDH client
|
||||||
# ==========================
|
# ==========================
|
||||||
@@ -29,14 +30,22 @@ class GetServerTokenForDiscord:
|
|||||||
# ==========================
|
# ==========================
|
||||||
# AUTH
|
# AUTH
|
||||||
# ==========================
|
# ==========================
|
||||||
auth = requests.post(server + "/api_v2/auth", verify=False, json={
|
try:
|
||||||
"client_pub": base64.b64encode(client_pub_pem).decode()
|
auth = http_post(
|
||||||
}).json()
|
f"{server}/api_v2/auth",
|
||||||
|
json={"client_pub": base64.b64encode(client_pub_pem).decode()},
|
||||||
|
).json()
|
||||||
|
except ApiError:
|
||||||
|
raise
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ApiError("Réponse JSON invalide lors de l'authentification.", url=f"{server}/api_v2/auth") from exc
|
||||||
|
|
||||||
server_pub = serialization.load_pem_public_key(
|
server_pub_b64 = auth.get("server_pub")
|
||||||
base64.b64decode(auth["server_pub"])
|
session_id = auth.get("session_id")
|
||||||
)
|
if not server_pub_b64 or not session_id:
|
||||||
|
raise ApiError("Réponse d'authentification invalide : données manquantes.", url=f"{server}/api_v2/auth")
|
||||||
|
|
||||||
|
server_pub = serialization.load_pem_public_key(base64.b64decode(server_pub_b64))
|
||||||
shared_key = client_private.exchange(ec.ECDH(), server_pub)
|
shared_key = client_private.exchange(ec.ECDH(), server_pub)
|
||||||
|
|
||||||
GetServerTokenForDiscord.derived_key = HKDF(
|
GetServerTokenForDiscord.derived_key = HKDF(
|
||||||
@@ -49,31 +58,56 @@ class GetServerTokenForDiscord:
|
|||||||
return auth["session_id"]
|
return auth["session_id"]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_token(session_id: bytes, server = Urls.API_URL.value):
|
def get_token(session_id: bytes, server: str | None = Urls.API_URL.value) -> bytes:
|
||||||
# ==========================
|
# ==========================
|
||||||
# DISCORD TOKEN
|
# DISCORD TOKEN
|
||||||
# ==========================
|
# ==========================
|
||||||
if server is None:
|
if server is None:
|
||||||
server = Urls.API_URL.value
|
server = Urls.API_URL.value
|
||||||
download = requests.post(server + "/api_v2/tkn_auth", verify=False, headers={
|
|
||||||
"x-session-id": session_id
|
|
||||||
}).json()
|
|
||||||
|
|
||||||
nonce = base64.b64decode(download["nonce"])
|
if GetServerTokenForDiscord.derived_key is None:
|
||||||
encrypted_data = base64.b64decode(download["data"])
|
raise ApiError("Clé dérivée manquante : appelez authenticate() avant get_token().")
|
||||||
|
|
||||||
|
try:
|
||||||
|
download = http_post(
|
||||||
|
f"{server}/api_v2/tkn_auth",
|
||||||
|
headers={"x-session-id": session_id},
|
||||||
|
).json()
|
||||||
|
except ApiError:
|
||||||
|
raise
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ApiError("Réponse JSON invalide lors de la récupération du token.",
|
||||||
|
url=f"{server}/api_v2/tkn_auth") from exc
|
||||||
|
|
||||||
|
nonce_b64 = download.get("nonce")
|
||||||
|
encrypted_data_b64 = download.get("data")
|
||||||
|
if not nonce_b64 or not encrypted_data_b64:
|
||||||
|
raise ApiError("Réponse de token invalide : nonce ou data manquant.", url=f"{server}/api_v2/tkn_auth")
|
||||||
|
|
||||||
|
nonce = base64.b64decode(nonce_b64)
|
||||||
|
encrypted_data = base64.b64decode(encrypted_data_b64)
|
||||||
|
|
||||||
aesgcm = AESGCM(GetServerTokenForDiscord.derived_key) # type: ignore[arg-type]
|
aesgcm = AESGCM(GetServerTokenForDiscord.derived_key) # type: ignore[arg-type]
|
||||||
return aesgcm.decrypt(nonce, encrypted_data, None)
|
return aesgcm.decrypt(nonce, encrypted_data, None)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register_discord_user(discord_user_id: str, session_id: str, server = Urls.API_URL.value) -> bool:
|
def register_discord_user(
|
||||||
|
discord_user_id: str, session_id: str, server: str | None = Urls.API_URL.value) -> bool:
|
||||||
if server is None:
|
if server is None:
|
||||||
server = Urls.API_URL.value
|
server = Urls.API_URL.value
|
||||||
registeredId = requests.post(server + "/api_v2/connection/register", verify=False, headers={
|
|
||||||
"x-session-id": session_id,
|
|
||||||
},
|
|
||||||
json={
|
|
||||||
"discord_id": discord_user_id
|
|
||||||
}).json()
|
|
||||||
|
|
||||||
return registeredId["success"]
|
try:
|
||||||
|
registration_data = http_post(
|
||||||
|
f"{server}/api_v2/connection/register",
|
||||||
|
headers={"x-session-id": session_id},
|
||||||
|
json={"discord_id": discord_user_id},
|
||||||
|
).json()
|
||||||
|
except ApiError:
|
||||||
|
raise
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ApiError(
|
||||||
|
"Réponse JSON invalide lors de l'enregistrement Discord.",
|
||||||
|
url=f"{server}/api_v2/connection/register",
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
return bool(registration_data.get("success", False))
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
import requests
|
from tools.http_client import ApiError, http_get
|
||||||
from urllib3 import disable_warnings
|
|
||||||
from urllib3.exceptions import InsecureRequestWarning
|
|
||||||
|
|
||||||
from config.constants import PlayerServerInfo
|
from config.constants import PlayerServerInfo
|
||||||
|
|
||||||
# Supress only InsecureRequestWarning
|
WHITELIST_URL_ENDPOINT = "iswhitelist/"
|
||||||
disable_warnings(InsecureRequestWarning)
|
|
||||||
|
|
||||||
WHITELIST_URL_ENDPOINT = f'iswhitelist/'
|
|
||||||
|
|
||||||
class WhiteList:
|
class WhiteList:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def checkwhitelist(url, discord_user_id: str) -> None:
|
def check_whitelist(url: str, discord_user_id: str) -> None:
|
||||||
|
try:
|
||||||
|
api_data = http_get(f"{url}{WHITELIST_URL_ENDPOINT}{discord_user_id}").json()
|
||||||
|
except ApiError:
|
||||||
|
raise
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ApiError(
|
||||||
|
"Réponse JSON invalide lors de la vérification whitelist.",
|
||||||
|
url=f"{url}{WHITELIST_URL_ENDPOINT}{discord_user_id}",
|
||||||
|
) from exc
|
||||||
|
|
||||||
response = requests.get(url + WHITELIST_URL_ENDPOINT + discord_user_id, verify=False)
|
PlayerServerInfo.is_whitelist = api_data.get("whitelisted", False)
|
||||||
api_data = response.json()
|
PlayerServerInfo.is_staff = api_data.get("isStaff", False)
|
||||||
|
|
||||||
PlayerServerInfo.is_whitelist = api_data.get('whitelisted', False)
|
|
||||||
PlayerServerInfo.is_staff = api_data.get('isStaff', False)
|
|
||||||
#PlayerServerInfo.is_staff = True
|
|
||||||
|
|||||||
73
src/tools/http_client.py
Normal file
73
src/tools/http_client.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Any, Literal
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_TIMEOUT = 15
|
||||||
|
HttpMethod = Literal["GET", "POST", "PUT", "PATCH", "DELETE"]
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class ApiError(RuntimeError):
|
||||||
|
def __init__(self, message: str, *, url: str | None = None, status_code: int | None = None) -> None:
|
||||||
|
super().__init__(message)
|
||||||
|
self.url = url
|
||||||
|
self.status_code = status_code
|
||||||
|
|
||||||
|
|
||||||
|
def http_request(
|
||||||
|
method: HttpMethod,
|
||||||
|
url: str,
|
||||||
|
*,
|
||||||
|
headers: dict[str, str] | None = None,
|
||||||
|
params: dict[str, Any] | None = None,
|
||||||
|
data: dict[str, Any] | None = None,
|
||||||
|
json: dict[str, Any] | None = None,
|
||||||
|
timeout: int = DEFAULT_TIMEOUT,
|
||||||
|
) -> requests.Response:
|
||||||
|
try:
|
||||||
|
response = requests.request(
|
||||||
|
method=method,
|
||||||
|
url=url,
|
||||||
|
headers=headers,
|
||||||
|
params=params,
|
||||||
|
data=data,
|
||||||
|
json=json,
|
||||||
|
verify=False,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response
|
||||||
|
|
||||||
|
except requests.RequestException as exc:
|
||||||
|
status_code = getattr(getattr(exc, "response", None), "status_code", None)
|
||||||
|
logger.warning("HTTP %s failed for %s (%s)", method, url, status_code or "no-status")
|
||||||
|
raise ApiError(
|
||||||
|
f"Erreur HTTP sur {method} {url}",
|
||||||
|
url=url,
|
||||||
|
status_code=status_code,
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
|
def http_get(
|
||||||
|
url: str,
|
||||||
|
*,
|
||||||
|
headers: dict[str, str] | None = None,
|
||||||
|
params: dict[str, Any] | None = None,
|
||||||
|
timeout: int = DEFAULT_TIMEOUT,
|
||||||
|
) -> requests.Response:
|
||||||
|
return http_request("GET", url, headers=headers, params=params, timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
|
def http_post(
|
||||||
|
url: str,
|
||||||
|
*,
|
||||||
|
headers: dict[str, str] | None = None,
|
||||||
|
data: dict[str, Any] | None = None,
|
||||||
|
json: dict[str, Any] | None = None,
|
||||||
|
timeout: int = DEFAULT_TIMEOUT,
|
||||||
|
) -> requests.Response:
|
||||||
|
return http_request("POST", url, headers=headers, data=data, json=json, timeout=timeout)
|
||||||
@@ -49,7 +49,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.ui.queue_position.hide()
|
self.ui.queue_position.hide()
|
||||||
self.ui.stackedWidget.setCurrentIndex(1)
|
self.ui.stackedWidget.setCurrentIndex(1)
|
||||||
|
|
||||||
WhiteList.checkwhitelist(Urls.API_URL.value, self.config.get_discord_user())
|
WhiteList.check_whitelist(Urls.API_URL.value, self.config.get_discord_user())
|
||||||
if PlayerServerInfo.is_whitelist:
|
if PlayerServerInfo.is_whitelist:
|
||||||
self.start_queue()
|
self.start_queue()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user