feat: harden public install guide loading
This commit is contained in:
@@ -38,8 +38,21 @@ async def public_subscription_guides_route(request: web.Request) -> web.Response
|
|||||||
if not share_token:
|
if not share_token:
|
||||||
return web.json_response({"ok": False, "error": "invalid_share_token"}, status=404)
|
return web.json_response({"ok": False, "error": "invalid_share_token"}, status=404)
|
||||||
|
|
||||||
status = await _subscription_guides_status_shared(request.app)
|
|
||||||
subscription = await _public_subscription_payload(request, share_token)
|
subscription = await _public_subscription_payload(request, share_token)
|
||||||
|
if not subscription.get("active"):
|
||||||
|
return web.json_response(
|
||||||
|
{
|
||||||
|
"ok": False,
|
||||||
|
"enabled": False,
|
||||||
|
"config": None,
|
||||||
|
"source": None,
|
||||||
|
"subscription": subscription,
|
||||||
|
"error": "subscription_unavailable",
|
||||||
|
},
|
||||||
|
status=404,
|
||||||
|
)
|
||||||
|
|
||||||
|
status = await _subscription_guides_status_shared(request.app)
|
||||||
payload = {
|
payload = {
|
||||||
"enabled": bool(status.get("enabled")),
|
"enabled": bool(status.get("enabled")),
|
||||||
"config": status.get("config") if status.get("enabled") else None,
|
"config": status.get("config") if status.get("enabled") else None,
|
||||||
|
|||||||
+10
-1
@@ -1064,6 +1064,15 @@
|
|||||||
openExternalLink(url);
|
openExternalLink(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openPublicConnectLink() {
|
||||||
|
const url = publicInstallSubscription?.connect_url || publicInstallSubscription?.config_link;
|
||||||
|
if (!url) {
|
||||||
|
showToast(t("wa_connect_link_unavailable"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openExternalLink(url);
|
||||||
|
}
|
||||||
|
|
||||||
function openInstallOrConnect() {
|
function openInstallOrConnect() {
|
||||||
if (canUseInstallGuides()) {
|
if (canUseInstallGuides()) {
|
||||||
goInstall();
|
goInstall();
|
||||||
@@ -1363,7 +1372,7 @@
|
|||||||
user={{}}
|
user={{}}
|
||||||
subscription={publicInstallSubscription || { install_share_token: publicInstallToken }}
|
subscription={publicInstallSubscription || { install_share_token: publicInstallToken }}
|
||||||
{goHome}
|
{goHome}
|
||||||
{openConnectLink}
|
openConnectLink={openPublicConnectLink}
|
||||||
{openExternalLink}
|
{openExternalLink}
|
||||||
{copyText}
|
{copyText}
|
||||||
{t}
|
{t}
|
||||||
|
|||||||
@@ -21,7 +21,12 @@ export function createInstallGuidesStore({ api, t, showToast }) {
|
|||||||
});
|
});
|
||||||
if (!force && snapshot?.loaded) return snapshot;
|
if (!force && snapshot?.loaded) return snapshot;
|
||||||
const promise = (async () => {
|
const promise = (async () => {
|
||||||
state.update((s) => ({ ...s, loading: true, error: "" }));
|
state.update((s) => ({
|
||||||
|
...s,
|
||||||
|
loading: true,
|
||||||
|
loaded: force ? false : s.loaded,
|
||||||
|
error: "",
|
||||||
|
}));
|
||||||
try {
|
try {
|
||||||
const response = await api(path);
|
const response = await api(path);
|
||||||
const next = {
|
const next = {
|
||||||
|
|||||||
@@ -206,6 +206,71 @@ class SubscriptionGuidesRouteTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
self.assertEqual(body["subscription"]["install_share_token"], share_token)
|
self.assertEqual(body["subscription"]["install_share_token"], share_token)
|
||||||
panel_service.get_user_by_uuid.assert_awaited_once_with("panel-user")
|
panel_service.get_user_by_uuid.assert_awaited_once_with("panel-user")
|
||||||
|
|
||||||
|
async def test_public_route_rejects_unknown_share_token_without_loading_config(self):
|
||||||
|
share_token = "8f559061460e8fede78ef18dce887236"
|
||||||
|
panel_service = SimpleNamespace(
|
||||||
|
get_subscription_page_config_list=AsyncMock(),
|
||||||
|
get_subscription_page_config_by_uuid=AsyncMock(),
|
||||||
|
get_user_by_uuid=AsyncMock(),
|
||||||
|
)
|
||||||
|
request = self._request(
|
||||||
|
self._settings(SUBSCRIPTION_MINI_APP_URL="https://app.example.test/app"),
|
||||||
|
panel_service,
|
||||||
|
match_info={"share_token": share_token},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
guides.subscription_dal,
|
||||||
|
"get_subscription_by_install_share_token",
|
||||||
|
AsyncMock(return_value=None),
|
||||||
|
):
|
||||||
|
response = await guides.public_subscription_guides_route(request)
|
||||||
|
|
||||||
|
body = json.loads(response.text)
|
||||||
|
self.assertEqual(response.status, 404)
|
||||||
|
self.assertFalse(body["ok"])
|
||||||
|
self.assertEqual(body["error"], "subscription_unavailable")
|
||||||
|
self.assertFalse(body["enabled"])
|
||||||
|
self.assertIsNone(body["config"])
|
||||||
|
self.assertEqual(body["subscription"]["install_share_token"], share_token)
|
||||||
|
self.assertFalse(body["subscription"]["active"])
|
||||||
|
panel_service.get_subscription_page_config_list.assert_not_called()
|
||||||
|
panel_service.get_subscription_page_config_by_uuid.assert_not_called()
|
||||||
|
panel_service.get_user_by_uuid.assert_not_called()
|
||||||
|
|
||||||
|
async def test_public_route_rejects_inactive_share_token_without_panel_user_lookup(self):
|
||||||
|
share_token = "8f559061460e8fede78ef18dce887236"
|
||||||
|
panel_service = SimpleNamespace(
|
||||||
|
get_subscription_page_config_list=AsyncMock(),
|
||||||
|
get_subscription_page_config_by_uuid=AsyncMock(),
|
||||||
|
get_user_by_uuid=AsyncMock(),
|
||||||
|
)
|
||||||
|
request = self._request(
|
||||||
|
self._settings(SUBSCRIPTION_MINI_APP_URL="https://app.example.test/app"),
|
||||||
|
panel_service,
|
||||||
|
match_info={"share_token": share_token},
|
||||||
|
)
|
||||||
|
local_sub = SimpleNamespace(
|
||||||
|
panel_user_uuid="panel-user",
|
||||||
|
install_share_token=share_token,
|
||||||
|
is_active=False,
|
||||||
|
end_date=datetime.now(timezone.utc) + timedelta(days=3),
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
guides.subscription_dal,
|
||||||
|
"get_subscription_by_install_share_token",
|
||||||
|
AsyncMock(return_value=local_sub),
|
||||||
|
):
|
||||||
|
response = await guides.public_subscription_guides_route(request)
|
||||||
|
|
||||||
|
body = json.loads(response.text)
|
||||||
|
self.assertEqual(response.status, 404)
|
||||||
|
self.assertFalse(body["ok"])
|
||||||
|
self.assertEqual(body["error"], "subscription_unavailable")
|
||||||
|
self.assertFalse(body["subscription"]["active"])
|
||||||
|
panel_service.get_user_by_uuid.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user