2026-10-13 08:32:55 +08:00
|
|
|
# Phase 2 Task 3: /api/platform/ 前缀豁免租户解析——豁免≠放行。
|
|
|
|
|
#
|
|
|
|
|
# Host 中间件默认按 Host 头解析租户,未知 Host → 404;平台后台
|
|
|
|
|
# /api/platform/* 不依赖租户域名(生产的 admin 域名与租户域名在反代层
|
|
|
|
|
# 区分,spec §4 边界规则),故对前缀豁免。豁免≠放行:豁免只跳过"租户
|
|
|
|
|
# 解析"这一层,受保护端点的认证由 platform_user 依赖守护(401)。
|
|
|
|
|
#
|
|
|
|
|
# 中间件顺序守护(豁免改动后租户中间件仍须最外层)由 test_host_routing.py
|
|
|
|
|
# 的 test_tenant_middleware_is_outermost_user_middleware 覆盖
|
|
|
|
|
# (user_middleware[0] 断言)——全量回归确认仍绿,此处不重复建。
|
|
|
|
|
|
2026-10-13 08:50:16 +08:00
|
|
|
from tests.conftest import NOPE_HOST
|
2026-10-13 08:32:55 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_platform_api_bypasses_tenant_resolution(app_client):
|
|
|
|
|
# 未知 Host 下 POST /api/platform/auth/login(坏凭据即可)可达路由——
|
|
|
|
|
# 不被中间件 404,预期 401(防枚举静态文案,auth_routes)
|
|
|
|
|
r = app_client.post(
|
|
|
|
|
"/api/platform/auth/login",
|
|
|
|
|
json={"username": "nope", "password": "bad"},
|
|
|
|
|
headers={"host": NOPE_HOST},
|
|
|
|
|
)
|
|
|
|
|
assert r.status_code == 401
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_platform_prefix_not_open(app_client):
|
|
|
|
|
# 豁免≠放行:未知 Host 下无 token 打已存在的受保护端点(logout)
|
|
|
|
|
# → 401——认证守护在 platform_user 依赖,不在中间件
|
|
|
|
|
r = app_client.post(
|
|
|
|
|
"/api/platform/auth/logout", headers={"host": NOPE_HOST})
|
|
|
|
|
assert r.status_code == 401
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_prefix_match_precise(app_client):
|
|
|
|
|
# /api/platform-evil/ 仍走租户解析:豁免必须 startswith("/api/platform/")
|
|
|
|
|
# 精确到斜杠。未知 Host → 中间件(而非路由)404——text/plain 的
|
|
|
|
|
# PlainTextResponse 区别于路由 404 的 JSON body
|
|
|
|
|
r = app_client.get("/api/platform-evil/x", headers={"host": NOPE_HOST})
|
|
|
|
|
assert r.status_code == 404
|
|
|
|
|
assert r.headers["content-type"].startswith("text/plain")
|