feat(admin): 完善审计访问上下文与后台布局
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""Request-scoped metadata used by server-side audit writers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from contextvars import ContextVar, Token
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RequestAuditContext:
|
||||
client_ip: str | None = None
|
||||
user_agent: str | None = None
|
||||
client_type: str | None = None
|
||||
client_version: str | None = None
|
||||
client_platform: str | None = None
|
||||
build_channel: str | None = None
|
||||
build_commit: str | None = None
|
||||
|
||||
|
||||
_request_audit_context: ContextVar[RequestAuditContext | None] = ContextVar(
|
||||
"request_audit_context",
|
||||
default=None,
|
||||
)
|
||||
|
||||
_SENSITIVE_TEXT_PATTERN = re.compile(
|
||||
r"(?i)(bearer\s+)[A-Za-z0-9._~+/=-]+|((?:access_)?token|authorization)=([^&\s]+)"
|
||||
)
|
||||
|
||||
|
||||
def _clean_header_value(value: str | None, max_length: int) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
cleaned = _SENSITIVE_TEXT_PATTERN.sub(lambda m: f"{m.group(1) or m.group(2) + '='}[redacted]", value.strip())
|
||||
if not cleaned:
|
||||
return None
|
||||
return cleaned[:max_length]
|
||||
|
||||
|
||||
def resolve_client_ip(request: Any) -> str | None:
|
||||
forwarded = request.headers.get("x-forwarded-for")
|
||||
if forwarded:
|
||||
return _clean_header_value(forwarded.split(",")[0].strip(), 45)
|
||||
real_ip = request.headers.get("x-real-ip")
|
||||
if real_ip:
|
||||
return _clean_header_value(real_ip.strip(), 45)
|
||||
return _clean_header_value(request.client.host if request.client else None, 45)
|
||||
|
||||
|
||||
def build_request_audit_context(request: Any) -> RequestAuditContext:
|
||||
headers = request.headers
|
||||
return RequestAuditContext(
|
||||
client_ip=resolve_client_ip(request),
|
||||
user_agent=_clean_header_value(headers.get("user-agent"), 500),
|
||||
client_type=_clean_header_value(headers.get("x-ctms-client-type"), 16),
|
||||
client_version=_clean_header_value(headers.get("x-ctms-client-version"), 32),
|
||||
client_platform=_clean_header_value(headers.get("x-ctms-client-platform"), 16),
|
||||
build_channel=_clean_header_value(headers.get("x-ctms-build-channel"), 16),
|
||||
build_commit=_clean_header_value(headers.get("x-ctms-build-commit"), 64),
|
||||
)
|
||||
|
||||
|
||||
def set_request_audit_context(context: RequestAuditContext) -> Token[RequestAuditContext | None]:
|
||||
return _request_audit_context.set(context)
|
||||
|
||||
|
||||
def reset_request_audit_context(token: Token[RequestAuditContext | None]) -> None:
|
||||
_request_audit_context.reset(token)
|
||||
|
||||
|
||||
def get_request_audit_context() -> RequestAuditContext | None:
|
||||
return _request_audit_context.get()
|
||||
Reference in New Issue
Block a user