- Added `auth.py` for mock login and JWT issuance. - Introduced `chat.py` for handling chat requests with role-based access control. - Enhanced `main.py` to include new routers and middleware for tracing. - Implemented input validation in `input_guard.py` to prevent SQL injection. - Created repositories for managing agent sessions and audit logs. - Added exception handling for authorization errors. - Updated settings to include JWT configuration. - Introduced tests for authentication and input validation.
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
|