"""跨服务器桌面认证:只在路由缺失时兼容,拒绝结果不能降级。""" import io import json import unittest import urllib.error from unittest import mock import zyt_auth class DesktopVerificationRegressionTest(unittest.TestCase): def response(self, payload): return io.BytesIO(json.dumps(payload).encode("utf-8")) def session(self, **updates): data = {"user_id": "admin:9", "terminal": 7, "status": "active", "root": False, "role_ids": [2], "permissions": ["tcm.diagnosis/lists"]} data.update(updates) return {"code": 1, "data": data} def verify(self): return zyt_auth.verify_desktop_token("test-token", api_url="https://identity.example.test") def missing(self, status=404): return urllib.error.HTTPError("https://identity.example.test", status, "test", None, None) def test_success_never_requests_ip_bound_web_profile(self): with mock.patch.object(zyt_auth.urllib.request, "urlopen", side_effect=[ self.response(self.session(root=True, permissions=["*"])), self.response({"code": -1, "msg": "ip地址发生变化,请重新登录"}), ]) as opened: identity = self.verify() self.assertEqual(opened.call_count, 1) self.assertTrue(identity["root"]) self.assertEqual(identity["role_ids"], [2]) self.assertEqual(identity["permissions"], ["*"]) self.assertTrue(identity["permissions_known"]) def test_missing_permissions_do_not_trigger_profile_request(self): payload = self.session() del payload["data"]["permissions"] with mock.patch.object(zyt_auth.urllib.request, "urlopen", return_value=self.response(payload)) as opened: identity = self.verify() self.assertEqual(opened.call_count, 1) self.assertEqual(identity["permissions"], []) self.assertFalse(identity["permissions_known"]) def test_empty_permissions_are_known(self): with mock.patch.object(zyt_auth.urllib.request, "urlopen", return_value=self.response(self.session(permissions=[]))): identity = self.verify() self.assertTrue(identity["permissions_known"]) self.assertEqual(identity["permissions"], []) def test_only_missing_routes_allow_legacy_verification(self): for status in (404, 405): with self.subTest(status=status), mock.patch.object(zyt_auth.urllib.request, "urlopen", side_effect=[ self.missing(status), self.response({"code": 1, "data": { "user": {"id": 9, "disable": 0, "role_ids": [2]}, "permissions": []}}), ]) as opened: identity = self.verify() self.assertEqual(identity["user_id"], "admin:9") self.assertEqual(opened.call_count, 2) def test_http_rejections_do_not_fall_back(self): for status in (401, 403, 500, 502): with self.subTest(status=status), mock.patch.object(zyt_auth.urllib.request, "urlopen", side_effect=self.missing(status)) as opened: with self.assertRaises(zyt_auth.ZytAuthError): self.verify() self.assertEqual(opened.call_count, 1) def test_invalid_identity_cannot_be_accepted_through_web_fallback(self): failures = [{"code": -1, "msg": "登录已失效"}, {"code": "bad"}, [], {"code": 1, "data": []}, self.session(user_id=""), self.session(terminal=1), self.session(terminal="bad"), self.session(status="disabled")] for payload in failures: with self.subTest(payload=payload), mock.patch.object(zyt_auth.urllib.request, "urlopen", side_effect=[ self.response(payload), self.response({"code": 1, "data": {"user": {"id": 9}}}), ]) as opened: with self.assertRaises(zyt_auth.ZytAuthError): self.verify() self.assertEqual(opened.call_count, 1) def test_bad_json_and_network_failure_do_not_fall_back(self): for result in (io.BytesIO(b"error"), urllib.error.URLError("offline")): with self.subTest(result=result), mock.patch.object(zyt_auth.urllib.request, "urlopen", side_effect=[result]) as opened: with self.assertRaises(zyt_auth.ZytAuthError): self.verify() self.assertEqual(opened.call_count, 1) def test_legacy_ip_error_identifies_missing_server_deployment(self): with mock.patch.object(zyt_auth.urllib.request, "urlopen", side_effect=[ self.missing(), self.response({"code": -1, "msg": "ip地址发生变化,请重新登录"}), ]): with self.assertRaisesRegex(zyt_auth.ZytAuthError, "/adminapi/desktop/session"): self.verify() if __name__ == "__main__": unittest.main()