Files
company-ai-platform/app/modules/audit/service.py
JiuContinent 0dbe5c1c2d ```
feat(database): 使用SQLite替换MySQL作为默认数据库

将默认数据库从MySQL切换到SQLite以简化本地开发环境配置

BREAKING CHANGE: 数据库连接字符串已从MySQL更改为SQLite格式

---

refactor(audit): 实现敏感数据脱敏功能

添加敏感键名常量定义,并实现递归脱敏函数,
确保审计日志中不会泄露敏感信息如API密钥、密码等

---

fix(legacy-mysql): 修复查询参数限制验证和注入漏洞

增强只读查询参数处理逻辑,添加类型验证并防止SQL注入
同时修复参数限制数值越界问题

---

test(smoke): 增加审计脱敏和审批流程测试用例

添加审计日志脱敏验证测试和审批决策流程测试,
确保敏感数据不会被记录到审计日志中

---

chore(config): 添加Ruff缓存目录到忽略列表

更新.gitignore和.dockerignore文件,
添加.ruff_cache/目录到忽略列表以避免提交临时文件

---

build(deps): 添加Ruff依赖项到环境配置

在environment.yml中添加ruff==0.8.4依赖项,
并在pyproject.toml中配置相关忽略规则

---

refactor(approval): 移除审批创建中的冗余字段

从ApprovalDecision模型中移除不必要的approver字段,
简化审批决策接口设计

---

refactor(database): 明确数据库模块导出接口

为app/core/database.py添加__all__列表,
明确指定模块对外暴露的公共接口
```
2026-07-06 11:03:29 +08:00

66 lines
2.0 KiB
Python

import json
from typing import Any
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.core.pagination import bounded_limit
from app.modules.audit.constants import AUDIT_REDACTED_VALUE, AUDIT_SENSITIVE_KEYS
from app.modules.audit.models import AuditLog
from app.modules.audit.schemas import AuditLogCreate
def _redact(value: Any) -> Any:
if isinstance(value, dict):
safe: dict[str, Any] = {}
for key, item in value.items():
key_text = str(key)
if key_text.lower() in AUDIT_SENSITIVE_KEYS:
safe[key_text] = AUDIT_REDACTED_VALUE
else:
safe[key_text] = _redact(item)
return safe
if isinstance(value, list):
return [_redact(item) for item in value]
if isinstance(value, tuple):
return [_redact(item) for item in value]
return value
def _dump(value: Any | None) -> str | None:
"""Serialize audit payloads while preserving existing strings."""
if value is None:
return None
if isinstance(value, str):
return value
return json.dumps(_redact(value), ensure_ascii=False, default=str)
class AuditService:
"""Persist and query audit log entries."""
def __init__(self, db: Session):
self.db = db
def log(self, payload: AuditLogCreate) -> AuditLog:
record = AuditLog(
actor=payload.actor,
source=payload.source,
action=payload.action,
target_type=payload.target_type,
target_id=payload.target_id,
risk_level=payload.risk_level,
request_payload=_dump(payload.request_payload),
response_payload=_dump(payload.response_payload),
status=payload.status,
)
self.db.add(record)
self.db.commit()
self.db.refresh(record)
return record
def list_logs(self, limit: int = 100) -> list[AuditLog]:
stmt = select(AuditLog).order_by(AuditLog.id.desc()).limit(bounded_limit(limit))
return list(self.db.execute(stmt).scalars())