101 lines
3.4 KiB
Python
101 lines
3.4 KiB
Python
"""A5.7 / ADR-0012:账号与登录态。
|
||
|
||
要证明三件事:密码不落盘、能力调用必须带登录态、登录态对不上就是拒绝。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
from pathlib import Path
|
||
|
||
from fastapi.testclient import TestClient
|
||
|
||
from aps.api.app import app
|
||
from aps.identity import verify_password
|
||
|
||
client = TestClient(app)
|
||
ACCOUNTS_FILE = Path(__file__).resolve().parents[1] / "data" / "demo_accounts.json"
|
||
DEMO_PASSWORD = "aps-demo-pass"
|
||
|
||
|
||
def login(account_id: str = "E1001", password: str = DEMO_PASSWORD) -> dict:
|
||
response = client.post("/api/auth/login", json={"accountId": account_id, "password": password})
|
||
return {"status": response.status_code, "body": response.json()}
|
||
|
||
|
||
def auth_header(token: str) -> dict[str, str]:
|
||
return {"authorization": f"Bearer {token}"}
|
||
|
||
|
||
def test_password_is_never_stored_in_plaintext() -> None:
|
||
"""凭据文件里只有 PBKDF2 哈希;明文密码绝不落盘。"""
|
||
raw = ACCOUNTS_FILE.read_text(encoding="utf-8")
|
||
|
||
assert DEMO_PASSWORD not in raw, "演示密码不该出现在账号文件里"
|
||
stored = json.loads(raw)["accounts"][0]["passwordHash"]
|
||
assert stored.startswith("pbkdf2_sha256$"), stored
|
||
assert verify_password(DEMO_PASSWORD, stored)
|
||
assert not verify_password("wrong-password", stored)
|
||
|
||
|
||
def test_login_returns_token_and_whoami_accepts_it() -> None:
|
||
result = login()
|
||
assert result["status"] == 200
|
||
body = result["body"]
|
||
assert body["accountId"] == "E1001"
|
||
assert body["token"].startswith("aps_")
|
||
assert body["expiresAt"] > 0
|
||
|
||
who = client.get("/api/auth/whoami", headers=auth_header(body["token"]))
|
||
assert who.status_code == 200
|
||
assert who.json()["displayName"] == body["displayName"]
|
||
|
||
|
||
def test_wrong_password_is_rejected() -> None:
|
||
result = login(password="not-the-password")
|
||
|
||
assert result["status"] == 401
|
||
assert "token" not in result["body"]
|
||
|
||
|
||
def test_whoami_rejects_unknown_token() -> None:
|
||
assert client.get("/api/auth/whoami", headers=auth_header("aps_not-a-real-token")).status_code == 401
|
||
|
||
|
||
def test_capability_call_requires_login_state() -> None:
|
||
"""登录态归 APS 判:没带 token 的能力调用一律 401(D9)。"""
|
||
response = client.post("/api/capabilities/aps.orders.query", json={"params": {}})
|
||
|
||
assert response.status_code == 401
|
||
assert "重新绑定" in response.json()["detail"]
|
||
|
||
|
||
def test_capability_call_succeeds_with_token() -> None:
|
||
token = login()["body"]["token"]
|
||
response = client.post(
|
||
"/api/capabilities/aps.orders.query",
|
||
json={"params": {"limit": 2}},
|
||
headers=auth_header(token),
|
||
)
|
||
|
||
assert response.status_code == 200
|
||
assert response.json()["status"] == "succeeded"
|
||
|
||
|
||
def test_account_mismatch_is_forbidden() -> None:
|
||
"""平台传来的账号与登录态不是同一个人:拒绝,别拿别人的登录态干活。"""
|
||
token = login()["body"]["token"]
|
||
response = client.post(
|
||
"/api/capabilities/aps.orders.query",
|
||
json={"params": {}, "identity": {"accountId": "E1002"}},
|
||
headers=auth_header(token),
|
||
)
|
||
|
||
assert response.status_code == 403
|
||
|
||
|
||
def test_logout_invalidates_token() -> None:
|
||
token = login()["body"]["token"]
|
||
assert client.post("/api/auth/logout", headers=auth_header(token)).json()["loggedOut"] is True
|
||
assert client.get("/api/auth/whoami", headers=auth_header(token)).status_code == 401
|