后端新增 (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 通过
207 lines
6.9 KiB
Python
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
|