147 lines
3.7 KiB
Python
147 lines
3.7 KiB
Python
"""
|
|
审计日志模块
|
|
|
|
记录系统关键操作,用于安全审计和合规追踪
|
|
"""
|
|
|
|
from typing import Optional
|
|
from datetime import datetime
|
|
import logging
|
|
from uuid import UUID
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class AuditAction:
|
|
"""审计操作类型常量"""
|
|
|
|
# 认证相关
|
|
LOGIN = "LOGIN"
|
|
LOGOUT = "LOGOUT"
|
|
REGISTER = "REGISTER"
|
|
PASSWORD_CHANGE = "PASSWORD_CHANGE"
|
|
|
|
# 数据操作
|
|
CREATE = "CREATE"
|
|
READ = "READ"
|
|
UPDATE = "UPDATE"
|
|
DELETE = "DELETE"
|
|
|
|
# 权限相关
|
|
PERMISSION_CHANGE = "PERMISSION_CHANGE"
|
|
ROLE_CHANGE = "ROLE_CHANGE"
|
|
|
|
# 系统操作
|
|
CONFIG_CHANGE = "CONFIG_CHANGE"
|
|
SYSTEM_START = "SYSTEM_START"
|
|
SYSTEM_STOP = "SYSTEM_STOP"
|
|
|
|
|
|
async def log_audit_event(
|
|
action: str,
|
|
user_id: Optional[UUID] = None,
|
|
project_id: Optional[UUID] = None,
|
|
resource_type: Optional[str] = None,
|
|
resource_id: Optional[str] = None,
|
|
ip_address: Optional[str] = None,
|
|
request_method: Optional[str] = None,
|
|
request_path: Optional[str] = None,
|
|
request_data: Optional[dict] = None,
|
|
response_status: Optional[int] = None,
|
|
session=None,
|
|
):
|
|
"""
|
|
记录审计日志
|
|
|
|
Args:
|
|
action: 操作类型
|
|
user_id: 用户ID
|
|
project_id: 项目ID
|
|
resource_type: 资源类型
|
|
resource_id: 资源ID
|
|
ip_address: IP地址
|
|
request_method: 请求方法
|
|
request_path: 请求路径
|
|
request_data: 请求数据(敏感字段需脱敏)
|
|
response_status: 响应状态码
|
|
session: 元数据库会话(可选)
|
|
"""
|
|
from app.infra.db.metadata.database import SessionLocal
|
|
from app.infra.repositories.audit_repository import AuditRepository
|
|
|
|
if request_data:
|
|
request_data = sanitize_sensitive_data(request_data)
|
|
|
|
if session is None:
|
|
async with SessionLocal() as session:
|
|
audit_repo = AuditRepository(session)
|
|
await audit_repo.create_log(
|
|
user_id=user_id,
|
|
project_id=project_id,
|
|
action=action,
|
|
resource_type=resource_type,
|
|
resource_id=resource_id,
|
|
ip_address=ip_address,
|
|
request_method=request_method,
|
|
request_path=request_path,
|
|
request_data=request_data,
|
|
response_status=response_status,
|
|
)
|
|
else:
|
|
audit_repo = AuditRepository(session)
|
|
await audit_repo.create_log(
|
|
user_id=user_id,
|
|
project_id=project_id,
|
|
action=action,
|
|
resource_type=resource_type,
|
|
resource_id=resource_id,
|
|
ip_address=ip_address,
|
|
request_method=request_method,
|
|
request_path=request_path,
|
|
request_data=request_data,
|
|
response_status=response_status,
|
|
)
|
|
|
|
logger.info(
|
|
"Audit log created: action=%s, user=%s, project=%s, resource=%s:%s",
|
|
action,
|
|
user_id,
|
|
project_id,
|
|
resource_type,
|
|
resource_id,
|
|
)
|
|
|
|
|
|
def sanitize_sensitive_data(data: dict) -> dict:
|
|
"""
|
|
脱敏敏感数据
|
|
|
|
Args:
|
|
data: 原始数据
|
|
|
|
Returns:
|
|
脱敏后的数据
|
|
"""
|
|
sensitive_fields = [
|
|
"password",
|
|
"passwd",
|
|
"pwd",
|
|
"secret",
|
|
"token",
|
|
"api_key",
|
|
"apikey",
|
|
"credit_card",
|
|
"ssn",
|
|
"social_security",
|
|
]
|
|
|
|
sanitized = data.copy()
|
|
|
|
for key in sanitized:
|
|
if isinstance(sanitized[key], dict):
|
|
sanitized[key] = sanitize_sensitive_data(sanitized[key])
|
|
elif any(sensitive in key.lower() for sensitive in sensitive_fields):
|
|
sanitized[key] = "***REDACTED***"
|
|
|
|
return sanitized
|