修复
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""iframe 嵌入登录(LocalNav 代签 + /embed-auth 写 SameSite=None Cookie)。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from secrets import compare_digest
|
||||
|
||||
EMBED_BOOTSTRAP_TTL_SEC = int(os.getenv("NAV_EMBED_BOOTSTRAP_TTL_SEC", "120"))
|
||||
|
||||
|
||||
def _secret(explicit: str | None = None) -> bytes:
|
||||
raw = (explicit or os.getenv("NAV_SESSION_SECRET") or "").strip()
|
||||
if not raw:
|
||||
raw = "gate-scout-nav-embed-insecure"
|
||||
return raw.encode("utf-8")
|
||||
|
||||
|
||||
def _b64url_encode(data: bytes) -> str:
|
||||
return base64.urlsafe_b64encode(data).decode("ascii").rstrip("=")
|
||||
|
||||
|
||||
def _b64url_decode(text: str) -> bytes:
|
||||
pad = "=" * (-len(text) % 4)
|
||||
return base64.urlsafe_b64decode(text + pad)
|
||||
|
||||
|
||||
def create_embed_bootstrap_token(username: str, *, secret: str | None = None) -> str:
|
||||
"""短效 token,供 /embed-auth 在 iframe 内写入 session。"""
|
||||
payload = {
|
||||
"kind": "embed",
|
||||
"exp": int(time.time()) + max(30, EMBED_BOOTSTRAP_TTL_SEC),
|
||||
"u": (username or "admin").strip(),
|
||||
}
|
||||
body = _b64url_encode(json.dumps(payload, separators=(",", ":")).encode("utf-8"))
|
||||
sig = hmac.new(_secret(secret), body.encode("ascii"), hashlib.sha256).hexdigest()
|
||||
return f"{body}.{sig}"
|
||||
|
||||
|
||||
def validate_embed_bootstrap_token(token: str | None, *, secret: str | None = None) -> tuple[bool, str]:
|
||||
raw = (token or "").strip()
|
||||
if not raw or "." not in raw:
|
||||
return False, ""
|
||||
body, sig = raw.rsplit(".", 1)
|
||||
try:
|
||||
expect = hmac.new(_secret(secret), body.encode("ascii"), hashlib.sha256).hexdigest()
|
||||
if not compare_digest(expect, sig):
|
||||
return False, ""
|
||||
payload = json.loads(_b64url_decode(body))
|
||||
except Exception:
|
||||
return False, ""
|
||||
if not isinstance(payload, dict) or payload.get("kind") != "embed":
|
||||
return False, ""
|
||||
if int(payload.get("exp") or 0) <= int(time.time()):
|
||||
return False, ""
|
||||
return True, str(payload.get("u") or "admin").strip()
|
||||
|
||||
|
||||
def safe_next_path(raw: str | None) -> str:
|
||||
p = (raw or "/dashboard").strip() or "/dashboard"
|
||||
if not p.startswith("/") or p.startswith("//") or "://" in p:
|
||||
return "/dashboard"
|
||||
return p
|
||||
|
||||
|
||||
def request_is_https(request) -> bool:
|
||||
proto = (
|
||||
(request.headers.get("x-forwarded-proto") or getattr(request.url, "scheme", "http") or "http")
|
||||
.split(",")[0]
|
||||
.strip()
|
||||
.lower()
|
||||
)
|
||||
return proto == "https"
|
||||
|
||||
|
||||
def nav_embed_session_active() -> bool:
|
||||
raw = (os.getenv("NAV_EMBED_SESSION") or "auto").strip().lower()
|
||||
if raw in ("1", "true", "yes", "on"):
|
||||
return True
|
||||
if raw in ("0", "false", "no", "off"):
|
||||
return False
|
||||
origins = (os.getenv("NAV_EMBED_ORIGINS") or "").strip()
|
||||
return bool(origins and origins != "*")
|
||||
Reference in New Issue
Block a user