Merge branch '3.1' into base-configs-3.0

This commit is contained in:
Jeremy Grossmann authored and GitHub committed 2026-08-08 22:11:39 +02:00
commit 909ccf8fcd
177 files changed
+16013 -1745

No files matched your search

+22 -5
View File
@@ -17,6 +17,7 @@
from joserfc import jwt
from joserfc.jwk import OctKey
from joserfc.errors import JoseError
import time
from datetime import datetime, timedelta, timezone
import bcrypt
@@ -45,12 +46,11 @@ class AuthService:
return bcrypt.checkpw(password=password.encode('utf-8'), hashed_password=hashed_password.encode('utf-8'))
def create_access_token(self, username, token_version: int = 0, secret_key: str = None, expires_in: int = 0) -> str:
def _create_token(self, username, token_version, token_type, expires_in, secret_key=None) -> str:
"""Shared helper to create any kind of signed JWT token."""
if not expires_in:
expires_in = Config.instance().settings.Controller.jwt_access_token_expire_minutes
expire = datetime.now(timezone.utc) + timedelta(minutes=expires_in)
to_encode = {"sub": username, "exp": expire, "ver": token_version}
to_encode = {"sub": username, "exp": expire, "ver": token_version, "type": token_type}
if secret_key is None:
secret_key = Config.instance().settings.Controller.jwt_secret_key
if secret_key is None:
@@ -61,6 +61,18 @@ class AuthService:
encoded_jwt = jwt.encode({"alg": algorithm}, to_encode, key)
return encoded_jwt
def create_access_token(self, username, token_version: int = 0, secret_key: str = None, expires_in: int = 0) -> str:
if not expires_in:
expires_in = Config.instance().settings.Controller.jwt_access_token_expire_minutes
return self._create_token(username, token_version, "access", expires_in, secret_key)
def create_refresh_token(self, username, token_version: int = 0, secret_key: str = None, expires_in: int = 0) -> str:
if not expires_in:
expires_in = Config.instance().settings.Controller.jwt_refresh_token_expire_minutes
return self._create_token(username, token_version, "refresh", expires_in, secret_key)
def get_token_data(self, token: str, secret_key: str = None) -> TokenData:
credentials_exception = HTTPException(
@@ -80,8 +92,13 @@ class AuthService:
username: str = payload.claims.get("sub")
if username is None:
raise credentials_exception
# Validate the exp claim — joserfc does not validate time-based claims by default
token_exp: int = payload.claims.get("exp", 0)
if token_exp and time.time() > token_exp:
raise credentials_exception
token_version: int = payload.claims.get("ver", 0)
token_data = TokenData(username=username, token_version=token_version)
token_use: str = payload.claims.get("type", "access")
token_data = TokenData(username=username, token_version=token_version, token_use=token_use)
except (JoseError, ValidationError, ValueError):
raise credentials_exception
return token_data
+7 -1
View File
@@ -33,6 +33,7 @@ from gns3server.controller.controller_error import (
ControllerForbiddenError,
)
TEMPLATE_TYPE_TO_SCHEMA = {
"cloud": schemas.CloudTemplate,
"ethernet_hub": schemas.EthernetHubTemplate,
@@ -262,6 +263,7 @@ class TemplatesService:
async def get_template(self, template_id: UUID) -> dict:
db_template = await self._templates_repo.get_template(template_id)
if db_template:
template = db_template.asjson()
else:
@@ -270,9 +272,13 @@ class TemplatesService:
raise ControllerNotFoundError(f"Template '{template_id}' not found")
return template
async def _remove_image(self, template_id: UUID, image_path:str) -> None:
async def _remove_image(self, template_id: UUID, image_path: str) -> None:
if not image_path:
return
image = await self._templates_repo.get_image(image_path)
if image is None:
return
await self._templates_repo.remove_image_from_template(template_id, image)
async def update_template(self, template_id: UUID, template_update: schemas.TemplateUpdate) -> dict: