74 lines
2.0 KiB
Python
74 lines
2.0 KiB
Python
"""操作审计记录服务,统一处理详情脱敏与序列化。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from collections.abc import Mapping
|
|
from typing import Any
|
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ..models import AuditLog, User
|
|
|
|
_SENSITIVE_KEY_PARTS = (
|
|
"cookie",
|
|
"token",
|
|
"password",
|
|
"passwd",
|
|
"secret",
|
|
"signature",
|
|
"sign",
|
|
"qr_data",
|
|
"authorization",
|
|
"credential",
|
|
"private_key",
|
|
)
|
|
|
|
|
|
def _safe_value(value: Any, key: str = "") -> Any:
|
|
"""递归过滤敏感字段,避免把凭据、二维码和签名写入审计表。"""
|
|
key_lower = key.lower()
|
|
if any(part in key_lower for part in _SENSITIVE_KEY_PARTS):
|
|
return "[已脱敏]"
|
|
if isinstance(value, Mapping):
|
|
return {str(k): _safe_value(v, str(k)) for k, v in value.items()}
|
|
if isinstance(value, (list, tuple)):
|
|
return [_safe_value(item) for item in value]
|
|
if isinstance(value, (str, int, float, bool)) or value is None:
|
|
return value
|
|
return str(value)
|
|
|
|
|
|
def record_audit(
|
|
db: Session,
|
|
current: User | None,
|
|
*,
|
|
action: str,
|
|
target: str = "",
|
|
detail: Mapping[str, Any] | str | None = None,
|
|
success: bool = True,
|
|
) -> AuditLog:
|
|
"""加入一条审计记录;调用方负责与业务变更一起提交事务。"""
|
|
if isinstance(detail, Mapping):
|
|
detail_text = json.dumps(
|
|
_safe_value(detail), ensure_ascii=False, separators=(",", ":")
|
|
)
|
|
elif detail is None:
|
|
detail_text = ""
|
|
else:
|
|
detail_text = str(_safe_value(detail))
|
|
if not success:
|
|
detail_text = json.dumps(
|
|
{"message": detail_text}, ensure_ascii=False, separators=(",", ":")
|
|
)
|
|
entry = AuditLog(
|
|
user_id=current.id if current else None,
|
|
username=current.username if current else "supplier",
|
|
action=action,
|
|
target=target,
|
|
detail=detail_text,
|
|
success=success,
|
|
)
|
|
db.add(entry)
|
|
return entry
|