""" 审计日志模块 记录系统关键操作,用于安全审计和合规追踪 """ from typing import Optional from datetime import datetime import logging 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[int] = None, username: Optional[str] = None, resource_type: Optional[str] = None, resource_id: Optional[str] = None, ip_address: Optional[str] = None, user_agent: Optional[str] = None, request_method: Optional[str] = None, request_path: Optional[str] = None, request_data: Optional[dict] = None, response_status: Optional[int] = None, error_message: Optional[str] = None, db=None, # 新增:可选的数据库实例 ): """ 记录审计日志 Args: action: 操作类型 user_id: 用户ID username: 用户名 resource_type: 资源类型 resource_id: 资源ID ip_address: IP地址 user_agent: User-Agent request_method: 请求方法 request_path: 请求路径 request_data: 请求数据(敏感字段需脱敏) response_status: 响应状态码 error_message: 错误消息 db: 数据库实例(可选,如果不提供则尝试获取) """ from app.infra.repositories.audit_repository import AuditRepository try: # 脱敏敏感数据 if request_data: request_data = sanitize_sensitive_data(request_data) # 如果没有提供数据库实例,尝试从全局获取 if db is None: try: from app.infra.db.postgresql.database import db as default_db # 仅当连接池已初始化时使用 if default_db.pool: db = default_db except ImportError: pass # 如果仍然没有数据库实例 if db is None: # 在某些上下文中可能无法获取,此时静默失败 logger.warning("No database instance provided for audit logging") return audit_repo = AuditRepository(db) await audit_repo.create_log( user_id=user_id, username=username, action=action, resource_type=resource_type, resource_id=resource_id, ip_address=ip_address, user_agent=user_agent, request_method=request_method, request_path=request_path, request_data=request_data, response_status=response_status, error_message=error_message, ) logger.info( f"Audit log created: action={action}, user={username or user_id}, " f"resource={resource_type}:{resource_id}" ) except Exception as e: # 审计日志失败不应影响业务流程 logger.error(f"Failed to create audit log: {e}", exc_info=True) 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