From fe8b68d2bef03b8c62b908b5ca91477bedfca58d Mon Sep 17 00:00:00 2001 From: Thomas Basler Date: Mon, 7 Nov 2022 18:48:02 +0100 Subject: [PATCH] Password protection for config settings API --- src/WebApi_config.cpp | 17 +++++++++++++++++ webapp/src/router/index.ts | 2 +- webapp/src/views/ConfigAdminView.vue | 28 +++++++++++++++++----------- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/WebApi_config.cpp b/src/WebApi_config.cpp index e437643..fe52265 100644 --- a/src/WebApi_config.cpp +++ b/src/WebApi_config.cpp @@ -6,6 +6,7 @@ #include "ArduinoJson.h" #include "AsyncJson.h" #include "Configuration.h" +#include "WebApi.h" #include void WebApiConfigClass::init(AsyncWebServer* server) @@ -32,11 +33,19 @@ void WebApiConfigClass::loop() void WebApiConfigClass::onConfigGet(AsyncWebServerRequest* request) { + if (!WebApi.checkCredentials(request)) { + return; + } + request->send(LittleFS, CONFIG_FILENAME_JSON, String(), true); } void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request) { + if (!WebApi.checkCredentials(request)) { + return; + } + AsyncJsonResponse* response = new AsyncJsonResponse(); JsonObject retMsg = response->getRoot(); retMsg[F("type")] = F("warning"); @@ -93,6 +102,10 @@ void WebApiConfigClass::onConfigDelete(AsyncWebServerRequest* request) void WebApiConfigClass::onConfigUploadFinish(AsyncWebServerRequest* request) { + if (!WebApi.checkCredentials(request)) { + return; + } + // the request handler is triggered after the upload has finished... // create the response, add header, and send response @@ -108,6 +121,10 @@ void WebApiConfigClass::onConfigUploadFinish(AsyncWebServerRequest* request) void WebApiConfigClass::onConfigUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) { + if (!WebApi.checkCredentials(request)) { + return; + } + if (!index) { // open the file on first call and store the file handle in the request object request->_tempFile = LittleFS.open(CONFIG_FILENAME_JSON, "w"); diff --git a/webapp/src/router/index.ts b/webapp/src/router/index.ts index 4e343a7..166172f 100644 --- a/webapp/src/router/index.ts +++ b/webapp/src/router/index.ts @@ -100,7 +100,7 @@ const router = createRouter({ router.beforeEach((to, from, next) => { // redirect to login page if not logged in and trying to access a restricted page const publicPages = ['/', '/login', '/about', '/info/network', '/info/system', '/info/ntp', '/info/mqtt', - '/firmware/upgrade', '/settings/config', ]; + '/firmware/upgrade', ]; const authRequired = !publicPages.includes(to.path); const loggedIn = localStorage.getItem('user'); diff --git a/webapp/src/views/ConfigAdminView.vue b/webapp/src/views/ConfigAdminView.vue index 6d6f1df..7802ddf 100644 --- a/webapp/src/views/ConfigAdminView.vue +++ b/webapp/src/views/ConfigAdminView.vue @@ -112,6 +112,7 @@ import { } from 'bootstrap-icons-vue'; import * as bootstrap from 'bootstrap'; import BootstrapAlert from "@/components/BootstrapAlert.vue"; +import { handleResponse, authHeader } from '@/utils/authentication'; export default defineComponent({ components: { @@ -152,15 +153,10 @@ export default defineComponent({ fetch("/api/config/delete", { method: "POST", + headers: authHeader(), body: formData, }) - .then(function (response) { - if (response.status != 200) { - throw response.status; - } else { - return response.json(); - } - }) + .then(handleResponse) .then( (response) => { this.alertMessage = response.message; @@ -171,10 +167,17 @@ export default defineComponent({ this.modalFactoryReset.hide(); }, downloadConfig() { - const link = document.createElement('a') - link.href = "/api/config/get" - link.download = 'config.json' - link.click() + fetch("/api/config/get", { headers: authHeader() }) + .then(res => res.blob()) + .then(blob => { + var file = window.URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = file; + a.download = "config.json"; + document.body.appendChild(a); + a.click(); + a.remove(); + }); }, uploadConfig(event: Event | null) { this.uploading = true; @@ -206,6 +209,9 @@ export default defineComponent({ formData.append("config", this.file, "config"); request.open("post", "/api/config/upload"); + authHeader().forEach((value, key) => { + request.setRequestHeader(key, value); + }); request.send(formData); }, clear() {