2026-07-05 11:01:30 +08:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from app.chis.auth import ChisLoginClient, ChisLoginError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FakeTransport:
|
|
|
|
|
def __init__(self, responses):
|
|
|
|
|
self.responses = list(responses)
|
|
|
|
|
self.calls = []
|
|
|
|
|
|
|
|
|
|
def post(self, url, headers, payload):
|
|
|
|
|
self.calls.append({"url": url, "headers": headers, "payload": payload})
|
|
|
|
|
return self.responses.pop(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fake_encrypt(public_key, raw_text):
|
|
|
|
|
if raw_text == "secret":
|
|
|
|
|
return "encrypted-password"
|
|
|
|
|
if raw_text == "1234567890":
|
|
|
|
|
return "encrypted-time"
|
|
|
|
|
return "encrypted-value"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_login_gets_role_apps_and_cookie_without_leaking_password():
|
|
|
|
|
transport = FakeTransport(
|
|
|
|
|
[
|
|
|
|
|
(
|
|
|
|
|
{
|
|
|
|
|
"code": 200,
|
|
|
|
|
"body": {
|
|
|
|
|
"tokens": [
|
|
|
|
|
{"roleName": "其他角色", "id": "1", "userName": "ignored"},
|
|
|
|
|
{"roleName": "责任医生", "id": "role-2", "userName": "doctor"},
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{"Set-Cookie": "JSESSIONID=abc123; Path=/chis; HttpOnly"},
|
|
|
|
|
),
|
|
|
|
|
({"code": 200, "body": {"apps": []}}, {}),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
client = ChisLoginClient(
|
|
|
|
|
base_url="http://chis.example.test/chis",
|
|
|
|
|
public_key="public-key",
|
|
|
|
|
transport=transport,
|
|
|
|
|
encrypt=fake_encrypt,
|
|
|
|
|
now_millis=lambda: 1234567890,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
session = client.login(username="u001", password="secret")
|
|
|
|
|
|
|
|
|
|
assert session.cookie == "JSESSIONID=abc123; sessionId1=abc123; u001=%E8%B4%A3%E4%BB%BB%E5%8C%BB%E7%94%9F@photo/default.jpg"
|
|
|
|
|
assert session.role_id == "role-2"
|
|
|
|
|
assert session.role_name == "责任医生"
|
|
|
|
|
assert session.user_name == "doctor"
|
|
|
|
|
assert transport.calls[0]["url"] == "http://chis.example.test/chis/logon/myRoles"
|
|
|
|
|
assert transport.calls[0]["payload"] == {
|
|
|
|
|
"url": "logon/myRoles",
|
|
|
|
|
"uid": "u001",
|
|
|
|
|
"pwd": "encrypted-password",
|
|
|
|
|
"d": "encrypted-time",
|
|
|
|
|
}
|
|
|
|
|
assert "secret" not in str(transport.calls)
|
|
|
|
|
assert transport.calls[1]["url"].startswith("http://chis.example.test/chis/logon/myApps?")
|
|
|
|
|
assert transport.calls[1]["headers"]["Cookie"] == "JSESSIONID=abc123"
|
|
|
|
|
assert "urt=role-2" in transport.calls[1]["url"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_login_raises_clear_error_when_roles_fail():
|
|
|
|
|
transport = FakeTransport([({"code": 501, "msg": "PasswordNotRight"}, {})])
|
|
|
|
|
client = ChisLoginClient(
|
|
|
|
|
base_url="http://chis.example.test/chis",
|
|
|
|
|
public_key="public-key",
|
|
|
|
|
transport=transport,
|
|
|
|
|
encrypt=fake_encrypt,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(ChisLoginError) as exc_info:
|
|
|
|
|
client.login(username="u001", password="secret")
|
|
|
|
|
|
|
|
|
|
assert exc_info.value.code == "chis_login_failed"
|
|
|
|
|
assert "PasswordNotRight" in str(exc_info.value)
|
|
|
|
|
assert "secret" not in str(exc_info.value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_login_raises_clear_error_when_required_role_missing():
|
|
|
|
|
transport = FakeTransport(
|
|
|
|
|
[
|
|
|
|
|
(
|
|
|
|
|
{
|
|
|
|
|
"code": 200,
|
|
|
|
|
"body": {
|
|
|
|
|
"tokens": [
|
|
|
|
|
{"roleName": "其他角色", "id": "1", "userName": "ignored"},
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{"Set-Cookie": "JSESSIONID=abc123; Path=/chis"},
|
|
|
|
|
),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
client = ChisLoginClient(
|
|
|
|
|
base_url="http://chis.example.test/chis",
|
|
|
|
|
public_key="public-key",
|
|
|
|
|
transport=transport,
|
|
|
|
|
encrypt=fake_encrypt,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(ChisLoginError) as exc_info:
|
|
|
|
|
client.login(username="u001", password="secret")
|
|
|
|
|
|
|
|
|
|
assert exc_info.value.code == "chis_role_not_allowed"
|
|
|
|
|
assert "责任医生助理" in str(exc_info.value)
|
|
|
|
|
assert "责任医生" in str(exc_info.value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_requests_transport_uses_proxy_when_configured():
|
|
|
|
|
client = ChisLoginClient.from_config(
|
|
|
|
|
{
|
|
|
|
|
"CHIS_BASE_URL": "http://chis.example.test/chis",
|
|
|
|
|
"CHIS_PUBLIC_KEY": "public-key",
|
|
|
|
|
"CHIS_PROXY": "socks5h://127.0.0.1:1080",
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert client.transport.session.proxies["http"] == "socks5h://127.0.0.1:1080"
|
|
|
|
|
assert client.transport.session.proxies["https"] == "socks5h://127.0.0.1:1080"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_requests_transport_direct_when_proxy_empty():
|
|
|
|
|
client = ChisLoginClient.from_config(
|
|
|
|
|
{
|
|
|
|
|
"CHIS_BASE_URL": "http://chis.example.test/chis",
|
|
|
|
|
"CHIS_PUBLIC_KEY": "public-key",
|
|
|
|
|
"CHIS_PROXY": "",
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert client.transport.session.proxies == {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_login_client_accepts_plain_host_config():
|
|
|
|
|
client = ChisLoginClient(
|
|
|
|
|
base_url="chis.example.test:9002",
|
|
|
|
|
public_key="public-key",
|
|
|
|
|
transport=FakeTransport([]),
|
|
|
|
|
encrypt=fake_encrypt,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert client.base_url == "http://chis.example.test:9002/chis"
|
|
|
|
|
assert client.host == "chis.example.test:9002"
|
2026-07-05 11:13:34 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_lander_info_uses_existing_cookie_to_query_account_info():
|
|
|
|
|
transport = FakeTransport(
|
|
|
|
|
[
|
|
|
|
|
(
|
|
|
|
|
{
|
|
|
|
|
"code": 200,
|
|
|
|
|
"body": {"userId": "u001", "userName": "doctor"},
|
|
|
|
|
"msg": "success",
|
|
|
|
|
},
|
|
|
|
|
{},
|
|
|
|
|
),
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
client = ChisLoginClient(
|
|
|
|
|
base_url="http://chis.example.test/chis",
|
|
|
|
|
public_key="public-key",
|
|
|
|
|
transport=transport,
|
|
|
|
|
encrypt=fake_encrypt,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = client.get_lander_info("JSESSIONID=abc123; sessionId1=abc123")
|
|
|
|
|
|
|
|
|
|
assert result["body"]["userId"] == "u001"
|
|
|
|
|
assert transport.calls[0]["url"] == "http://chis.example.test/chis/*.jsonRequest?"
|
|
|
|
|
assert transport.calls[0]["headers"]["Cookie"] == "JSESSIONID=abc123; sessionId1=abc123"
|
|
|
|
|
assert transport.calls[0]["payload"] == {
|
|
|
|
|
"serviceId": "chis.myPageService",
|
|
|
|
|
"serviceAction": "getLanderInfo",
|
|
|
|
|
"method": "execute",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_lander_info_raises_session_invalid_when_chis_rejects_cookie():
|
|
|
|
|
transport = FakeTransport([({"code": 403, "msg": "not login"}, {})])
|
|
|
|
|
client = ChisLoginClient(
|
|
|
|
|
base_url="http://chis.example.test/chis",
|
|
|
|
|
public_key="public-key",
|
|
|
|
|
transport=transport,
|
|
|
|
|
encrypt=fake_encrypt,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
with pytest.raises(ChisLoginError) as exc_info:
|
|
|
|
|
client.get_lander_info("JSESSIONID=expired")
|
|
|
|
|
|
|
|
|
|
assert exc_info.value.code == "chis_session_invalid"
|
|
|
|
|
assert "not login" in str(exc_info.value)
|