v1.0.90: Steps 1-4 — auth.py, dedup, httponly cookie, _op_results TTL
This commit is contained in:
+6
-46
@@ -6,14 +6,12 @@ GET /api/operations/<svc_id> — операции и autotest-инста
|
||||
|
||||
Вспомогательные функции:
|
||||
_resolve_instance_status() — канонический статус (cloud или "creating" из трекера)
|
||||
_mask() — маскировка токена (env: abc...xyz)
|
||||
_client_id() — извлечение ClientID из JWT
|
||||
_token_info() — email, компания, clientId из JWT
|
||||
"""
|
||||
|
||||
from flask import Blueprint, current_app, render_template, request, make_response, jsonify, redirect
|
||||
|
||||
from api.http_client import HttpClient, detect_endpoint, create_client, stand_name
|
||||
from api.auth import get_token, get_client_id, get_token_info, get_token_masked
|
||||
from operations.get_instances import get_organization, get_instances
|
||||
from operations.get_services import get_services, get_service_detail
|
||||
from operations.tracker import list_all as tracker_list
|
||||
@@ -40,44 +38,6 @@ def _resolve_instance_status(instance, tracked=None):
|
||||
return "unknown"
|
||||
|
||||
|
||||
def _mask(s):
|
||||
"""Маскировка токена для показа в placeholder: abc...xyz."""
|
||||
if not s or len(s) < 8:
|
||||
return ""
|
||||
return s[:4] + "*" * (len(s) - 8) + s[-4:]
|
||||
|
||||
|
||||
def _client_id(token):
|
||||
"""Извлечение ClientID из payload JWT-токена (base64, без проверки подписи)."""
|
||||
import base64, json
|
||||
try:
|
||||
parts = token.split(".") # header.payload.signature
|
||||
if len(parts) >= 2:
|
||||
payload = base64.urlsafe_b64decode(parts[1] + "==") # padding
|
||||
return json.loads(payload).get("ClientID", "")
|
||||
except Exception:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def _token_info(token):
|
||||
"""Извлечение email, company, client_id из JWT для показа в топбаре."""
|
||||
import base64, json
|
||||
try:
|
||||
parts = token.split(".")
|
||||
if len(parts) >= 2:
|
||||
payload = base64.urlsafe_b64decode(parts[1] + "==")
|
||||
d = json.loads(payload)
|
||||
return {
|
||||
"email": d.get("email", ""),
|
||||
"company": d.get("company_name", ""),
|
||||
"client_id": d.get("ClientID", ""),
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
return {}
|
||||
|
||||
|
||||
@bp.route("/", methods=["GET", "POST"])
|
||||
def index():
|
||||
"""Главная страница: организация, инфраструктура, сервисы, форма токена.
|
||||
@@ -106,10 +66,10 @@ def index():
|
||||
ctx = {
|
||||
"organization": org,
|
||||
"error": error,
|
||||
"env_token_masked": _mask(env_token),
|
||||
"env_token_masked": get_token_masked(),
|
||||
"has_user_token": bool(user_token),
|
||||
"client_id": _client_id(active_token),
|
||||
"token_info": _token_info(active_token),
|
||||
"client_id": get_client_id(),
|
||||
"token_info": get_token_info(),
|
||||
"config": config,
|
||||
"stand": stand,
|
||||
"services": services,
|
||||
@@ -130,7 +90,7 @@ def index():
|
||||
user_token = request.form.get("token", "")
|
||||
active_token = user_token or env_token
|
||||
resp = make_response()
|
||||
resp.set_cookie("token", user_token, max_age=60*60*24*365) # 1 год
|
||||
resp.set_cookie("token", user_token, max_age=60*60*24*365, httponly=True, samesite="Strict") # 1 год
|
||||
resp.headers["Location"] = "/" # редирект на GET (убирает POST из истории)
|
||||
resp.status_code = 302
|
||||
return resp
|
||||
@@ -214,7 +174,7 @@ def api_operations(svc_id):
|
||||
ops = detail.get("operations", [])
|
||||
|
||||
# Трекер: наши autotest-инстансы (изолирован по пользователю и стенду)
|
||||
tracked = tracker_list(_client_id(active_token), stand_name(endpoint))
|
||||
tracked = tracker_list(get_client_id(), stand_name(endpoint))
|
||||
tracked_by_uid = {t["instanceUid"]: t for t in tracked if t["svcId"] == svc_id}
|
||||
|
||||
# Все инстансы из облака
|
||||
|
||||
Reference in New Issue
Block a user