mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 08:19:52 +03:00
first commit using PsychicHttp
This commit is contained in:
@@ -2,8 +2,10 @@
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
APSettingsService::APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
APSettingsService::APSettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager)
|
||||
, _httpEndpoint(APSettings::read, APSettings::update, this, server, AP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(APSettings::read, APSettings::update, this, fs, AP_SETTINGS_FILE)
|
||||
, _dnsServer(nullptr)
|
||||
, _lastManaged(0)
|
||||
@@ -19,6 +21,10 @@ void APSettingsService::begin() {
|
||||
// reconfigureAP();
|
||||
}
|
||||
|
||||
void APSettingsService::registerURI() {
|
||||
_httpEndpoint.registerURI();
|
||||
}
|
||||
|
||||
// wait 10 sec on STA disconnect before starting AP
|
||||
void APSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
uint8_t was_connected = _connected;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
#include <JsonUtils.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <DNSServer.h>
|
||||
#include <IPAddress.h>
|
||||
@@ -118,17 +119,21 @@ class APSettings {
|
||||
|
||||
class APSettingsService : public StatefulService<APSettings> {
|
||||
public:
|
||||
APSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
APSettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
void loop();
|
||||
void registerURI();
|
||||
|
||||
void begin();
|
||||
void loop();
|
||||
APNetworkStatus getAPNetworkStatus();
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
HttpEndpoint<APSettings> _httpEndpoint;
|
||||
FSPersistence<APSettings> _fsPersistence;
|
||||
|
||||
// for the captive portal
|
||||
DNSServer * _dnsServer;
|
||||
|
||||
// for the management delay loop
|
||||
|
||||
@@ -2,22 +2,26 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
APStatus::APStatus(AsyncWebServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService)
|
||||
: _apSettingsService(apSettingsService) {
|
||||
server->on(AP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
APStatus::APStatus(PsychicHttpServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService)
|
||||
: _apSettingsService(apSettingsService)
|
||||
, _server(server)
|
||||
, _securityManager(securityManager) {
|
||||
}
|
||||
|
||||
void APStatus::apStatus(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_AP_STATUS_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
void APStatus::registerURI() {
|
||||
_server->on(AP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
_securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
esp_err_t APStatus::apStatus(PsychicRequest * request) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_AP_STATUS_SIZE);
|
||||
JsonObject root = response.getRoot();
|
||||
|
||||
root["status"] = _apSettingsService->getAPNetworkStatus();
|
||||
root["ip_address"] = WiFi.softAPIP().toString();
|
||||
root["mac_address"] = WiFi.softAPmacAddress();
|
||||
root["station_num"] = WiFi.softAPgetStationNum();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return response.send();
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
#define APStatus_h
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <IPAddress.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <APSettingsService.h>
|
||||
@@ -15,11 +14,16 @@
|
||||
|
||||
class APStatus {
|
||||
public:
|
||||
APStatus(AsyncWebServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService);
|
||||
APStatus(PsychicHttpServer * server, SecurityManager * securityManager, APSettingsService * apSettingsService);
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
APSettingsService * _apSettingsService;
|
||||
void apStatus(AsyncWebServerRequest * request);
|
||||
|
||||
esp_err_t apStatus(PsychicRequest * request);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,46 +2,31 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
AuthenticationService::AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager)
|
||||
, _signInHandler(SIGN_IN_PATH, std::bind(&AuthenticationService::signIn, this, _1, _2)) {
|
||||
server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, std::bind(&AuthenticationService::verifyAuthorization, this, _1));
|
||||
_signInHandler.setMethod(HTTP_POST);
|
||||
_signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE);
|
||||
server->addHandler(&_signInHandler);
|
||||
AuthenticationService::AuthenticationService(PsychicHttpServer * server, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the request supplied a valid JWT.
|
||||
*/
|
||||
void AuthenticationService::verifyAuthorization(AsyncWebServerRequest * request) {
|
||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||
request->send(authentication.authenticated ? 200 : 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in
|
||||
* subsequent requests.
|
||||
*/
|
||||
void AuthenticationService::signIn(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
String username = json["username"];
|
||||
String password = json["password"];
|
||||
Authentication authentication = _securityManager->authenticate(username, password);
|
||||
if (authentication.authenticated) {
|
||||
User * user = authentication.user;
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_AUTHENTICATION_SIZE);
|
||||
JsonObject jsonObject = response->getRoot();
|
||||
jsonObject["access_token"] = _securityManager->generateJWT(user);
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
void AuthenticationService::registerURI() {
|
||||
// Signs in a user if the username and password match. Provides a JWT to be used in the Authorization header in subsequent requests
|
||||
_server->on(SIGN_IN_PATH, HTTP_POST, [this](PsychicRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
String username = json["username"];
|
||||
String password = json["password"];
|
||||
Authentication authentication = _securityManager->authenticate(username, password);
|
||||
if (authentication.authenticated) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, 256);
|
||||
JsonObject root = response.getRoot();
|
||||
root["access_token"] = _securityManager->generateJWT(authentication.user);
|
||||
return response.send();
|
||||
}
|
||||
}
|
||||
}
|
||||
AsyncWebServerResponse * response = request->beginResponse(401);
|
||||
request->send(response);
|
||||
}
|
||||
return request->reply(401);
|
||||
});
|
||||
|
||||
#endif
|
||||
// Verifies that the request supplied a valid JWT
|
||||
_server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, [this](PsychicRequest * request) {
|
||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||
return request->reply(authentication.authenticated ? 200 : 401);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,29 +2,20 @@
|
||||
#define AuthenticationService_H_
|
||||
|
||||
#include <Features.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization"
|
||||
#define SIGN_IN_PATH "/rest/signIn"
|
||||
|
||||
#define MAX_AUTHENTICATION_SIZE 256
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
class AuthenticationService {
|
||||
public:
|
||||
AuthenticationService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
AuthenticationService(PsychicHttpServer * server, SecurityManager * securityManager);
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
AsyncCallbackJsonWebHandler _signInHandler;
|
||||
|
||||
// endpoint functions
|
||||
void signIn(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
void verifyAuthorization(AsyncWebServerRequest * request);
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#include <ESP8266React.h>
|
||||
|
||||
#include <WWWData.h>
|
||||
|
||||
ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
: _featureService(server)
|
||||
, _securitySettingsService(server, fs)
|
||||
, _networkSettingsService(server, fs, &_securitySettingsService)
|
||||
, _wifiScanner(server, &_securitySettingsService)
|
||||
ESP8266React::ESP8266React(PsychicHttpServer * server, FS * fs)
|
||||
: _networkSettingsService(server, fs, &_securitySettingsService)
|
||||
, _networkStatus(server, &_securitySettingsService)
|
||||
, _featureService(server)
|
||||
, _securitySettingsService(server, fs)
|
||||
, _wifiScanner(server, &_securitySettingsService)
|
||||
, _apSettingsService(server, fs, &_securitySettingsService)
|
||||
, _apStatus(server, &_securitySettingsService, &_apSettingsService)
|
||||
, _ntpSettingsService(server, fs, &_securitySettingsService)
|
||||
@@ -20,40 +18,11 @@ ESP8266React::ESP8266React(AsyncWebServer * server, FS * fs)
|
||||
, _restartService(server, &_securitySettingsService)
|
||||
, _factoryResetService(server, fs, &_securitySettingsService)
|
||||
, _systemStatus(server, &_securitySettingsService) {
|
||||
// Serve static resources from PROGMEM
|
||||
WWWData::registerRoutes([server, this](const String & uri, const String & contentType, const uint8_t * content, size_t len) {
|
||||
ArRequestHandlerFunction requestHandler = [contentType, content, len](AsyncWebServerRequest * request) {
|
||||
AsyncWebServerResponse * response = request->beginResponse_P(200, contentType, content, len);
|
||||
response->addHeader("Content-Encoding", "gzip");
|
||||
// response->addHeader("Content-Encoding", "br"); // only works over HTTPS
|
||||
request->send(response);
|
||||
};
|
||||
server->on(uri.c_str(), HTTP_GET, requestHandler);
|
||||
// Serving non matching get requests with "/index.html"
|
||||
// OPTIONS get a straight up 200 response
|
||||
if (uri.equals("/index.html")) {
|
||||
server->onNotFound([requestHandler](AsyncWebServerRequest * request) {
|
||||
if (request->method() == HTTP_GET) {
|
||||
requestHandler(request);
|
||||
} else if (request->method() == HTTP_OPTIONS) {
|
||||
request->send(200);
|
||||
} else {
|
||||
request->send(404);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// register services
|
||||
void ESP8266React::begin() {
|
||||
_networkSettingsService.begin();
|
||||
_networkSettingsService.read([&](NetworkSettings & networkSettings) {
|
||||
if (networkSettings.enableCORS) {
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", networkSettings.CORSOrigin);
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
|
||||
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true");
|
||||
}
|
||||
});
|
||||
_apSettingsService.begin();
|
||||
_ntpSettingsService.begin();
|
||||
_otaSettingsService.begin();
|
||||
@@ -61,6 +30,35 @@ void ESP8266React::begin() {
|
||||
_securitySettingsService.begin();
|
||||
}
|
||||
|
||||
// create the web server endpoints
|
||||
void ESP8266React::registerURI() {
|
||||
_featureService.registerURI();
|
||||
_authenticationService.registerURI();
|
||||
_systemStatus.registerURI();
|
||||
|
||||
_networkSettingsService.registerURI();
|
||||
_networkStatus.registerURI();
|
||||
|
||||
_apSettingsService.registerURI();
|
||||
_apStatus.registerURI();
|
||||
|
||||
_ntpSettingsService.registerURI();
|
||||
_ntpStatus.registerURI();
|
||||
|
||||
_mqttSettingsService.registerURI();
|
||||
_mqttStatus.registerURI();
|
||||
|
||||
_securitySettingsService.registerURI();
|
||||
_otaSettingsService.registerURI();
|
||||
|
||||
_restartService.registerURI();
|
||||
_factoryResetService.registerURI();
|
||||
|
||||
_wifiScanner.registerURI();
|
||||
|
||||
_uploadFileService.registerURI();
|
||||
}
|
||||
|
||||
void ESP8266React::loop() {
|
||||
_networkSettingsService.loop();
|
||||
_apSettingsService.loop();
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#define ESP8266React_h
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <AsyncTCP.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <FeaturesService.h>
|
||||
@@ -26,10 +24,11 @@
|
||||
|
||||
class ESP8266React {
|
||||
public:
|
||||
ESP8266React(AsyncWebServer * server, FS * fs);
|
||||
ESP8266React(PsychicHttpServer * server, FS * fs);
|
||||
|
||||
void begin();
|
||||
void loop();
|
||||
void registerURI();
|
||||
|
||||
SecurityManager * getSecurityManager() {
|
||||
return &_securitySettingsService;
|
||||
|
||||
@@ -63,6 +63,10 @@ class FSPersistence {
|
||||
// serialize it to filesystem
|
||||
File settingsFile = _fs->open(_filePath, "w");
|
||||
|
||||
#ifdef EMSESP_DEBUG
|
||||
Serial.println("Writing settings to " + String(_filePath));
|
||||
#endif
|
||||
|
||||
// failed to open file, return false
|
||||
if (!settingsFile || !jsonObject.size()) {
|
||||
return false;
|
||||
|
||||
@@ -2,21 +2,24 @@
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
FactoryResetService::FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: fs(fs) {
|
||||
server->on(FACTORY_RESET_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
FactoryResetService::FactoryResetService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager) {
|
||||
}
|
||||
|
||||
void FactoryResetService::handleRequest(AsyncWebServerRequest * request) {
|
||||
request->onDisconnect(std::bind(&FactoryResetService::factoryReset, this));
|
||||
request->send(200);
|
||||
void FactoryResetService::registerURI() {
|
||||
_server->on(FACTORY_RESET_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
_securityManager->wrapRequest(std::bind(&FactoryResetService::handleRequest, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete function assumes that all files are stored flat, within the config directory.
|
||||
*/
|
||||
esp_err_t FactoryResetService::handleRequest(PsychicRequest * request) {
|
||||
request->reply(200);
|
||||
RestartService::restartNow();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// Delete function assumes that all files are stored flat, within the config directory.
|
||||
void FactoryResetService::factoryReset() {
|
||||
// TODO To replaced with fs.rmdir(FS_CONFIG_DIRECTORY) now we're using IDF 4.2
|
||||
File root = fs->open(FS_CONFIG_DIRECTORY);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define FactoryResetService_h
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <RestartService.h>
|
||||
#include <FS.h>
|
||||
@@ -14,12 +14,16 @@ class FactoryResetService {
|
||||
FS * fs;
|
||||
|
||||
public:
|
||||
FactoryResetService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
FactoryResetService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void registerURI();
|
||||
void factoryReset();
|
||||
|
||||
private:
|
||||
void handleRequest(AsyncWebServerRequest * request);
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
esp_err_t handleRequest(PsychicRequest * request);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#ifndef Features_h
|
||||
#define Features_h
|
||||
|
||||
// modified by Proddy
|
||||
|
||||
#define FT_ENABLED(feature) feature
|
||||
|
||||
// project feature on by default
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#include <FeaturesService.h>
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
FeaturesService::FeaturesService(AsyncWebServer * server) {
|
||||
server->on(FEATURES_SERVICE_PATH, HTTP_GET, std::bind(&FeaturesService::features, this, _1));
|
||||
FeaturesService::FeaturesService(PsychicHttpServer * server)
|
||||
: _server(server) {
|
||||
}
|
||||
|
||||
void FeaturesService::features(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
void FeaturesService::registerURI() {
|
||||
// return feature set
|
||||
_server->on(FEATURES_SERVICE_PATH, HTTP_GET, [this](PsychicRequest * request) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, 256);
|
||||
JsonObject root = response.getRoot();
|
||||
|
||||
root["version"] = EMSESP_APP_VERSION;
|
||||
root["platform"] = EMSESP_PLATFORM;
|
||||
root["version"] = EMSESP_APP_VERSION;
|
||||
root["platform"] = EMSESP_PLATFORM;
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return response.send();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
|
||||
#define MAX_FEATURES_SIZE 256
|
||||
#define FEATURES_SERVICE_PATH "/rest/features"
|
||||
|
||||
class FeaturesService {
|
||||
public:
|
||||
FeaturesService(AsyncWebServer * server);
|
||||
FeaturesService(PsychicHttpServer * server);
|
||||
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
void features(AsyncWebServerRequest * request);
|
||||
PsychicHttpServer * _server;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
|
||||
#include <SecurityManager.h>
|
||||
#include <StatefulService.h>
|
||||
|
||||
#define HTTP_ENDPOINT_ORIGIN_ID "http"
|
||||
#define HTTPS_ENDPOINT_ORIGIN_ID "https"
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
@@ -17,40 +18,43 @@ class HttpGetEndpoint {
|
||||
public:
|
||||
HttpGetEndpoint(JsonStateReader<T> stateReader,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const String & servicePath,
|
||||
PsychicHttpServer * server,
|
||||
const char * servicePath,
|
||||
SecurityManager * securityManager,
|
||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: _stateReader(stateReader)
|
||||
, _statefulService(statefulService)
|
||||
, _bufferSize(bufferSize) {
|
||||
server->on(servicePath.c_str(), HTTP_GET, securityManager->wrapRequest(std::bind(&HttpGetEndpoint::fetchSettings, this, _1), authenticationPredicate));
|
||||
}
|
||||
|
||||
HttpGetEndpoint(JsonStateReader<T> stateReader,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const String & servicePath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: _stateReader(stateReader)
|
||||
, _statefulService(statefulService)
|
||||
, _bufferSize(bufferSize) {
|
||||
server->on(servicePath.c_str(), HTTP_GET, std::bind(&HttpGetEndpoint::fetchSettings, this, _1));
|
||||
, _bufferSize(bufferSize)
|
||||
, _server(server)
|
||||
, _servicePath(servicePath)
|
||||
, _securityManager(securityManager)
|
||||
, _authenticationPredicate(authenticationPredicate) {
|
||||
}
|
||||
|
||||
protected:
|
||||
JsonStateReader<T> _stateReader;
|
||||
StatefulService<T> * _statefulService;
|
||||
size_t _bufferSize;
|
||||
JsonStateReader<T> _stateReader;
|
||||
StatefulService<T> * _statefulService;
|
||||
size_t _bufferSize;
|
||||
PsychicHttpServer * _server;
|
||||
const char * _servicePath;
|
||||
SecurityManager * _securityManager;
|
||||
AuthenticationPredicate _authenticationPredicate;
|
||||
|
||||
void fetchSettings(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, _bufferSize);
|
||||
JsonObject jsonObject = response->getRoot().to<JsonObject>();
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
void registerURI() {
|
||||
#ifdef EMSESP_DEBUG
|
||||
ESP_LOGE("HttpGetEndpoint", "Addding GET endpoint %s", _servicePath);
|
||||
#endif
|
||||
_server->on(_servicePath,
|
||||
HTTP_GET,
|
||||
_securityManager->wrapRequest(
|
||||
[this](PsychicRequest * request) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, _bufferSize);
|
||||
JsonObject jsonObject = response.getRoot();
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
return response.send();
|
||||
},
|
||||
_authenticationPredicate));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -60,63 +64,67 @@ class HttpPostEndpoint {
|
||||
HttpPostEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const String & servicePath,
|
||||
PsychicHttpServer * server,
|
||||
const char * servicePath,
|
||||
SecurityManager * securityManager,
|
||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: _stateReader(stateReader)
|
||||
, _stateUpdater(stateUpdater)
|
||||
, _statefulService(statefulService)
|
||||
, _updateHandler(servicePath, securityManager->wrapCallback(std::bind(&HttpPostEndpoint::updateSettings, this, _1, _2), authenticationPredicate), bufferSize)
|
||||
, _server(server)
|
||||
, _servicePath(servicePath)
|
||||
, _securityManager(securityManager)
|
||||
, _authenticationPredicate(authenticationPredicate)
|
||||
, _bufferSize(bufferSize) {
|
||||
_updateHandler.setMethod(HTTP_POST);
|
||||
server->addHandler(&_updateHandler);
|
||||
}
|
||||
|
||||
HttpPostEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const String & servicePath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: _stateReader(stateReader)
|
||||
, _stateUpdater(stateUpdater)
|
||||
, _statefulService(statefulService)
|
||||
, _updateHandler(servicePath, std::bind(&HttpPostEndpoint::updateSettings, this, _1, _2), bufferSize)
|
||||
, _bufferSize(bufferSize) {
|
||||
_updateHandler.setMethod(HTTP_POST);
|
||||
server->addHandler(&_updateHandler);
|
||||
}
|
||||
|
||||
protected:
|
||||
JsonStateReader<T> _stateReader;
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
StatefulService<T> * _statefulService;
|
||||
AsyncCallbackJsonWebHandler _updateHandler;
|
||||
size_t _bufferSize;
|
||||
JsonStateReader<T> _stateReader;
|
||||
JsonStateUpdater<T> _stateUpdater;
|
||||
StatefulService<T> * _statefulService;
|
||||
size_t _bufferSize;
|
||||
SecurityManager * _securityManager;
|
||||
AuthenticationPredicate _authenticationPredicate;
|
||||
PsychicHttpServer * _server;
|
||||
const char * _servicePath;
|
||||
|
||||
void updateSettings(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
if (!json.is<JsonObject>()) {
|
||||
request->send(400);
|
||||
return;
|
||||
}
|
||||
JsonObject jsonObject = json.as<JsonObject>();
|
||||
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
||||
if (outcome == StateUpdateResult::ERROR) {
|
||||
request->send(400);
|
||||
return;
|
||||
} else if ((outcome == StateUpdateResult::CHANGED) || (outcome == StateUpdateResult::CHANGED_RESTART)) {
|
||||
request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); });
|
||||
}
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, _bufferSize);
|
||||
jsonObject = response->getRoot().to<JsonObject>();
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
if (outcome == StateUpdateResult::CHANGED_RESTART) {
|
||||
response->setCode(205); // reboot required
|
||||
}
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
void registerURI() {
|
||||
#ifdef EMSESP_DEBUG
|
||||
ESP_LOGE("HttpPostEndpoint", "Addding POST endpoint %s", _servicePath);
|
||||
#endif
|
||||
_server->on(_servicePath,
|
||||
HTTP_POST,
|
||||
_securityManager->wrapCallback(
|
||||
[this](PsychicRequest * request, JsonVariant & json) {
|
||||
if (!json.is<JsonObject>()) {
|
||||
return request->reply(400);
|
||||
}
|
||||
|
||||
JsonObject jsonObject = json.as<JsonObject>();
|
||||
StateUpdateResult outcome = _statefulService->updateWithoutPropagation(jsonObject, _stateUpdater);
|
||||
|
||||
if (outcome == StateUpdateResult::ERROR) {
|
||||
return request->reply(400);
|
||||
} else if ((outcome == StateUpdateResult::CHANGED) || (outcome == StateUpdateResult::CHANGED_RESTART)) {
|
||||
// TODO see if this works as intended. Before the stat was updated on an onDisconnect
|
||||
// request->onDisconnect([this]() { _statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); });
|
||||
// TODO add https
|
||||
_statefulService->callUpdateHandlers(HTTP_ENDPOINT_ORIGIN_ID); // persist the changes to the FS
|
||||
}
|
||||
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, _bufferSize);
|
||||
jsonObject = response.getRoot();
|
||||
|
||||
_statefulService->read(jsonObject, _stateReader);
|
||||
|
||||
if (outcome == StateUpdateResult::CHANGED_RESTART) {
|
||||
return request->reply(205); // reboot required
|
||||
}
|
||||
return response.send();
|
||||
},
|
||||
_authenticationPredicate));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -126,8 +134,8 @@ class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> {
|
||||
HttpEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const String & servicePath,
|
||||
PsychicHttpServer * server,
|
||||
const char * servicePath,
|
||||
SecurityManager * securityManager,
|
||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
@@ -135,14 +143,10 @@ class HttpEndpoint : public HttpGetEndpoint<T>, public HttpPostEndpoint<T> {
|
||||
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, securityManager, authenticationPredicate, bufferSize) {
|
||||
}
|
||||
|
||||
HttpEndpoint(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
const String & servicePath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: HttpGetEndpoint<T>(stateReader, statefulService, server, servicePath, bufferSize)
|
||||
, HttpPostEndpoint<T>(stateReader, stateUpdater, statefulService, server, servicePath, bufferSize) {
|
||||
// register the web server on() endpoints
|
||||
void registerURI() {
|
||||
HttpGetEndpoint<T>::registerURI();
|
||||
HttpPostEndpoint<T>::registerURI();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -24,8 +24,10 @@ static char * retainCstr(const char * cstr, char ** ptr) {
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
MqttSettingsService::MqttSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(MqttSettings::read, MqttSettings::update, this, server, MQTT_SETTINGS_SERVICE_PATH, securityManager)
|
||||
MqttSettingsService::MqttSettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager)
|
||||
, _httpEndpoint(MqttSettings::read, MqttSettings::update, this, server, MQTT_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(MqttSettings::read, MqttSettings::update, this, fs, MQTT_SETTINGS_FILE)
|
||||
, _retainedHost(nullptr)
|
||||
, _retainedClientId(nullptr)
|
||||
@@ -47,6 +49,10 @@ void MqttSettingsService::begin() {
|
||||
startClient();
|
||||
}
|
||||
|
||||
void MqttSettingsService::registerURI() {
|
||||
_httpEndpoint.registerURI();
|
||||
}
|
||||
|
||||
void MqttSettingsService::startClient() {
|
||||
static bool isSecure = false;
|
||||
if (_mqttClient != nullptr) {
|
||||
@@ -218,8 +224,10 @@ bool MqttSettingsService::configureMqtt() {
|
||||
|
||||
void MqttSettings::read(MqttSettings & settings, JsonObject & root) {
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
#ifndef TASMOTA_SDK
|
||||
root["enableTLS"] = settings.enableTLS;
|
||||
root["rootCA"] = settings.rootCA;
|
||||
#endif
|
||||
#endif
|
||||
root["enabled"] = settings.enabled;
|
||||
root["host"] = settings.host;
|
||||
@@ -255,8 +263,10 @@ StateUpdateResult MqttSettings::update(JsonObject & root, MqttSettings & setting
|
||||
bool changed = false;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
#ifndef TASMOTA_SDK
|
||||
newSettings.enableTLS = root["enableTLS"] | false;
|
||||
newSettings.rootCA = root["rootCA"] | "";
|
||||
#endif
|
||||
#endif
|
||||
newSettings.enabled = root["enabled"] | FACTORY_MQTT_ENABLED;
|
||||
newSettings.host = root["host"] | FACTORY_MQTT_HOST;
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <ESPUtils.h>
|
||||
|
||||
#include <uuid/common.h>
|
||||
#include <esp_wps.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#define MQTT_RECONNECTION_DELAY 2000 // 2 seconds
|
||||
|
||||
@@ -103,12 +105,14 @@ class MqttSettings {
|
||||
|
||||
class MqttSettingsService : public StatefulService<MqttSettings> {
|
||||
public:
|
||||
MqttSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
MqttSettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager);
|
||||
~MqttSettingsService();
|
||||
|
||||
void begin();
|
||||
void startClient();
|
||||
void loop();
|
||||
void begin();
|
||||
void startClient();
|
||||
void loop();
|
||||
void registerURI();
|
||||
|
||||
bool isEnabled();
|
||||
bool isConnected();
|
||||
const char * getClientId();
|
||||
@@ -120,6 +124,9 @@ class MqttSettingsService : public StatefulService<MqttSettings> {
|
||||
void onConfigUpdated();
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
HttpEndpoint<MqttSettings> _httpEndpoint;
|
||||
FSPersistence<MqttSettings> _fsPersistence;
|
||||
|
||||
|
||||
@@ -4,16 +4,21 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
MqttStatus::MqttStatus(AsyncWebServer * server, MqttSettingsService * mqttSettingsService, SecurityManager * securityManager)
|
||||
: _mqttSettingsService(mqttSettingsService) {
|
||||
server->on(MQTT_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&MqttStatus::mqttStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
MqttStatus::MqttStatus(PsychicHttpServer * server, MqttSettingsService * mqttSettingsService, SecurityManager * securityManager)
|
||||
: _mqttSettingsService(mqttSettingsService)
|
||||
, _server(server)
|
||||
, _securityManager(securityManager) {
|
||||
}
|
||||
|
||||
void MqttStatus::mqttStatus(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_MQTT_STATUS_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
void MqttStatus::registerURI() {
|
||||
_server->on(MQTT_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
_securityManager->wrapRequest(std::bind(&MqttStatus::mqttStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
esp_err_t MqttStatus::mqttStatus(PsychicRequest * request) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_MQTT_STATUS_SIZE);
|
||||
JsonObject root = response.getRoot();
|
||||
|
||||
root["enabled"] = _mqttSettingsService->isEnabled();
|
||||
root["connected"] = _mqttSettingsService->isConnected();
|
||||
@@ -24,6 +29,5 @@ void MqttStatus::mqttStatus(AsyncWebServerRequest * request) {
|
||||
root["mqtt_fails"] = emsesp::Mqtt::publish_fails();
|
||||
root["connect_count"] = emsesp::Mqtt::connect_count();
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return response.send();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <WiFi.h>
|
||||
#include <MqttSettingsService.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define MAX_MQTT_STATUS_SIZE 1024
|
||||
@@ -12,12 +12,16 @@
|
||||
|
||||
class MqttStatus {
|
||||
public:
|
||||
MqttStatus(AsyncWebServer * server, MqttSettingsService * mqttSettingsService, SecurityManager * securityManager);
|
||||
MqttStatus(PsychicHttpServer * server, MqttSettingsService * mqttSettingsService, SecurityManager * securityManager);
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
MqttSettingsService * _mqttSettingsService;
|
||||
|
||||
void mqttStatus(AsyncWebServerRequest * request);
|
||||
esp_err_t mqttStatus(PsychicRequest * request);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,16 +4,12 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
NTPSettingsService::NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE)
|
||||
, _timeHandler(TIME_PATH, securityManager->wrapCallback(std::bind(&NTPSettingsService::configureTime, this, _1, _2), AuthenticationPredicates::IS_ADMIN)) {
|
||||
_timeHandler.setMethod(HTTP_POST);
|
||||
_timeHandler.setMaxContentLength(MAX_TIME_SIZE);
|
||||
server->addHandler(&_timeHandler);
|
||||
|
||||
NTPSettingsService::NTPSettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager)
|
||||
, _httpEndpoint(NTPSettings::read, NTPSettings::update, this, server, NTP_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(NTPSettings::read, NTPSettings::update, this, fs, NTP_SETTINGS_FILE) {
|
||||
WiFi.onEvent(std::bind(&NTPSettingsService::WiFiEvent, this, _1));
|
||||
|
||||
addUpdateHandler([&](const String & originId) { configureNTP(); }, false);
|
||||
}
|
||||
|
||||
@@ -22,6 +18,14 @@ void NTPSettingsService::begin() {
|
||||
configureNTP();
|
||||
}
|
||||
|
||||
void NTPSettingsService::registerURI() {
|
||||
_httpEndpoint.registerURI();
|
||||
|
||||
_server->on(TIME_PATH,
|
||||
HTTP_POST,
|
||||
_securityManager->wrapCallback(std::bind(&NTPSettingsService::configureTime, this, _1, _2), AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
// handles both WiFI and Ethernet
|
||||
void NTPSettingsService::WiFiEvent(WiFiEvent_t event) {
|
||||
switch (event) {
|
||||
@@ -61,7 +65,7 @@ void NTPSettingsService::configureNTP() {
|
||||
}
|
||||
}
|
||||
|
||||
void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
esp_err_t NTPSettingsService::configureTime(PsychicRequest * request, JsonVariant & json) {
|
||||
if (json.is<JsonObject>()) {
|
||||
struct tm tm = {0};
|
||||
String timeLocal = json["local_time"];
|
||||
@@ -71,14 +75,11 @@ void NTPSettingsService::configureTime(AsyncWebServerRequest * request, JsonVari
|
||||
time_t time = mktime(&tm);
|
||||
struct timeval now = {.tv_sec = time};
|
||||
settimeofday(&now, nullptr);
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
return;
|
||||
return request->reply(200);
|
||||
}
|
||||
}
|
||||
|
||||
AsyncWebServerResponse * response = request->beginResponse(400);
|
||||
request->send(response);
|
||||
return request->reply(400);
|
||||
}
|
||||
|
||||
void NTPSettingsService::ntp_received(struct timeval * tv) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef NTPSettingsService_h
|
||||
#define NTPSettingsService_h
|
||||
|
||||
#include <WiFi.h>
|
||||
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
|
||||
@@ -26,7 +28,6 @@
|
||||
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
|
||||
#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings"
|
||||
|
||||
#define MAX_TIME_SIZE 256
|
||||
#define TIME_PATH "/rest/time"
|
||||
|
||||
class NTPSettings {
|
||||
@@ -54,20 +55,26 @@ class NTPSettings {
|
||||
|
||||
class NTPSettingsService : public StatefulService<NTPSettings> {
|
||||
public:
|
||||
NTPSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
NTPSettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
void registerURI();
|
||||
|
||||
void begin();
|
||||
static void ntp_received(struct timeval * tv);
|
||||
|
||||
private:
|
||||
HttpEndpoint<NTPSettings> _httpEndpoint;
|
||||
FSPersistence<NTPSettings> _fsPersistence;
|
||||
AsyncCallbackJsonWebHandler _timeHandler;
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
HttpEndpoint<NTPSettings> _httpEndpoint;
|
||||
FSPersistence<NTPSettings> _fsPersistence;
|
||||
|
||||
bool connected_ = false;
|
||||
void WiFiEvent(WiFiEvent_t event);
|
||||
void configureNTP();
|
||||
void configureTime(AsyncWebServerRequest * request, JsonVariant & json);
|
||||
|
||||
// POST
|
||||
esp_err_t configureTime(PsychicRequest * request, JsonVariant & json);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,10 +3,16 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
NTPStatus::NTPStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(NTP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
NTPStatus::NTPStatus(PsychicHttpServer * server, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager) {
|
||||
}
|
||||
|
||||
|
||||
void NTPStatus::registerURI() {
|
||||
_server->on(NTP_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
_securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -28,9 +34,9 @@ String toLocalTimeString(tm * time) {
|
||||
return formatTime(time, "%FT%T");
|
||||
}
|
||||
|
||||
void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
esp_err_t NTPStatus::ntpStatus(PsychicRequest * request) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_NTP_STATUS_SIZE);
|
||||
JsonObject root = response.getRoot();
|
||||
|
||||
// grab the current instant in unix seconds
|
||||
time_t now = time(nullptr);
|
||||
@@ -47,6 +53,5 @@ void NTPStatus::ntpStatus(AsyncWebServerRequest * request) {
|
||||
// the sntp server name
|
||||
root["server"] = esp_sntp_getservername(0);
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return response.send();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <esp_sntp.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <uuid/common.h>
|
||||
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
class NTPStatus {
|
||||
public:
|
||||
NTPStatus(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
NTPStatus(PsychicHttpServer * server, SecurityManager * securityManager);
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
void ntpStatus(AsyncWebServerRequest * request);
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
esp_err_t ntpStatus(PsychicRequest * request);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
NetworkSettingsService::NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
NetworkSettingsService::NetworkSettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(NetworkSettings::read, NetworkSettings::update, this, server, NETWORK_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(NetworkSettings::read, NetworkSettings::update, this, fs, NETWORK_SETTINGS_FILE)
|
||||
, _lastConnectionAttempt(0) {
|
||||
@@ -23,14 +23,18 @@ void NetworkSettingsService::begin() {
|
||||
|
||||
WiFi.mode(WIFI_MODE_MAX);
|
||||
WiFi.mode(WIFI_MODE_NULL);
|
||||
|
||||
// WiFi.setScanMethod(WIFI_ALL_CHANNEL_SCAN); // default is FAST_SCAN, connect issues in 2.0.14
|
||||
// WiFi.setSortMethod(WIFI_CONNECT_AP_BY_SIGNAL); // is default, no need to set
|
||||
|
||||
|
||||
_fsPersistence.readFromFS();
|
||||
// reconfigureWiFiConnection();
|
||||
}
|
||||
|
||||
void NetworkSettingsService::registerURI() {
|
||||
_httpEndpoint.registerURI();
|
||||
}
|
||||
|
||||
void NetworkSettingsService::reconfigureWiFiConnection() {
|
||||
// do not disconnect for switching to eth, restart is needed
|
||||
if (WiFi.isConnected() && _state.ssid.length() == 0) {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
class NetworkSettings {
|
||||
public:
|
||||
// core wifi configuration
|
||||
// core network configuration
|
||||
String ssid;
|
||||
String bssid;
|
||||
String password;
|
||||
@@ -119,12 +119,15 @@ class NetworkSettings {
|
||||
|
||||
class NetworkSettingsService : public StatefulService<NetworkSettings> {
|
||||
public:
|
||||
NetworkSettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
NetworkSettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
void loop();
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
HttpEndpoint<NetworkSettings> _httpEndpoint;
|
||||
FSPersistence<NetworkSettings> _fsPersistence;
|
||||
unsigned long _lastConnectionAttempt;
|
||||
|
||||
@@ -4,15 +4,20 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
NetworkStatus::NetworkStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(NETWORK_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&NetworkStatus::networkStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
NetworkStatus::NetworkStatus(PsychicHttpServer * server, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager) {
|
||||
}
|
||||
|
||||
void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_NETWORK_STATUS_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
void NetworkStatus::registerURI() {
|
||||
_server->on(NETWORK_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
_securityManager->wrapRequest(std::bind(&NetworkStatus::networkStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
esp_err_t NetworkStatus::networkStatus(PsychicRequest * request) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_NETWORK_STATUS_SIZE);
|
||||
JsonObject root = response.getRoot();
|
||||
|
||||
bool ethernet_connected = emsesp::EMSESP::system_.ethernet_connected();
|
||||
wl_status_t wifi_status = WiFi.status();
|
||||
@@ -64,6 +69,5 @@ void NetworkStatus::networkStatus(AsyncWebServerRequest * request) {
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return response.send();
|
||||
}
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
#define NetworkStatus_h
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
|
||||
#include <ETH.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <IPAddress.h>
|
||||
#include <IPUtils.h>
|
||||
#include <SecurityManager.h>
|
||||
@@ -17,10 +16,14 @@
|
||||
|
||||
class NetworkStatus {
|
||||
public:
|
||||
NetworkStatus(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
NetworkStatus(PsychicHttpServer * server, SecurityManager * securityManager);
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
void networkStatus(AsyncWebServerRequest * request);
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
esp_err_t networkStatus(PsychicRequest * request);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
OTASettingsService::OTASettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _httpEndpoint(OTASettings::read, OTASettings::update, this, server, OTA_SETTINGS_SERVICE_PATH, securityManager)
|
||||
OTASettingsService::OTASettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager)
|
||||
, _httpEndpoint(OTASettings::read, OTASettings::update, this, server, OTA_SETTINGS_SERVICE_PATH, securityManager)
|
||||
, _fsPersistence(OTASettings::read, OTASettings::update, this, fs, OTA_SETTINGS_FILE)
|
||||
, _arduinoOTA(nullptr) {
|
||||
WiFi.onEvent(std::bind(&OTASettingsService::WiFiEvent, this, _1, _2));
|
||||
@@ -17,6 +19,10 @@ void OTASettingsService::begin() {
|
||||
configureArduinoOTA();
|
||||
}
|
||||
|
||||
void OTASettingsService::registerURI() {
|
||||
_httpEndpoint.registerURI();
|
||||
}
|
||||
|
||||
void OTASettingsService::loop() {
|
||||
if (_state.enabled && _arduinoOTA) {
|
||||
_arduinoOTA->handle();
|
||||
|
||||
@@ -44,15 +44,19 @@ class OTASettings {
|
||||
|
||||
class OTASettingsService : public StatefulService<OTASettings> {
|
||||
public:
|
||||
OTASettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager);
|
||||
OTASettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager);
|
||||
|
||||
void begin();
|
||||
void loop();
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
HttpEndpoint<OTASettings> _httpEndpoint;
|
||||
FSPersistence<OTASettings> _fsPersistence;
|
||||
ArduinoOTAClass * _arduinoOTA;
|
||||
|
||||
ArduinoOTAClass * _arduinoOTA;
|
||||
|
||||
void configureArduinoOTA();
|
||||
void WiFiEvent(WiFiEvent_t event, WiFiEventInfo_t info);
|
||||
|
||||
@@ -5,41 +5,49 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
RestartService::RestartService(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(RESTART_SERVICE_PATH, HTTP_POST, securityManager->wrapRequest(std::bind(&RestartService::restart, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(PARTITION_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
securityManager->wrapRequest(std::bind(&RestartService::partition, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
RestartService::RestartService(PsychicHttpServer * server, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager) {
|
||||
}
|
||||
|
||||
void RestartService::restart(AsyncWebServerRequest * request) {
|
||||
void RestartService::registerURI() {
|
||||
_server->on(RESTART_SERVICE_PATH, HTTP_POST, _securityManager->wrapRequest(std::bind(&RestartService::restart, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
_server->on(PARTITION_SERVICE_PATH,
|
||||
HTTP_POST,
|
||||
_securityManager->wrapRequest(std::bind(&RestartService::partition, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
esp_err_t RestartService::restart(PsychicRequest * request) {
|
||||
emsesp::EMSESP::system_.store_nvs_values();
|
||||
request->onDisconnect(RestartService::restartNow);
|
||||
request->send(200);
|
||||
request->reply(200);
|
||||
restartNow();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void RestartService::partition(AsyncWebServerRequest * request) {
|
||||
esp_err_t RestartService::partition(PsychicRequest * request) {
|
||||
const esp_partition_t * factory_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
|
||||
if (factory_partition) {
|
||||
esp_ota_set_boot_partition(factory_partition);
|
||||
emsesp::EMSESP::system_.store_nvs_values();
|
||||
request->onDisconnect(RestartService::restartNow);
|
||||
request->send(200);
|
||||
return;
|
||||
request->reply(200);
|
||||
restartNow();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const esp_partition_t * ota_partition = esp_ota_get_next_update_partition(NULL);
|
||||
if (!ota_partition) {
|
||||
request->send(400); // bad request
|
||||
return;
|
||||
return request->reply(400); // bad request
|
||||
}
|
||||
|
||||
uint64_t buffer;
|
||||
esp_partition_read(ota_partition, 0, &buffer, 8);
|
||||
if (buffer == 0xFFFFFFFFFFFFFFFF) { // partition empty
|
||||
request->send(400); // bad request
|
||||
return;
|
||||
return request->reply(400); // bad request
|
||||
}
|
||||
|
||||
esp_ota_set_boot_partition(ota_partition);
|
||||
emsesp::EMSESP::system_.store_nvs_values();
|
||||
request->onDisconnect(RestartService::restartNow);
|
||||
request->send(200);
|
||||
request->reply(200);
|
||||
restartNow();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
#define RestartService_h
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define RESTART_SERVICE_PATH "/rest/restart"
|
||||
@@ -12,17 +11,21 @@
|
||||
|
||||
class RestartService {
|
||||
public:
|
||||
RestartService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
RestartService(PsychicHttpServer * server, SecurityManager * securityManager);
|
||||
|
||||
void registerURI();
|
||||
|
||||
static void restartNow() {
|
||||
WiFi.disconnect(true);
|
||||
delay(500);
|
||||
delay(500); // wait for async tcp to catch up
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
private:
|
||||
void restart(AsyncWebServerRequest * request);
|
||||
void partition(AsyncWebServerRequest * request);
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
esp_err_t restart(PsychicRequest * request);
|
||||
esp_err_t partition(PsychicRequest * request);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
|
||||
#include <Features.h>
|
||||
#include <ArduinoJsonJWT.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <ESPUtils.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <list>
|
||||
|
||||
#ifndef FACTORY_JWT_SECRET
|
||||
@@ -70,7 +69,6 @@ class AuthenticationPredicates {
|
||||
|
||||
class SecurityManager {
|
||||
public:
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
/*
|
||||
* Authenticate, returning the user if found
|
||||
*/
|
||||
@@ -81,27 +79,25 @@ class SecurityManager {
|
||||
*/
|
||||
virtual String generateJWT(User * user) = 0;
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check the request header for the Authorization token
|
||||
*/
|
||||
virtual Authentication authenticateRequest(AsyncWebServerRequest * request) = 0;
|
||||
virtual Authentication authenticateRequest(PsychicRequest * request) = 0;
|
||||
|
||||
/**
|
||||
* Filter a request with the provided predicate, only returning true if the predicate matches.
|
||||
*/
|
||||
virtual ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0;
|
||||
virtual PsychicRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0;
|
||||
|
||||
/**
|
||||
* Wrap the provided request to provide validation against an AuthenticationPredicate.
|
||||
*/
|
||||
virtual ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) = 0;
|
||||
virtual PsychicHttpRequestCallback wrapRequest(PsychicHttpRequestCallback onRequest, AuthenticationPredicate predicate) = 0;
|
||||
|
||||
/**
|
||||
* Wrap the provided json request callback to provide validation against an AuthenticationPredicate.
|
||||
*/
|
||||
virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) = 0;
|
||||
virtual PsychicJsonRequestCallback wrapCallback(PsychicJsonRequestCallback onRequest, AuthenticationPredicate predicate) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
#include <SecuritySettingsService.h>
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
#include "../../src/emsesp_stub.hpp"
|
||||
|
||||
SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs)
|
||||
: _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this)
|
||||
SecuritySettingsService::SecuritySettingsService(PsychicHttpServer * server, FS * fs)
|
||||
: _server(server)
|
||||
, _httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this)
|
||||
, _fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE)
|
||||
, _jwtHandler(FACTORY_JWT_SECRET) {
|
||||
addUpdateHandler([&](const String & originId) { configureJWTHandler(); }, false);
|
||||
server->on(GENERATE_TOKEN_PATH,
|
||||
HTTP_GET,
|
||||
wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
void SecuritySettingsService::begin() {
|
||||
@@ -19,17 +15,20 @@ void SecuritySettingsService::begin() {
|
||||
configureJWTHandler();
|
||||
}
|
||||
|
||||
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest * request) {
|
||||
AsyncWebHeader * authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
|
||||
if (authorizationHeader) {
|
||||
String value = authorizationHeader->value();
|
||||
void SecuritySettingsService::registerURI() {
|
||||
_httpEndpoint.registerURI();
|
||||
_server->on(GENERATE_TOKEN_PATH, HTTP_GET, wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
}
|
||||
|
||||
Authentication SecuritySettingsService::authenticateRequest(PsychicRequest * request) {
|
||||
if (request->hasHeader(AUTHORIZATION_HEADER)) {
|
||||
auto value = request->header(AUTHORIZATION_HEADER);
|
||||
if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) {
|
||||
value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN);
|
||||
return authenticateJWT(value);
|
||||
}
|
||||
} else if (request->hasParam(ACCESS_TOKEN_PARAMATER)) {
|
||||
AsyncWebParameter * tokenParamater = request->getParam(ACCESS_TOKEN_PARAMATER);
|
||||
String value = tokenParamater->value();
|
||||
String value = request->getParam(ACCESS_TOKEN_PARAMATER)->value();
|
||||
return authenticateJWT(value);
|
||||
}
|
||||
return Authentication();
|
||||
@@ -82,76 +81,42 @@ String SecuritySettingsService::generateJWT(User * user) {
|
||||
return _jwtHandler.buildJWT(payload);
|
||||
}
|
||||
|
||||
ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) {
|
||||
return [this, predicate](AsyncWebServerRequest * request) {
|
||||
PsychicRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) {
|
||||
return [this, predicate](PsychicRequest * request) {
|
||||
Authentication authentication = authenticateRequest(request);
|
||||
return predicate(authentication);
|
||||
};
|
||||
}
|
||||
|
||||
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
|
||||
return [this, onRequest, predicate](AsyncWebServerRequest * request) {
|
||||
PsychicHttpRequestCallback SecuritySettingsService::wrapRequest(PsychicHttpRequestCallback onRequest, AuthenticationPredicate predicate) {
|
||||
return [this, onRequest, predicate](PsychicRequest * request) {
|
||||
Authentication authentication = authenticateRequest(request);
|
||||
if (!predicate(authentication)) {
|
||||
request->send(401);
|
||||
return;
|
||||
return request->reply(401);
|
||||
}
|
||||
onRequest(request);
|
||||
return onRequest(request);
|
||||
};
|
||||
}
|
||||
|
||||
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
|
||||
return [this, onRequest, predicate](AsyncWebServerRequest * request, JsonVariant & json) {
|
||||
PsychicJsonRequestCallback SecuritySettingsService::wrapCallback(PsychicJsonRequestCallback onRequest, AuthenticationPredicate predicate) {
|
||||
return [this, onRequest, predicate](PsychicRequest * request, JsonVariant & json) {
|
||||
Authentication authentication = authenticateRequest(request);
|
||||
if (!predicate(authentication)) {
|
||||
request->send(401);
|
||||
return;
|
||||
return request->reply(401);
|
||||
}
|
||||
onRequest(request, json);
|
||||
return onRequest(request, json);
|
||||
};
|
||||
}
|
||||
|
||||
void SecuritySettingsService::generateToken(AsyncWebServerRequest * request) {
|
||||
AsyncWebParameter * usernameParam = request->getParam("username");
|
||||
esp_err_t SecuritySettingsService::generateToken(PsychicRequest * request) {
|
||||
String usernameParam = request->getParam("username")->value();
|
||||
for (User _user : _state.users) {
|
||||
if (_user.username == usernameParam->value()) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, GENERATE_TOKEN_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
if (_user.username == usernameParam) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, GENERATE_TOKEN_SIZE);
|
||||
JsonObject root = response.getRoot();
|
||||
root["token"] = generateJWT(&_user);
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
return response.send();
|
||||
}
|
||||
}
|
||||
request->send(401);
|
||||
return request->reply(401);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
|
||||
|
||||
SecuritySettingsService::SecuritySettingsService(AsyncWebServer * server, FS * fs)
|
||||
: SecurityManager() {
|
||||
}
|
||||
SecuritySettingsService::~SecuritySettingsService() {
|
||||
}
|
||||
|
||||
ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) {
|
||||
return [this, predicate](AsyncWebServerRequest * request) { return true; };
|
||||
}
|
||||
|
||||
// Return the admin user on all request - disabling security features
|
||||
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest * request) {
|
||||
return Authentication(ADMIN_USER);
|
||||
}
|
||||
|
||||
// Return the function unwrapped
|
||||
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
|
||||
return onRequest;
|
||||
}
|
||||
|
||||
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
|
||||
return onRequest;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
#define GENERATE_TOKEN_SIZE 512
|
||||
#define GENERATE_TOKEN_PATH "/rest/generateToken"
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
class SecuritySettings {
|
||||
public:
|
||||
String jwtSecret;
|
||||
@@ -69,51 +67,33 @@ class SecuritySettings {
|
||||
|
||||
class SecuritySettingsService : public StatefulService<SecuritySettings>, public SecurityManager {
|
||||
public:
|
||||
SecuritySettingsService(AsyncWebServer * server, FS * fs);
|
||||
SecuritySettingsService(PsychicHttpServer * server, FS * fs);
|
||||
|
||||
void begin();
|
||||
void registerURI();
|
||||
|
||||
// Functions to implement SecurityManager
|
||||
Authentication authenticate(const String & username, const String & password);
|
||||
Authentication authenticateRequest(AsyncWebServerRequest * request);
|
||||
String generateJWT(User * user);
|
||||
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate);
|
||||
Authentication authenticate(const String & username, const String & password);
|
||||
Authentication authenticateRequest(PsychicRequest * request);
|
||||
String generateJWT(User * user);
|
||||
|
||||
PsychicRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
|
||||
PsychicHttpRequestCallback wrapRequest(PsychicHttpRequestCallback onRequest, AuthenticationPredicate predicate);
|
||||
PsychicJsonRequestCallback wrapCallback(PsychicJsonRequestCallback onRequest, AuthenticationPredicate predicate);
|
||||
|
||||
private:
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
HttpEndpoint<SecuritySettings> _httpEndpoint;
|
||||
FSPersistence<SecuritySettings> _fsPersistence;
|
||||
ArduinoJsonJWT _jwtHandler;
|
||||
|
||||
void generateToken(AsyncWebServerRequest * request);
|
||||
esp_err_t generateToken(PsychicRequest * request);
|
||||
|
||||
void configureJWTHandler();
|
||||
|
||||
/*
|
||||
* Lookup the user by JWT
|
||||
*/
|
||||
Authentication authenticateJWT(String & jwt);
|
||||
|
||||
/*
|
||||
* Verify the payload is correct
|
||||
*/
|
||||
boolean validatePayload(JsonObject & parsedPayload, User * user);
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
class SecuritySettingsService : public SecurityManager {
|
||||
public:
|
||||
SecuritySettingsService(AsyncWebServer * server, FS * fs);
|
||||
~SecuritySettingsService();
|
||||
|
||||
// minimal set of functions to support framework with security settings disabled
|
||||
Authentication authenticateRequest(AsyncWebServerRequest * request);
|
||||
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
Authentication authenticateJWT(String & jwt); // Lookup the user by JWT
|
||||
boolean validatePayload(JsonObject & parsedPayload, User * user); // Verify the payload is correct
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -5,24 +5,34 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
SystemStatus::SystemStatus(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(SYSTEM_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
SystemStatus::SystemStatus(PsychicHttpServer * server, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager) {
|
||||
}
|
||||
|
||||
void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
|
||||
void SystemStatus::registerURI() {
|
||||
emsesp::EMSESP::system_.refreshHeapMem(); // refresh free heap and max alloc heap
|
||||
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
_server->on(SYSTEM_STATUS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
_securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, _1), AuthenticationPredicates::IS_AUTHENTICATED));
|
||||
}
|
||||
|
||||
esp_err_t SystemStatus::systemStatus(PsychicRequest * request) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_ESP_STATUS_SIZE);
|
||||
JsonObject root = response.getRoot();
|
||||
|
||||
root["emsesp_version"] = EMSESP_APP_VERSION;
|
||||
root["esp_platform"] = EMSESP_PLATFORM;
|
||||
root["cpu_type"] = ESP.getChipModel();
|
||||
root["cpu_rev"] = ESP.getChipRevision();
|
||||
root["cpu_cores"] = ESP.getChipCores();
|
||||
root["cpu_freq_mhz"] = ESP.getCpuFreqMHz();
|
||||
root["max_alloc_heap"] = emsesp::EMSESP::system_.getMaxAllocMem();
|
||||
root["free_heap"] = emsesp::EMSESP::system_.getHeapMem();
|
||||
root["arduino_version"] = ARDUINO_VERSION;
|
||||
root["sdk_version"] = ESP.getSdkVersion();
|
||||
root["partition"] = esp_ota_get_running_partition()->label;
|
||||
root["flash_chip_size"] = ESP.getFlashChipSize() / 1024;
|
||||
root["flash_chip_speed"] = ESP.getFlashChipSpeed();
|
||||
root["app_used"] = emsesp::EMSESP::system_.appUsed();
|
||||
@@ -32,14 +42,17 @@ void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
|
||||
root["fs_free"] = emsesp::EMSESP::system_.FStotal() - FSused;
|
||||
root["uptime"] = uuid::log::format_timestamp_ms(uuid::get_uptime_ms(), 3);
|
||||
|
||||
// TODO add buid time with __TIME__ and __DATE__
|
||||
|
||||
if (emsesp::EMSESP::system_.PSram()) {
|
||||
root["psram_size"] = emsesp::EMSESP::system_.PSram();
|
||||
root["free_psram"] = ESP.getFreePsram() / 1024;
|
||||
}
|
||||
|
||||
const esp_partition_t * partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
|
||||
if (partition != NULL) { // factory partition found
|
||||
root["has_loader"] = true;
|
||||
} else { // check for not empty, smaller OTA partition
|
||||
} else { // check for not empty, smaller OTA partition
|
||||
partition = esp_ota_get_next_update_partition(NULL);
|
||||
if (partition) {
|
||||
uint64_t buffer;
|
||||
@@ -49,6 +62,5 @@ void SystemStatus::systemStatus(AsyncWebServerRequest * request) {
|
||||
}
|
||||
}
|
||||
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return response.send();
|
||||
}
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
#define SystemStatus_h
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define MAX_ESP_STATUS_SIZE 1024
|
||||
@@ -15,10 +14,14 @@
|
||||
|
||||
class SystemStatus {
|
||||
public:
|
||||
SystemStatus(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
SystemStatus(PsychicHttpServer * server, SecurityManager * securityManager);
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
void systemStatus(AsyncWebServerRequest * request);
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
esp_err_t systemStatus(PsychicRequest * request);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,25 +6,34 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
static bool is_firmware = false;
|
||||
static char md5[33] = "\0";
|
||||
static char md5[33] = "\0";
|
||||
|
||||
UploadFileService::UploadFileService(AsyncWebServer * server, SecurityManager * securityManager)
|
||||
: _securityManager(securityManager) {
|
||||
server->on(UPLOAD_FILE_PATH,
|
||||
HTTP_POST,
|
||||
std::bind(&UploadFileService::uploadComplete, this, _1),
|
||||
std::bind(&UploadFileService::handleUpload, this, _1, _2, _3, _4, _5, _6));
|
||||
static FileType fileType = ft_none;
|
||||
|
||||
UploadFileService::UploadFileService(PsychicHttpServer * server, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager) {
|
||||
}
|
||||
|
||||
void UploadFileService::handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final) {
|
||||
void UploadFileService::registerURI() {
|
||||
_server->maxUploadSize = 2300000; // 2.3 MB
|
||||
|
||||
PsychicUploadHandler * uploadHandler = new PsychicUploadHandler();
|
||||
|
||||
uploadHandler->onUpload(std::bind(&UploadFileService::handleUpload, this, _1, _2, _3, _4, _5, _6));
|
||||
uploadHandler->onRequest(std::bind(&UploadFileService::uploadComplete, this, _1)); //gets called after upload has been handled
|
||||
_server->on(UPLOAD_FILE_PATH, HTTP_POST, uploadHandler);
|
||||
}
|
||||
|
||||
esp_err_t UploadFileService::handleUpload(PsychicRequest * request, const String & filename, uint64_t index, uint8_t * data, size_t len, bool final) {
|
||||
// quit if not authorized
|
||||
Authentication authentication = _securityManager->authenticateRequest(request);
|
||||
if (!AuthenticationPredicates::IS_ADMIN(authentication)) {
|
||||
handleError(request, 403); // send the forbidden response
|
||||
return;
|
||||
return handleError(request, 403); // forbidden
|
||||
}
|
||||
|
||||
File jsonFile;
|
||||
|
||||
// at init
|
||||
if (!index) {
|
||||
// check details of the file, to see if its a valid bin or json file
|
||||
@@ -33,44 +42,41 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
std::string extension = fname.substr(position + 1);
|
||||
size_t fsize = request->contentLength();
|
||||
|
||||
is_firmware = false;
|
||||
fileType = ft_none;
|
||||
if ((extension == "bin") && (fsize > 1000000)) {
|
||||
is_firmware = true;
|
||||
fileType = ft_firmware;
|
||||
} else if (extension == "json") {
|
||||
md5[0] = '\0'; // clear md5
|
||||
fileType = ft_json;
|
||||
md5[0] = '\0'; // clear md5
|
||||
} else if (extension == "md5") {
|
||||
fileType = ft_md5;
|
||||
if (len == 32) {
|
||||
memcpy(md5, data, 32);
|
||||
md5[32] = '\0';
|
||||
}
|
||||
return;
|
||||
return ESP_OK;
|
||||
} else {
|
||||
md5[0] = '\0';
|
||||
handleError(request, 406); // Not Acceptable - unsupported file type
|
||||
return;
|
||||
return handleError(request, 406); // Not Acceptable - unsupported file type
|
||||
}
|
||||
|
||||
if (is_firmware) {
|
||||
if (fileType == ft_firmware) {
|
||||
// Check firmware header, 0xE9 magic offset 0 indicates esp bin, chip offset 12: esp32:0, S2:2, C3:5
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
#if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4
|
||||
if (len > 12 && (data[0] != 0xE9 || data[12] != 0)) {
|
||||
handleError(request, 503); // service unavailable
|
||||
return;
|
||||
return handleError(request, 503); // service unavailable
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
if (len > 12 && (data[0] != 0xE9 || data[12] != 2)) {
|
||||
handleError(request, 503); // service unavailable
|
||||
return;
|
||||
return handleError(request, 503); // service unavailable
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
if (len > 12 && (data[0] != 0xE9 || data[12] != 5)) {
|
||||
handleError(request, 503); // service unavailable
|
||||
return;
|
||||
return handleError(request, 503); // service unavailable
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
if (len > 12 && (data[0] != 0xE9 || data[12] != 9)) {
|
||||
handleError(request, 503); // service unavailable
|
||||
return;
|
||||
return handleError(request, 503); // service unavailable
|
||||
}
|
||||
#endif
|
||||
// it's firmware - initialize the ArduinoOTA updater
|
||||
@@ -79,21 +85,19 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
Update.setMD5(md5);
|
||||
md5[0] = '\0';
|
||||
}
|
||||
request->onDisconnect(UploadFileService::handleEarlyDisconnect); // success, let's make sure we end the update if the client hangs up
|
||||
} else {
|
||||
handleError(request, 507); // failed to begin, send an error response Insufficient Storage
|
||||
return;
|
||||
return handleError(request, 507); // failed to begin, send an error response Insufficient Storage
|
||||
}
|
||||
} else {
|
||||
// its a normal file, open a new temp file to write the contents too
|
||||
request->_tempFile = LittleFS.open(TEMP_FILENAME_PATH, "w");
|
||||
jsonFile = LittleFS.open(TEMP_FILENAME_PATH, FILE_WRITE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_firmware) {
|
||||
if (fileType == ft_json) {
|
||||
if (len) {
|
||||
if (len != request->_tempFile.write(data, len)) { // stream the incoming chunk to the opened file
|
||||
handleError(request, 507); // 507-Insufficient Storage
|
||||
if (len != jsonFile.write(data, len)) { // stream the incoming chunk to the opened file
|
||||
return handleError(request, 507); // failed to write chunk to file, send an error response Insufficient Storage
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -109,60 +113,42 @@ void UploadFileService::handleUpload(AsyncWebServerRequest * request, const Stri
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void UploadFileService::uploadComplete(AsyncWebServerRequest * request) {
|
||||
// did we complete uploading a json file?
|
||||
if (request->_tempFile) {
|
||||
request->_tempFile.close(); // close the file handle as the upload is now done
|
||||
emsesp::EMSESP::system_.store_nvs_values();
|
||||
request->onDisconnect(RestartService::restartNow);
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
return;
|
||||
esp_err_t UploadFileService::uploadComplete(PsychicRequest * request) {
|
||||
// did we complete uploading a json file? no need to close the file
|
||||
if (fileType == ft_md5) {
|
||||
if (strlen(md5) == 32) {
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, 256);
|
||||
JsonObject root = response.getRoot();
|
||||
root["md5"] = md5;
|
||||
return response.send();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// check if it was a firmware upgrade
|
||||
// if no error, send the success response as a JSON
|
||||
if (is_firmware && !request->_tempObject) {
|
||||
emsesp::EMSESP::system_.store_nvs_values();
|
||||
request->onDisconnect(RestartService::restartNow);
|
||||
AsyncWebServerResponse * response = request->beginResponse(200);
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(md5) == 32) {
|
||||
auto * response = new AsyncJsonResponse(false, 256);
|
||||
JsonObject root = response->getRoot();
|
||||
root["md5"] = md5;
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
handleError(request, 500);
|
||||
// store and restart regardless of whether it worked or not
|
||||
emsesp::EMSESP::system_.store_nvs_values();
|
||||
request->reply(200);
|
||||
RestartService::restartNow();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void UploadFileService::handleError(AsyncWebServerRequest * request, int code) {
|
||||
esp_err_t UploadFileService::handleError(PsychicRequest * request, int code) {
|
||||
// if we have had an error already, do nothing
|
||||
if (request->_tempObject) {
|
||||
return;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
// send the error code to the client and record the error code in the temp object
|
||||
AsyncWebServerResponse * response = request->beginResponse(code);
|
||||
request->send(response);
|
||||
|
||||
// check for invalid extension and immediately kill the connection, which will through an error
|
||||
// that is caught by the web code. Unfortunately the http error code is not sent to the client on fast network connections
|
||||
if (code == 406) {
|
||||
request->client()->close(true);
|
||||
handleEarlyDisconnect();
|
||||
request->client()->close();
|
||||
fileType = ft_none;
|
||||
Update.abort();
|
||||
}
|
||||
}
|
||||
|
||||
void UploadFileService::handleEarlyDisconnect() {
|
||||
is_firmware = false;
|
||||
Update.abort();
|
||||
return request->reply(code);
|
||||
}
|
||||
|
||||
@@ -8,23 +8,28 @@
|
||||
|
||||
#include <LittleFS.h>
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <RestartService.h>
|
||||
|
||||
#define UPLOAD_FILE_PATH "/rest/uploadFile"
|
||||
#define TEMP_FILENAME_PATH "/tmp_upload"
|
||||
|
||||
enum FileType { ft_none = 0, ft_firmware = 1, ft_md5 = 2, ft_json = 3 };
|
||||
|
||||
class UploadFileService {
|
||||
public:
|
||||
UploadFileService(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
UploadFileService(PsychicHttpServer * server, SecurityManager * securityManager);
|
||||
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
SecurityManager * _securityManager;
|
||||
void handleUpload(AsyncWebServerRequest * request, const String & filename, size_t index, uint8_t * data, size_t len, bool final);
|
||||
void uploadComplete(AsyncWebServerRequest * request);
|
||||
void handleError(AsyncWebServerRequest * request, int code);
|
||||
static void handleEarlyDisconnect();
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
esp_err_t handleUpload(PsychicRequest * request, const String & filename, uint64_t index, uint8_t * data, size_t len, bool final);
|
||||
esp_err_t uploadComplete(PsychicRequest * request);
|
||||
esp_err_t handleError(PsychicRequest * request, int code);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define WebSocketTxRx_h
|
||||
|
||||
#include <StatefulService.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define WEB_SOCKET_CLIENT_ID_MSG_SIZE 128
|
||||
@@ -16,12 +16,12 @@ template <class T>
|
||||
class WebSocketConnector {
|
||||
protected:
|
||||
StatefulService<T> * _statefulService;
|
||||
AsyncWebServer * _server;
|
||||
PsychicHttpServer * _server;
|
||||
AsyncWebSocket _webSocket;
|
||||
size_t _bufferSize;
|
||||
|
||||
WebSocketConnector(StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
PsychicHttpServer * server,
|
||||
const char * webSocketPath,
|
||||
SecurityManager * securityManager,
|
||||
AuthenticationPredicate authenticationPredicate,
|
||||
@@ -36,7 +36,7 @@ class WebSocketConnector {
|
||||
_server->on(webSocketPath, HTTP_GET, std::bind(&WebSocketConnector::forbidden, this, _1));
|
||||
}
|
||||
|
||||
WebSocketConnector(StatefulService<T> * statefulService, AsyncWebServer * server, const char * webSocketPath, size_t bufferSize)
|
||||
WebSocketConnector(StatefulService<T> * statefulService, PsychicHttpServer * server, const char * webSocketPath, size_t bufferSize)
|
||||
: _statefulService(statefulService)
|
||||
, _server(server)
|
||||
, _webSocket(webSocketPath)
|
||||
@@ -52,7 +52,7 @@ class WebSocketConnector {
|
||||
}
|
||||
|
||||
private:
|
||||
void forbidden(AsyncWebServerRequest * request) {
|
||||
void forbidden(PsychicRequest * request) {
|
||||
request->send(403);
|
||||
}
|
||||
};
|
||||
@@ -62,7 +62,7 @@ class WebSocketTx : virtual public WebSocketConnector<T> {
|
||||
public:
|
||||
WebSocketTx(JsonStateReader<T> stateReader,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
PsychicHttpServer * server,
|
||||
const char * webSocketPath,
|
||||
SecurityManager * securityManager,
|
||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
||||
@@ -74,7 +74,7 @@ class WebSocketTx : virtual public WebSocketConnector<T> {
|
||||
|
||||
WebSocketTx(JsonStateReader<T> stateReader,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
PsychicHttpServer * server,
|
||||
const char * webSocketPath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
|
||||
@@ -140,7 +140,7 @@ class WebSocketRx : virtual public WebSocketConnector<T> {
|
||||
public:
|
||||
WebSocketRx(JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
PsychicHttpServer * server,
|
||||
const char * webSocketPath,
|
||||
SecurityManager * securityManager,
|
||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
||||
@@ -151,7 +151,7 @@ class WebSocketRx : virtual public WebSocketConnector<T> {
|
||||
|
||||
WebSocketRx(JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
PsychicHttpServer * server,
|
||||
const char * webSocketPath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
|
||||
@@ -185,7 +185,7 @@ class WebSocketTxRx : public WebSocketTx<T>, public WebSocketRx<T> {
|
||||
WebSocketTxRx(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
PsychicHttpServer * server,
|
||||
const char * webSocketPath,
|
||||
SecurityManager * securityManager,
|
||||
AuthenticationPredicate authenticationPredicate = AuthenticationPredicates::IS_ADMIN,
|
||||
@@ -198,7 +198,7 @@ class WebSocketTxRx : public WebSocketTx<T>, public WebSocketRx<T> {
|
||||
WebSocketTxRx(JsonStateReader<T> stateReader,
|
||||
JsonStateUpdater<T> stateUpdater,
|
||||
StatefulService<T> * statefulService,
|
||||
AsyncWebServer * server,
|
||||
PsychicHttpServer * server,
|
||||
const char * webSocketPath,
|
||||
size_t bufferSize = DEFAULT_BUFFER_SIZE)
|
||||
: WebSocketConnector<T>(statefulService, server, webSocketPath, bufferSize)
|
||||
|
||||
@@ -2,30 +2,36 @@
|
||||
|
||||
using namespace std::placeholders; // for `_1` etc
|
||||
|
||||
WiFiScanner::WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager) {
|
||||
server->on(SCAN_NETWORKS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
server->on(LIST_NETWORKS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
WiFiScanner::WiFiScanner(PsychicHttpServer * server, SecurityManager * securityManager)
|
||||
: _server(server)
|
||||
, _securityManager(securityManager){};
|
||||
|
||||
void WiFiScanner::registerURI() {
|
||||
_server->on(SCAN_NETWORKS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
_securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
_server->on(LIST_NETWORKS_SERVICE_PATH,
|
||||
HTTP_GET,
|
||||
_securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, _1), AuthenticationPredicates::IS_ADMIN));
|
||||
};
|
||||
|
||||
void WiFiScanner::scanNetworks(AsyncWebServerRequest * request) {
|
||||
request->send(202); // special code to indicate scan in progress
|
||||
|
||||
esp_err_t WiFiScanner::scanNetworks(PsychicRequest * request) {
|
||||
if (WiFi.scanComplete() != -1) {
|
||||
WiFi.scanDelete();
|
||||
WiFi.scanNetworks(true);
|
||||
}
|
||||
|
||||
return request->reply(202); // special code to indicate scan in progress
|
||||
}
|
||||
|
||||
void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
|
||||
esp_err_t WiFiScanner::listNetworks(PsychicRequest * request) {
|
||||
int numNetworks = WiFi.scanComplete();
|
||||
if (numNetworks > -1) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_WIFI_SCANNER_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
JsonArray networks = root.createNestedArray("networks");
|
||||
PsychicJsonResponse response = PsychicJsonResponse(request, false, MAX_WIFI_SCANNER_SIZE);
|
||||
JsonObject root = response.getRoot();
|
||||
|
||||
JsonArray networks = root.createNestedArray("networks");
|
||||
for (int i = 0; i < numNetworks; i++) {
|
||||
JsonObject network = networks.createNestedObject();
|
||||
network["rssi"] = WiFi.RSSI(i);
|
||||
@@ -34,11 +40,12 @@ void WiFiScanner::listNetworks(AsyncWebServerRequest * request) {
|
||||
network["channel"] = WiFi.channel(i);
|
||||
network["encryption_type"] = (uint8_t)WiFi.encryptionType(i);
|
||||
}
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
|
||||
return response.send();
|
||||
|
||||
} else if (numNetworks == -1) {
|
||||
request->send(202); // special code to indicate scan in progress
|
||||
return request->reply(202); // special code to indicate scan in progress
|
||||
} else {
|
||||
scanNetworks(request);
|
||||
return scanNetworks(request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
#define WiFiScanner_h
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <PsychicHttp.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define SCAN_NETWORKS_SERVICE_PATH "/rest/scanNetworks"
|
||||
@@ -15,11 +14,16 @@
|
||||
|
||||
class WiFiScanner {
|
||||
public:
|
||||
WiFiScanner(AsyncWebServer * server, SecurityManager * securityManager);
|
||||
WiFiScanner(PsychicHttpServer * server, SecurityManager * securityManager);
|
||||
|
||||
void registerURI();
|
||||
|
||||
private:
|
||||
void scanNetworks(AsyncWebServerRequest * request);
|
||||
void listNetworks(AsyncWebServerRequest * request);
|
||||
SecurityManager * _securityManager;
|
||||
PsychicHttpServer * _server;
|
||||
|
||||
esp_err_t scanNetworks(PsychicRequest * request);
|
||||
esp_err_t listNetworks(PsychicRequest * request);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user