fix(cma): isolate KPI alignment tree by entity (project-20260918-alignment-entity-leak)

GET /api/cma/alignment/tree queried all active KPIs without entity_id filter,

so switching the three alignment modes showed other tenants' KPIs.

Add get_entity_id dependency and filter KPIDefinition by entity_id.

Three mode builders (_build_vertical_tree/_build_horizontal_tree/_build_bsc_chain) unchanged.

Auth semantics unchanged; cross-entity explicit entity_id still returns 403.

Test: tests/test_alignment_entity_isolation_20260918.py (3 modes x both directions, 403, 401).
This commit is contained in:
Hermes CI Fix
2026-09-18 17:18:52 +08:00
parent d33b5e1ced
commit a2fedd892a
2 changed files with 201 additions and 3 deletions
+13 -3
View File
@@ -10,6 +10,7 @@ import json
from app.database import get_db
from app.auth_middleware import require_auth, require_role, require_auth_401
from app.deps import get_entity_id
from app.models import KPIDefinition, OperationLog, RolePermission
router = APIRouter(prefix="/api/cma/alignment", tags=["KPI目标对齐"],
@@ -90,14 +91,20 @@ def set_alignment_config(
@router.get("/tree")
def get_alignment_tree(
kpi_id: Optional[int] = Query(None),
entity_id: int = Depends(get_entity_id),
db: Session = Depends(get_db),
user = Depends(require_auth),
):
"""获取KPI对齐关系树
"""获取KPI对齐关系树(按当前账套隔离)
根据当前系统配置的对齐模式,返回KPI的父子层级关系。
如果指定kpi_id,返回该KPI及其下级树;
如果不指定,返回整个对齐树。
账套隔离(2026-09-18 project-20260918-alignment-entity-leak):
只取 entity_id 归属本账套的 KPI(entity_id 来自会话 token 绑定,写法对齐
growth-quality / predict.accuracy)。三种模式共用本列表 → 三模式同时隔离。
跨实体显式传参(?entity_id=<他人账套>)由 get_entity_id 返回 403。
"""
# 获取当前模式
config_perm = db.query(RolePermission).filter(RolePermission.key == "alignment_config").first()
@@ -105,8 +112,11 @@ def get_alignment_tree(
if not mode:
raise HTTPException(400, "系统未配置对齐模式,请先在系统设置中初始化")
# 获取所有KPI
kpis = db.query(KPIDefinition).filter(KPIDefinition.status == "active").order_by(KPIDefinition.kpi_code).all()
# 获取本账套所有KPI(账套隔离:仅当前 entity_id,不再返回全实体 KPI)
kpis = (db.query(KPIDefinition)
.filter(KPIDefinition.entity_id == entity_id, KPIDefinition.status == "active")
.order_by(KPIDefinition.kpi_code)
.all())
kpi_map = {k.id: k for k in kpis}
# 构建父子关系