54 lines
2.0 KiB
Python
54 lines
2.0 KiB
Python
"""数据层归属校验(第二层)。"""
|
|||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from app.gateway.jwt_service import permissions_for_roles
|
||
|
|
from app.model.schemas import AuthContext
|
||
|
|
from app.repository.advisor_rel_repository import AdvisorRelRepository
|
||
|
|
from app.repository.audit_repository import AuditRepository
|
||
|
|
from app.utils.exceptions import ForbiddenError
|
||
|
|
|
||
|
|
|
||
|
|
def audit_denied(ctx: AuthContext, error_code: str, customer_id: str | None) -> None:
|
||
|
|
AuditRepository().insert(
|
||
|
|
trace_id=ctx.trace_id,
|
||
|
|
event_type="auth_denied",
|
||
|
|
agent_type=ctx.agent_type,
|
||
|
|
actor_id=ctx.sub,
|
||
|
|
customer_id=customer_id,
|
||
|
|
decision=error_code,
|
||
|
|
input_summary={"roles": ctx.roles, "agent_type": ctx.agent_type},
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def assert_customer_access(ctx: AuthContext, customer_id: str, *, action: str = "detail") -> None:
|
||
|
|
if ctx.token_type == "customer":
|
||
|
|
if ctx.sub != customer_id:
|
||
|
|
audit_denied(ctx, "AUTH_403_NOT_OWNER", customer_id)
|
||
|
|
raise ForbiddenError("无权访问该客户数据", error_code="AUTH_403_NOT_OWNER")
|
||
|
|
return
|
||
|
|
|
||
|
|
if "advisor" in ctx.roles:
|
||
|
|
if not AdvisorRelRepository().is_assigned(ctx.sub, customer_id):
|
||
|
|
audit_denied(ctx, "AUTH_403_NOT_ASSIGNED", customer_id)
|
||
|
|
raise ForbiddenError("客户不在您的服务名下", error_code="AUTH_403_NOT_ASSIGNED")
|
||
|
|
return
|
||
|
|
|
||
|
|
if "analyst" in ctx.roles:
|
||
|
|
if action == "detail" and "core:customer:read:detail" not in ctx.permissions:
|
||
|
|
audit_denied(ctx, "AUTH_403_SCOPE", customer_id)
|
||
|
|
raise ForbiddenError("分析员无客户明细权限", error_code="AUTH_403_SCOPE")
|
||
|
|
return
|
||
|
|
|
||
|
|
if "risk_officer" in ctx.roles:
|
||
|
|
return
|
||
|
|
|
||
|
|
audit_denied(ctx, "AUTH_403_ROLE", customer_id)
|
||
|
|
raise ForbiddenError("当前角色无权访问客户数据", error_code="AUTH_403_ROLE")
|
||
|
|
|
||
|
|
|
||
|
|
def resolve_effective_customer_id(ctx: AuthContext, requested: str | None) -> str | None:
|
||
|
|
if ctx.token_type == "customer":
|
||
|
|
return ctx.sub
|
||
|
|
return requested
|