Files
opsxsql/backend/app/api/v1/endpoints/audit_rule.py
T
logan 58eb78c987 merge: SQL平台代码v1 优化包合并 — 193条审核规则 + 16页面UI优化
后端新增 (Step 1):
- mysql_audit_rules.py: 63条 MySQL审核规则 (DDL 36 + DML 9 + 命名 5 + 性能 5 + 安全 5)
- redis_audit_rules.py: 43条 Redis审核规则 (KEY管理 10 + 命令安全 12 + 数据结构 8 + 性能 8 + 安全 5)
- mongodb_audit_rules.py: 42条 MongoDB审核规则 (集合管理 + 查询安全 + 聚合管道 + 写入安全 + 性能)
- postgres_audit_rules.py: 45条 PostgreSQL审核规则 (DDL 18 + DML 8 + DQL 6 + 安全 6 + 性能 7)

后端合并 (Step 2):
- audit_engine_v2.py: 新增4个DB规则模块import (100+→200+规则)
- audit_rule_seeds.py: 新增30条MongoDB种子规则 + 别名映射
- audit_rule.py: 新增数据库类型别名 (postgresql→postgres, pg→postgres, mongo→mongodb)

前端优化 (Step 4) — 16页面全覆盖:
- InstancePage, WorkflowPage, TicketsPage, TicketCreatePage, TicketDetailPage
- UsersPage, SlowQueryPage, DataDictionaryPage, Dashboard
- InstanceGroupPage, AuditLogsPage, QueriesPage, AuditRulesPage, RolesPage
- useAuth.tsx, SqlEditor.tsx

后端测试 895/895 通过, 前端 tsc --noEmit 通过
2026-05-23 16:46:13 +08:00

207 lines
6.9 KiB
Python

"""Audit rule management endpoints."""
from typing import Optional, List
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.core.security import require_role
from app.models.user import User
from app.models.audit_rule import AuditRule
from app.schemas.audit_rule import AuditRuleCreate, AuditRuleUpdate, AuditRuleResponse
from app.services.audit_rule_seeds import SEED_RULES_MAP
from app.services.audit_rule_seeds_v2 import seed_all_rules
router = APIRouter(prefix="/audit-rules", tags=["审核规则"])
@router.get("", response_model=List[AuditRuleResponse])
async def list_audit_rules(
rule_type: Optional[str] = None,
severity: Optional[str] = None,
db_type: Optional[str] = None,
is_enabled: Optional[bool] = None,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
stmt = select(AuditRule).order_by(AuditRule.id)
if rule_type:
stmt = stmt.where(AuditRule.rule_type == rule_type)
if severity:
stmt = stmt.where(AuditRule.severity == severity)
if db_type:
# 支持类型别名: postgresql -> postgres, mongo -> mongodb
db_type_aliases = {"postgresql": "postgres", "pg": "postgres", "mongo": "mongodb"}
resolved = db_type_aliases.get(db_type.lower(), db_type.lower())
stmt = stmt.where((AuditRule.db_type == resolved) | (AuditRule.db_type == db_type) | (AuditRule.db_type == "all"))
if is_enabled is not None:
stmt = stmt.where(AuditRule.is_enabled == is_enabled)
result = await db.execute(stmt)
return result.scalars().all()
@router.post("", response_model=AuditRuleResponse, status_code=201)
async def create_audit_rule(
data: AuditRuleCreate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
exists = await db.execute(select(AuditRule).where(AuditRule.name == data.name))
if exists.scalar_one_or_none():
raise HTTPException(status_code=400, detail="规则名称已存在")
rule = AuditRule(**data.model_dump())
db.add(rule)
await db.commit()
await db.refresh(rule)
return rule
@router.get("/stats/summary")
async def rule_stats(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
total_stmt = select(func.count(AuditRule.id))
total = (await db.execute(total_stmt)).scalar()
enabled_stmt = select(func.count(AuditRule.id)).where(AuditRule.is_enabled == True)
enabled = (await db.execute(enabled_stmt)).scalar()
type_stmt = select(AuditRule.rule_type, func.count(AuditRule.id)).group_by(AuditRule.rule_type)
type_result = (await db.execute(type_stmt)).all()
by_type = {row[0]: row[1] for row in type_result}
return {"total": total, "enabled": enabled, "disabled": total - enabled, "by_type": by_type}
@router.post("/seed/{db_type}", response_model=List[AuditRuleResponse])
async def seed_audit_rules(
db_type: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
"""批量初始化预设审核规则种子数据。
支持4种数据库类型:
- mysql: MySQL 专用规则 (60+ 条)
- postgres: PostgreSQL 专用规则 (45+ 条)
- redis: Redis 专用规则 (43+ 条)
- mongodb: MongoDB 专用规则 (42+ 条)
"""
db_type_lower = db_type.lower()
# 别名映射
db_type_aliases = {
"postgresql": "postgres",
"pg": "postgres",
"mongo": "mongodb",
}
db_type_lower = db_type_aliases.get(db_type_lower, db_type_lower)
if db_type_lower not in SEED_RULES_MAP:
raise HTTPException(
status_code=400,
detail=f"不支持的数据库类型: {db_type},支持: {', '.join(SEED_RULES_MAP.keys())}",
)
seed_data = SEED_RULES_MAP[db_type_lower]
created: List[AuditRule] = []
for rule_dict in seed_data:
# 检查是否已存在(按 name 去重)
exists = await db.execute(select(AuditRule).where(AuditRule.name == rule_dict["name"]))
if exists.scalar_one_or_none():
continue # 跳过已存在的规则
rule = AuditRule(**rule_dict)
db.add(rule)
created.append(rule)
await db.commit()
for rule in created:
await db.refresh(rule)
return created
@router.get("/{rule_id}", response_model=AuditRuleResponse)
async def get_audit_rule(
rule_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
result = await db.execute(select(AuditRule).where(AuditRule.id == rule_id))
rule = result.scalar_one_or_none()
if not rule:
raise HTTPException(status_code=404, detail="规则不存在")
return rule
@router.put("/{rule_id}", response_model=AuditRuleResponse)
async def update_audit_rule(
rule_id: int,
data: AuditRuleUpdate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
result = await db.execute(select(AuditRule).where(AuditRule.id == rule_id))
rule = result.scalar_one_or_none()
if not rule:
raise HTTPException(status_code=404, detail="规则不存在")
update_data = data.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(rule, key, value)
await db.commit()
await db.refresh(rule)
return rule
@router.delete("/{rule_id}")
async def delete_audit_rule(
rule_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
result = await db.execute(select(AuditRule).where(AuditRule.id == rule_id))
rule = result.scalar_one_or_none()
if not rule:
raise HTTPException(status_code=404, detail="规则不存在")
await db.delete(rule)
await db.commit()
return {"message": "规则已删除"}
@router.post("/{rule_id}/toggle", response_model=AuditRuleResponse)
async def toggle_audit_rule(
rule_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
result = await db.execute(select(AuditRule).where(AuditRule.id == rule_id))
rule = result.scalar_one_or_none()
if not rule:
raise HTTPException(status_code=404, detail="规则不存在")
rule.is_enabled = not rule.is_enabled
await db.commit()
await db.refresh(rule)
return rule
@router.post("/seed-v2", response_model=List[AuditRuleResponse])
async def seed_v2_audit_rules(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
"""批量初始化 SQL 审核引擎 V2 的所有规则种子数据(40+ 条规则)。
涵盖 DDL/DML/DQL/命名规范/性能/安全等多个维度,
跳过数据库中已存在的规则(按名称去重)。
"""
created = await seed_all_rules(db)
return created