From a7d084206bc11124a008bb5fb87e63a8f5ac9870 Mon Sep 17 00:00:00 2001 From: Maxim Prokhorov Date: Wed, 2 Apr 2025 05:34:33 +0300 Subject: [PATCH] webui(server): export web & ws settings in the respective module cors setup does not have to be in ws, move to web setup routine actually set up ws auth setting, don't just report it in the ui --- code/espurna/web.cpp | 35 +++++++++++++++++++++++++++++----- code/espurna/ws.cpp | 45 +++++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/code/espurna/web.cpp b/code/espurna/web.cpp index 3ee04ffa..59e41aff 100644 --- a/code/espurna/web.cpp +++ b/code/espurna/web.cpp @@ -71,6 +71,8 @@ namespace { namespace build { +STRING_VIEW_INLINE(WebRemoteDomain, WEB_REMOTE_DOMAIN); + constexpr auto DefaultPort = uint16_t{ #if WEB_SSL_ENABLED 443 @@ -96,19 +98,24 @@ namespace keys { STRING_VIEW_INLINE(Prefix, "web"); -STRING_VIEW_INLINE(Port, "webPort"); STRING_VIEW_INLINE(AccessLog, "webAccessLog"); +STRING_VIEW_INLINE(Domain, "webDomain"); +STRING_VIEW_INLINE(Port, "webPort"); } // namespace keys -uint16_t port() { - return getSetting(keys::Port, build::port()); -} - bool access_log() { return getSetting(keys::AccessLog, build::access_log()); } +String domain() { + return getSetting(keys::Domain, build::WebRemoteDomain); +} + +uint16_t port() { + return getSetting(keys::Port, build::port()); +} + } // namespace settings } // namespace @@ -368,6 +375,16 @@ void _onDiscover(AsyncWebServerRequest *request) { request->send(response); } +void _setupAccessControlHeaders() { + const auto domain = espurna::web::settings::domain(); + + auto& headers = DefaultHeaders::Instance(); + headers.addHeader(F("Access-Control-Allow-Origin"), domain); + if (!domain.equals("*")) { + headers.addHeader(F("Access-Control-Allow-Credentials"), F("true")); + } +} + void _addSecurityHeaders(AsyncWebServerResponse* response) { response->addHeader(F("X-XSS-Protection"), F("1; mode=block")); response->addHeader(F("X-Content-Type-Options"), F("nosniff")); @@ -649,6 +666,10 @@ void _onBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t i } +void _onVisible(JsonObject& root) { + root[espurna::web::settings::keys::Port] = _port; +} + bool _onKeyCheck(espurna::StringView key, const JsonVariant& value) { return key.startsWith(espurna::web::settings::keys::Prefix); } @@ -759,8 +780,12 @@ void webSetup() { DEBUG_MSG_P(PSTR("[WEBSERVER] Webserver running on port %hu\n"), _port); + // CORS setup + _setupAccessControlHeaders(); + // Handle ws server settings updates wsRegister() + .onVisible(_onVisible) .onKeyCheck(_onKeyCheck); } diff --git a/code/espurna/ws.cpp b/code/espurna/ws.cpp index 104707a8..46a113b5 100644 --- a/code/espurna/ws.cpp +++ b/code/espurna/ws.cpp @@ -42,16 +42,26 @@ STRING_VIEW_INLINE(SchemaKey, "schema"); namespace build { -constexpr uint16_t port() { - return WEB_PORT; -} - constexpr bool authentication() { return 1 == WS_AUTHENTICATION; } } // namespace build +namespace settings { +namespace keys { + +STRING_VIEW_INLINE(Prefix, "ws"); +STRING_VIEW_INLINE(Auth, "wsAuth"); + +} // namespace keys + +bool authentication() { + return getSetting(keys::Auth, build::authentication()); +} + +} // namespace settings + } // namespace EnumerableConfig::EnumerableConfig(JsonObject& root, StringView name) : @@ -107,7 +117,7 @@ EnumerablePayload::EnumerablePayload(JsonObject& root, StringView name) : _root(root.createNestedObject(name)) {} -void EnumerablePayload::operator()(StringView name, settings::Iota iota, Check check, Pairs&& pairs) { +void EnumerablePayload::operator()(StringView name, espurna::settings::Iota iota, Check check, Pairs&& pairs) { JsonArray& entries = _root.createNestedArray(name); if (_root.containsKey(internal::SchemaKey)) { @@ -154,10 +164,6 @@ void EnumerableTypes::operator()(int value, StringView text) { namespace { -template -struct BaseTimeFormat { -}; - void _wsUpdateAp(JsonObject& root) { IPAddress ip{}; @@ -245,6 +251,8 @@ void _wsDoUpdate(const bool connected) { namespace { +bool _ws_auth { espurna::web::ws::build::authentication() }; + AsyncWebSocket _ws("/ws"); std::queue _ws_queue; ws_callbacks_t _ws_callbacks; @@ -668,7 +676,7 @@ void _wsParse(AsyncWebSocketClient* client, uint8_t* payload, size_t length) { } bool _wsOnKeyCheck(espurna::StringView key, const JsonVariant&) { - return key.startsWith(STRING_VIEW("ws")); + return key.startsWith(espurna::web::ws::settings::keys::Prefix); } void _wsOnConnected(JsonObject& root) { @@ -696,9 +704,10 @@ void _wsOnConnected(JsonObject& root) { root[F("sketch_size")] = ESP.getSketchSize(); root[F("free_size")] = ESP.getFreeSketchSpace(); +} - root[F("webPort")] = getSetting(F("webPort"), espurna::web::ws::build::port()); - root[F("wsAuth")] = getSetting(F("wsAuth"), espurna::web::ws::build::authentication()); +void _wsOnVisible(JsonObject& root) { + root[espurna::web::ws::settings::keys::Auth] = _ws_auth ? 1 : 0; } void _wsConnected(uint32_t client_id) { @@ -726,7 +735,7 @@ void _wsEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType { const auto ip = client->remoteIP().toString(); #ifndef NOWSAUTH - if (!_wsAuth(client)) { + if (_ws_auth && !_wsAuth(client)) { DEBUG_MSG_P(PSTR("[WEBSOCKET] #%u session expired for %s\n"), client->id(), ip.c_str()); client->close(); @@ -936,21 +945,15 @@ void wsSend(uint32_t client_id, const char * payload) { } void wsSetup() { - _ws.onEvent(_wsEvent); webServer().addHandler(&_ws); - // CORS - const String webDomain = getSetting(F("webDomain"), F(WEB_REMOTE_DOMAIN)); - DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Origin"), webDomain); - if (!webDomain.equals("*")) { - DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Credentials"), F("true")); - } - + _ws_auth = espurna::web::ws::settings::authentication(); webServer().on("/auth", HTTP_GET, _onAuth); wsRegister() .onConnected(_wsOnConnected) + .onVisible(_wsOnVisible) .onKeyCheck(_wsOnKeyCheck); espurnaRegisterLoop(_wsLoop);