Implement CHIS login flow
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
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"
|
||||
Reference in New Issue
Block a user