Files
cma-management/backend/app/deps.py
T
Hermes CI Fix b31f9b80c4 feat: 账套模式后端 — token绑定entity_id + user_entities授权表 + 解析链倒置
- auth_middleware: create_token存JSON{user_id,entity_id},旧int格式token强制下线
- models: 新增UserEntity授权表(user_id↔entity_id多对多,唯一约束)
- database: init_db自动建表+存量用户×active企业默认授权(平滑迁移)
- deps: get_entity_id解析链倒置 token优先 → Bot白名单(query/header校验entity active) → 默认1
- auth: login加entity_id+授权校验; 新增switch-entity/my-entities/登录页entities接口; register自动授权
2026-08-11 11:24:52 +08:00

61 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""多租户公共依赖 — 账套模式:token优先,query/header降级为Bot服务白名单"""
from fastapi import Request, Header, Query, Depends, HTTPException
from typing import Optional
from sqlalchemy.orm import Session
from app.database import get_db
from app.auth_middleware import get_token_entity_id
from app.models import Entity
def get_entity_id(
request: Request,
x_entity_id: Optional[str] = Header(None, alias="X-Entity-Id"),
entity_id: Optional[int] = Query(None, ge=1),
db: Session = Depends(get_db),
) -> int:
"""解析当前企业ID(账套模式):token优先 → query/headerBot白名单)→ 默认1
解析链(倒置后):
1. Authorization Bearer token → 优先返回 token.entity_id(唯一可信来源)
2. query参数 / X-Entity-Id header → 仅Bot服务通道使用(校验entity状态active
3. body中的 entity_idPOST场景)→ Bot服务通道兼容
4. 兜底默认 1(酣客)
安全说明:登录用户必须通过token绑定账套,query/header传入的entity_id
在token存在时被忽略,防止越权传参(旧漏洞:query优先且无授权校验)。
"""
# 1. token优先(账套模式唯一来源)
auth = request.headers.get("Authorization", "")
if auth.startswith("Bearer "):
token_entity = get_token_entity_id(auth[7:])
if token_entity is not None:
return token_entity
# token存在但是旧格式/无entity → 账套模式下强制走白名单或默认(由require_auth拦截)
# 这里不抛401:公开接口可能带旧token,交给require_auth统一处理
# 2. query/header → Bot服务通道白名单(校验entity状态active
candidate = None
if entity_id is not None:
candidate = entity_id
elif x_entity_id and x_entity_id.isdigit():
candidate = int(x_entity_id)
# 3. body 中的 entity_idPOST场景,Bot通道兼容)
if candidate is None and request.method in ("POST", "PUT", "PATCH"):
try:
body = request.state.body_json or {}
if body.get("entity_id"):
candidate = int(body["entity_id"])
except Exception:
pass
if candidate is not None:
ent = db.query(Entity).filter(Entity.id == candidate).first()
if ent and ent.status == "active":
return candidate
# Bot通道校验不通过:不返回默认,直接拒绝(防越权注入无效entity)
raise HTTPException(403, f"企业 entity_id={candidate} 不存在或未激活")
return 1 # 默认酣客(无token、无参数时兜底,兼容存量公开接口)