From b12cdfd145a27fc8177152b914427046743d72ea Mon Sep 17 00:00:00 2001 From: Sebastian Streich Date: Tue, 10 Dec 2024 14:54:22 +0100 Subject: [PATCH] Allow sending settings to the web-ext (#10112) * Allow sending settings to the web-ext * Export makeMessage * Fix test, actually send the correct blob --- src/webextensionadapter.cpp | 215 +++++++++++++----------- src/webextensionadapter.h | 4 + tests/functional/testWebExtensionApi.js | 44 +++-- tests/functional/utils/webextension.js | 15 +- 4 files changed, 165 insertions(+), 113 deletions(-) diff --git a/src/webextensionadapter.cpp b/src/webextensionadapter.cpp index 4e8e9da950..128452ed76 100644 --- a/src/webextensionadapter.cpp +++ b/src/webextensionadapter.cpp @@ -4,6 +4,7 @@ #include "webextensionadapter.h" +#include #include #include #include @@ -73,104 +74,112 @@ WebExtensionAdapter::WebExtensionAdapter(QObject* parent) connect(vpn->connectionHealth(), &ConnectionHealth::stabilityChanged, this, &WebExtensionAdapter::writeState); - m_commands = QList( - {RequestType{"activate", - [](const QJsonObject&) { - auto t = new TaskControllerAction( - TaskControllerAction::eActivateForExtension); - TaskScheduler::scheduleTask(t); - QJsonObject obj; - obj["ok"] = true; - return QJsonObject(); - }}, - RequestType{"deactivate", - [](const QJsonObject&) { - auto t = new TaskControllerAction( - TaskControllerAction::eDeactivateForExtension); - TaskScheduler::scheduleTask(t); - QJsonObject obj; - obj["ok"] = true; - return QJsonObject(); - }}, - RequestType{"servers", - [this](const QJsonObject&) { - QJsonObject servers; - serializeServerCountry( - MozillaVPN::instance()->serverCountryModel(), servers); - - QJsonObject obj; - obj["servers"] = servers; - return obj; - }}, - RequestType{"focus", - [](const QJsonObject&) { - QmlEngineHolder* engine = QmlEngineHolder::instance(); - engine->showWindow(); - return QJsonObject{}; - }}, - RequestType{"openAuth", - [](const QJsonObject&) { - MozillaVPN* vpn = MozillaVPN::instance(); - if (vpn->state() != MozillaVPN::StateInitialize) { - return QJsonObject{}; - } - vpn->authenticate(); - return QJsonObject{}; - }}, - RequestType{"disabled_apps", - [](const QJsonObject&) { - QJsonArray apps; - for (const QString& app : - SettingsHolder::instance()->vpnDisabledApps()) { - apps.append(app); - } - - QJsonObject obj; - obj["disabled_apps"] = apps; - return obj; - }}, - RequestType{"featurelist", - [this](const QJsonObject&) { - QJsonObject obj; - obj["featurelist"] = serializeFeaturelist(); - return obj; - }}, - - RequestType{"status", - [this](const QJsonObject&) { - QJsonObject obj; - obj["status"] = serializeStatus(); - return obj; - }}, - RequestType{"telemetry", - [](const QJsonObject& data) { - auto info = WebextensionTelemetry::fromJson(data); - if (info.has_value()) { - WebextensionTelemetry::recordTelemetry(info.value()); - } - return QJsonObject{}; - }}, - RequestType{"session_start", - [](const QJsonObject& data) { - WebextensionTelemetry::startSession(); - return QJsonObject{}; - }}, - RequestType{"session_stop", - [](const QJsonObject& data) { - WebextensionTelemetry::stopSession(); - return QJsonObject{}; - }}, - RequestType{"interventions", [](const QJsonObject&) { - QJsonObject out; - QJsonArray interventions; + m_commands = QList({ + RequestType{"activate", + [](const QJsonObject&) { + auto t = new TaskControllerAction( + TaskControllerAction::eActivateForExtension); + TaskScheduler::scheduleTask(t); + QJsonObject obj; + obj["ok"] = true; + return QJsonObject(); + }}, + RequestType{"deactivate", + [](const QJsonObject&) { + auto t = new TaskControllerAction( + TaskControllerAction::eDeactivateForExtension); + TaskScheduler::scheduleTask(t); + QJsonObject obj; + obj["ok"] = true; + return QJsonObject(); + }}, + RequestType{"servers", + [this](const QJsonObject&) { + QJsonObject servers; + serializeServerCountry( + MozillaVPN::instance()->serverCountryModel(), servers); + + QJsonObject obj; + obj["servers"] = servers; + return obj; + }}, + RequestType{"focus", + [](const QJsonObject&) { + QmlEngineHolder* engine = QmlEngineHolder::instance(); + engine->showWindow(); + return QJsonObject{}; + }}, + RequestType{"openAuth", + [](const QJsonObject&) { + MozillaVPN* vpn = MozillaVPN::instance(); + if (vpn->state() != MozillaVPN::StateInitialize) { + return QJsonObject{}; + } + vpn->authenticate(); + return QJsonObject{}; + }}, + RequestType{"disabled_apps", + [](const QJsonObject&) { + QJsonArray apps; + for (const QString& app : + SettingsHolder::instance()->vpnDisabledApps()) { + apps.append(app); + } + + QJsonObject obj; + obj["disabled_apps"] = apps; + return obj; + }}, + RequestType{"featurelist", + [this](const QJsonObject&) { + QJsonObject obj; + obj["featurelist"] = serializeFeaturelist(); + return obj; + }}, + RequestType{"status", + [this](const QJsonObject&) { + QJsonObject obj; + obj["status"] = serializeStatus(); + return obj; + }}, + RequestType{"telemetry", + [](const QJsonObject& data) { + auto info = WebextensionTelemetry::fromJson(data); + if (info.has_value()) { + WebextensionTelemetry::recordTelemetry(info.value()); + } + return QJsonObject{}; + }}, + RequestType{"session_start", + [](const QJsonObject& data) { + WebextensionTelemetry::startSession(); + return QJsonObject{}; + }}, + RequestType{"session_stop", + [](const QJsonObject& data) { + WebextensionTelemetry::stopSession(); + return QJsonObject{}; + }}, + RequestType{"interventions", + [](const QJsonObject&) { + QJsonObject out; + QJsonArray interventions; #ifdef MZ_WINDOWS - if (Intervention::KillerNetwork::systemAffected()) { - interventions.append(Intervention::KillerNetwork::id); - } + if (Intervention::KillerNetwork::systemAffected()) { + interventions.append(Intervention::KillerNetwork::id); + } #endif - out["interventions"] = interventions; - return out; - }}}); + out["interventions"] = interventions; + return out; + }}, + RequestType{"settings", + [this](const QJsonObject& data) { + if (data["settings"].isObject()) { + applySettings(data["settings"].toObject()); + } + return QJsonObject{{"settings", serializeSettings()}}; + }}, + }); } WebExtensionAdapter::~WebExtensionAdapter() { @@ -290,3 +299,17 @@ void WebExtensionAdapter::serializeServerCountry(ServerCountryModel* model, obj["countries"] = countries; } + +QJsonObject WebExtensionAdapter::serializeSettings() { + auto const settings = SettingsHolder::instance(); + return {{"extensionTelemetryEnabled", settings->extensionTelemetryEnabled()}}; +} + +void WebExtensionAdapter::applySettings(const QJsonObject& data) { + auto const settings = SettingsHolder::instance(); + + auto enabled = data["extensionTelemetryEnabled"]; + if (enabled.isBool()) { + settings->setExtensionTelemetryEnabled(enabled.toBool()); + } +} diff --git a/src/webextensionadapter.h b/src/webextensionadapter.h index 001aa84a46..4ed338153c 100644 --- a/src/webextensionadapter.h +++ b/src/webextensionadapter.h @@ -29,6 +29,10 @@ class WebExtensionAdapter : public WebExtension::BaseAdapter { QJsonObject serializeStatus(); QJsonObject serializeFeaturelist(); void serializeServerCountry(ServerCountryModel* model, QJsonObject& obj); + + QJsonObject serializeSettings(); + void applySettings(const QJsonObject& data); + QPropertyObserver mProxyStateChanged; }; diff --git a/tests/functional/testWebExtensionApi.js b/tests/functional/testWebExtensionApi.js index 8d241b0177..5befc83e55 100644 --- a/tests/functional/testWebExtensionApi.js +++ b/tests/functional/testWebExtensionApi.js @@ -11,6 +11,7 @@ const { connectExtension, getMessageStream, ExtensionMessage, + makeMessage, readResponseOfType } = require('./utils/webextension.js'); @@ -29,7 +30,7 @@ describe('WebExtension API', function() { const sock = await connectExtension(); const messagePipe = getMessageStream(sock); const statusPromise = readResponseOfType('status', messagePipe); - sentToClient(new ExtensionMessage('status'), sock); + sentToClient(makeMessage('status'), sock); const msg = await statusPromise assert(msg.status.version, `A Version is sent in msg: ${JSON.stringify(msg)}` ) assert(msg.status.connectionHealth, `The current Connection Health status is sent in msg: ${JSON.stringify(msg)}` ) @@ -38,10 +39,10 @@ describe('WebExtension API', function() { it('A Webextension can activate the VPN', async () => { const sock = await connectExtension(); const messagePipe = getMessageStream(sock); - sentToClient(new ExtensionMessage('activate'), sock); + sentToClient(makeMessage('activate'), sock); await vpn.waitForCondition(async () => { - sentToClient(new ExtensionMessage('status'), sock); + sentToClient(makeMessage('status'), sock); const msg = await readResponseOfType('status', messagePipe); return msg.status.vpn === 'StateOnPartial'; }); @@ -53,16 +54,16 @@ describe('WebExtension API', function() { it('A Webextension can deactivate the VPN if it Self Activated', async () => { const sock = await connectExtension(); const messagePipe = getMessageStream(sock); - sentToClient(new ExtensionMessage('activate'), sock); + sentToClient(makeMessage('activate'), sock); await readResponseOfType('activate', messagePipe); await vpn.waitForCondition(async () => { - sentToClient(new ExtensionMessage('status'), sock); + sentToClient(makeMessage('status'), sock); const msg = await readResponseOfType('status', messagePipe); return msg.status.vpn === 'StateOnPartial'; }); - sentToClient(new ExtensionMessage('deactivate'), sock); + sentToClient(makeMessage('deactivate'), sock); await vpn.waitForCondition(async () => { - sentToClient(new ExtensionMessage('status'), sock); + sentToClient(makeMessage('status'), sock); const msg = await readResponseOfType('status', messagePipe); return msg.status.vpn === 'StateOff'; }); @@ -81,15 +82,15 @@ describe('WebExtension API', function() { const messagePipe = getMessageStream(sock); await vpn.waitForCondition(async () => { - sentToClient(new ExtensionMessage('status'), sock); + sentToClient(makeMessage('status'), sock); const msg = await readResponseOfType('status', messagePipe); return msg.status.vpn === 'StateOn'; }, 500, 'VPN should report StateOn'); - sentToClient(new ExtensionMessage('deactivate'), sock); + sentToClient(makeMessage('deactivate'), sock); await vpn.wait(200); await vpn.waitForCondition(async () => { - sentToClient(new ExtensionMessage('status'), sock); + sentToClient(makeMessage('status'), sock); const msg = await readResponseOfType('status', messagePipe); return msg.status.vpn === 'StateOn'; }, 500, 'VPN should still report StateOn'); @@ -101,7 +102,7 @@ describe('WebExtension API', function() { const messagePipe = getMessageStream(sock); const response = readResponseOfType('featurelist', messagePipe); - sentToClient(new ExtensionMessage('featurelist'), sock); + sentToClient(makeMessage('featurelist'), sock); const msg = await response; assert(msg.featurelist.webExtension === true); assert(msg.featurelist.hasOwnProperty('localProxy')); @@ -117,6 +118,27 @@ describe('WebExtension API', function() { assert(msg.status.connectionHealth == "Unstable", "The extension was notified of the instability: "+ msg.status.connectionHealth) sock.destroy(); }); + + it('A Webextension can read settings', async () => { + const sock = await connectExtension(); + const messagePipe = getMessageStream(sock); + const settingsPromise = readResponseOfType('settings', messagePipe); + sentToClient(makeMessage('settings'), sock); + const msg = await settingsPromise + assert(Object.keys(msg.settings).length != 0, "The Extension was sent setting values"); + sock.destroy(); + }); + it('A Webextension can write settings', async () => { + const sock = await connectExtension(); + const messagePipe = getMessageStream(sock); + const settingsPromise = readResponseOfType('settings', messagePipe); + sentToClient(makeMessage('settings', { + settings: {"extensionTelemetryEnabled":true} + }), sock); + const msg = await settingsPromise + assert(msg.settings.extensionTelemetryEnabled, "The Extension was able to set the TelemetrySetting"); + sock.destroy(); + }); }); } diff --git a/tests/functional/utils/webextension.js b/tests/functional/utils/webextension.js index 731a9f58a1..033bd57e60 100644 --- a/tests/functional/utils/webextension.js +++ b/tests/functional/utils/webextension.js @@ -4,13 +4,15 @@ const {Buffer} = require('node:buffer'); const SERVER_PORT = 8754; +class ExtensionMessage{ + t= "" +} -function ExtensionMessage(aT) { - if (!aT) { - this.t = 'help' - return; +function makeMessage(aT, extra) { + return { + ...extra, + t: aT } - this.t = aT }; /** @@ -98,5 +100,6 @@ module.exports = { sentToClient, connectExtension, ExtensionMessage, - readResponseOfType + readResponseOfType, + makeMessage, }