reformat with .clang-format based on google's spec with some minor changes

This commit is contained in:
Rick Watson 2019-12-03 23:16:06 +00:00
parent 8fb805e0f2
commit f4ae632956
40 changed files with 885 additions and 880 deletions

15
.clang-format Normal file
View File

@ -0,0 +1,15 @@
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 120
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
BinPackArguments: false
BinPackParameters: false
BreakConstructorInitializers: AfterColon
AllowAllParametersOfDeclarationOnNextLine: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ExperimentalAutoDetectBinPacking: false
KeepEmptyLinesAtTheStartOfBlocks: false
DerivePointerAlignment: false

View File

@ -1,8 +1,11 @@
#include <APSettingsService.h> #include <APSettingsService.h>
APSettingsService::APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : AdminSettingsService(server, fs, securityManager, AP_SETTINGS_SERVICE_PATH, AP_SETTINGS_FILE) {} APSettingsService::APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) :
AdminSettingsService(server, fs, securityManager, AP_SETTINGS_SERVICE_PATH, AP_SETTINGS_FILE) {
}
APSettingsService::~APSettingsService() {} APSettingsService::~APSettingsService() {
}
void APSettingsService::begin() { void APSettingsService::begin() {
SettingsService::begin(); SettingsService::begin();
@ -12,10 +15,10 @@ void APSettingsService::begin() {
void APSettingsService::loop() { void APSettingsService::loop() {
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
unsigned long manageElapsed = (unsigned long)(currentMillis - _lastManaged); unsigned long manageElapsed = (unsigned long)(currentMillis - _lastManaged);
if (manageElapsed >= MANAGE_NETWORK_DELAY){ if (manageElapsed >= MANAGE_NETWORK_DELAY) {
_lastManaged = currentMillis; _lastManaged = currentMillis;
manageAP(); manageAP();
} }
handleDNS(); handleDNS();
} }

View File

@ -20,39 +20,34 @@
#define AP_SETTINGS_SERVICE_PATH "/rest/apSettings" #define AP_SETTINGS_SERVICE_PATH "/rest/apSettings"
class APSettingsService : public AdminSettingsService { class APSettingsService : public AdminSettingsService {
public:
APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
~APSettingsService();
public: void begin();
void loop();
APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); protected:
~APSettingsService(); void readFromJsonObject(JsonObject& root);
void writeToJsonObject(JsonObject& root);
void onConfigUpdated();
void begin(); private:
void loop(); // access point settings
uint8_t _provisionMode;
String _ssid;
String _password;
protected: // for the mangement delay loop
unsigned long _lastManaged;
void readFromJsonObject(JsonObject& root); // for the captive portal
void writeToJsonObject(JsonObject& root); DNSServer* _dnsServer;
void onConfigUpdated();
private:
// access point settings
uint8_t _provisionMode;
String _ssid;
String _password;
// for the mangement delay loop
unsigned long _lastManaged;
// for the captive portal
DNSServer *_dnsServer;
void manageAP();
void startAP();
void stopAP() ;
void handleDNS();
void manageAP();
void startAP();
void stopAP();
void handleDNS();
}; };
#endif // end APSettingsConfig_h #endif // end APSettingsConfig_h

View File

@ -1,17 +1,18 @@
#include <APStatus.h> #include <APStatus.h>
APStatus::APStatus(AsyncWebServer* server, SecurityManager* securityManager) { APStatus::APStatus(AsyncWebServer* server, SecurityManager* securityManager) {
server->on(AP_STATUS_SERVICE_PATH, HTTP_GET, server->on(AP_STATUS_SERVICE_PATH,
securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED) HTTP_GET,
); securityManager->wrapRequest(std::bind(&APStatus::apStatus, this, std::placeholders::_1),
AuthenticationPredicates::IS_AUTHENTICATED));
} }
void APStatus::apStatus(AsyncWebServerRequest *request) { void APStatus::apStatus(AsyncWebServerRequest* request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_AP_STATUS_SIZE); AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AP_STATUS_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
WiFiMode_t currentWiFiMode = WiFi.getMode(); WiFiMode_t currentWiFiMode = WiFi.getMode();
root["active"] = (currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA); root["active"] = (currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA);
root["ip_address"] = WiFi.softAPIP().toString(); root["ip_address"] = WiFi.softAPIP().toString();
root["mac_address"] = WiFi.softAPmacAddress(); root["mac_address"] = WiFi.softAPmacAddress();
root["station_num"] = WiFi.softAPgetStationNum(); root["station_num"] = WiFi.softAPgetStationNum();

View File

@ -2,16 +2,16 @@
#define APStatus_h #define APStatus_h
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h> #include <AsyncTCP.h>
#include <AsyncTCP.h> #include <WiFi.h>
#endif #endif
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <ESPAsyncWebServer.h>
#include <IPAddress.h> #include <IPAddress.h>
#include <SecurityManager.h> #include <SecurityManager.h>
@ -19,15 +19,11 @@
#define AP_STATUS_SERVICE_PATH "/rest/apStatus" #define AP_STATUS_SERVICE_PATH "/rest/apStatus"
class APStatus { class APStatus {
public:
APStatus(AsyncWebServer* server, SecurityManager* securityManager);
public: private:
void apStatus(AsyncWebServerRequest* request);
APStatus(AsyncWebServer* server, SecurityManager* securityManager);
private:
void apStatus(AsyncWebServerRequest *request);
}; };
#endif // end APStatus_h #endif // end APStatus_h

View File

@ -4,42 +4,46 @@
#include <SettingsService.h> #include <SettingsService.h>
class AdminSettingsService : public SettingsService { class AdminSettingsService : public SettingsService {
public:
public: AdminSettingsService(AsyncWebServer* server,
AdminSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager, char const* servicePath, char const* filePath): FS* fs,
SettingsService(server, fs, servicePath, filePath), _securityManager(securityManager) {} SecurityManager* securityManager,
char const* servicePath,
char const* filePath) :
SettingsService(server, fs, servicePath, filePath),
_securityManager(securityManager) {
}
protected: protected:
// will validate the requests with the security manager // will validate the requests with the security manager
SecurityManager* _securityManager; SecurityManager* _securityManager;
void fetchConfig(AsyncWebServerRequest *request) { void fetchConfig(AsyncWebServerRequest* request) {
// verify the request against the predicate // verify the request against the predicate
Authentication authentication = _securityManager->authenticateRequest(request); Authentication authentication = _securityManager->authenticateRequest(request);
if (!getAuthenticationPredicate()(authentication)) { if (!getAuthenticationPredicate()(authentication)) {
request->send(401); request->send(401);
return; return;
}
// delegate to underlying implemetation
SettingsService::fetchConfig(request);
} }
// delegate to underlying implemetation
SettingsService::fetchConfig(request);
}
void updateConfig(AsyncWebServerRequest *request, JsonDocument &jsonDocument) { void updateConfig(AsyncWebServerRequest* request, JsonDocument& jsonDocument) {
// verify the request against the predicate // verify the request against the predicate
Authentication authentication = _securityManager->authenticateRequest(request); Authentication authentication = _securityManager->authenticateRequest(request);
if (!getAuthenticationPredicate()(authentication)) { if (!getAuthenticationPredicate()(authentication)) {
request->send(401); request->send(401);
return; return;
}
// delegate to underlying implemetation
SettingsService::updateConfig(request, jsonDocument);
}
// override this to replace the default authentication predicate, IS_ADMIN
AuthenticationPredicate getAuthenticationPredicate() {
return AuthenticationPredicates::IS_ADMIN;
} }
// delegate to underlying implemetation
SettingsService::updateConfig(request, jsonDocument);
}
// override this to replace the default authentication predicate, IS_ADMIN
AuthenticationPredicate getAuthenticationPredicate() {
return AuthenticationPredicates::IS_ADMIN;
}
}; };
#endif // end AdminSettingsService #endif // end AdminSettingsService

View File

@ -1,47 +1,48 @@
#include "ArduinoJsonJWT.h" #include "ArduinoJsonJWT.h"
ArduinoJsonJWT::ArduinoJsonJWT(String secret) : _secret(secret) { } ArduinoJsonJWT::ArduinoJsonJWT(String secret) : _secret(secret) {
}
void ArduinoJsonJWT::setSecret(String secret){ void ArduinoJsonJWT::setSecret(String secret) {
_secret = secret; _secret = secret;
} }
String ArduinoJsonJWT::getSecret(){ String ArduinoJsonJWT::getSecret() {
return _secret; return _secret;
} }
/* /*
* ESP32 uses mbedtls, ESP2866 uses bearssl. * ESP32 uses mbedtls, ESP2866 uses bearssl.
* *
* Both come with decent HMAC implmentations supporting sha256, as well as others. * Both come with decent HMAC implmentations supporting sha256, as well as others.
* *
* No need to pull in additional crypto libraries - lets use what we already have. * No need to pull in additional crypto libraries - lets use what we already have.
*/ */
String ArduinoJsonJWT::sign(String &payload) { String ArduinoJsonJWT::sign(String& payload) {
unsigned char hmacResult[32]; unsigned char hmacResult[32];
{ {
#if defined(ESP_PLATFORM) #if defined(ESP_PLATFORM)
mbedtls_md_context_t ctx; mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256; mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
mbedtls_md_init(&ctx); mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1); mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
mbedtls_md_hmac_starts(&ctx, (unsigned char *) _secret.c_str(), _secret.length()); mbedtls_md_hmac_starts(&ctx, (unsigned char*)_secret.c_str(), _secret.length());
mbedtls_md_hmac_update(&ctx, (unsigned char *) payload.c_str(), payload.length()); mbedtls_md_hmac_update(&ctx, (unsigned char*)payload.c_str(), payload.length());
mbedtls_md_hmac_finish(&ctx, hmacResult); mbedtls_md_hmac_finish(&ctx, hmacResult);
mbedtls_md_free(&ctx); mbedtls_md_free(&ctx);
#else #else
br_hmac_key_context keyCtx; br_hmac_key_context keyCtx;
br_hmac_key_init(&keyCtx, &br_sha256_vtable, _secret.c_str(), _secret.length()); br_hmac_key_init(&keyCtx, &br_sha256_vtable, _secret.c_str(), _secret.length());
br_hmac_context hmacCtx; br_hmac_context hmacCtx;
br_hmac_init(&hmacCtx, &keyCtx, 0); br_hmac_init(&hmacCtx, &keyCtx, 0);
br_hmac_update(&hmacCtx, payload.c_str(), payload.length()); br_hmac_update(&hmacCtx, payload.c_str(), payload.length());
br_hmac_out(&hmacCtx, hmacResult); br_hmac_out(&hmacCtx, hmacResult);
#endif #endif
} }
return encode((char *) hmacResult, 32); return encode((char*)hmacResult, 32);
} }
String ArduinoJsonJWT::buildJWT(JsonObject &payload) { String ArduinoJsonJWT::buildJWT(JsonObject& payload) {
// serialize, then encode payload // serialize, then encode payload
String jwt; String jwt;
serializeJson(payload, jwt); serializeJson(payload, jwt);
@ -49,14 +50,14 @@ String ArduinoJsonJWT::buildJWT(JsonObject &payload) {
// add the header to payload // add the header to payload
jwt = JWT_HEADER + '.' + jwt; jwt = JWT_HEADER + '.' + jwt;
// add signature // add signature
jwt += '.' + sign(jwt); jwt += '.' + sign(jwt);
return jwt; return jwt;
} }
void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument &jsonDocument) { void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument& jsonDocument) {
// clear json document before we begin, jsonDocument wil be null on failure // clear json document before we begin, jsonDocument wil be null on failure
jsonDocument.clear(); jsonDocument.clear();
@ -74,33 +75,33 @@ void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument &jsonDocument) {
// check the signature is valid // check the signature is valid
String signature = jwt.substring(signatureDelimiterIndex + 1); String signature = jwt.substring(signatureDelimiterIndex + 1);
jwt = jwt.substring(0, signatureDelimiterIndex); jwt = jwt.substring(0, signatureDelimiterIndex);
if (sign(jwt) != signature){ if (sign(jwt) != signature) {
return; return;
} }
// decode payload // decode payload
jwt = jwt.substring(JWT_HEADER_SIZE + 1); jwt = jwt.substring(JWT_HEADER_SIZE + 1);
jwt = decode(jwt); jwt = decode(jwt);
// parse payload, clearing json document after failure // parse payload, clearing json document after failure
DeserializationError error = deserializeJson(jsonDocument, jwt); DeserializationError error = deserializeJson(jsonDocument, jwt);
if (error != DeserializationError::Ok || !jsonDocument.is<JsonObject>()){ if (error != DeserializationError::Ok || !jsonDocument.is<JsonObject>()) {
jsonDocument.clear(); jsonDocument.clear();
} }
} }
String ArduinoJsonJWT::encode(const char *cstr, int inputLen) { String ArduinoJsonJWT::encode(const char* cstr, int inputLen) {
// prepare encoder // prepare encoder
base64_encodestate _state; base64_encodestate _state;
#if defined(ESP8266) #if defined(ESP8266)
base64_init_encodestate_nonewlines(&_state); base64_init_encodestate_nonewlines(&_state);
size_t encodedLength = base64_encode_expected_len_nonewlines(inputLen) + 1; size_t encodedLength = base64_encode_expected_len_nonewlines(inputLen) + 1;
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
base64_init_encodestate(&_state); base64_init_encodestate(&_state);
size_t encodedLength = base64_encode_expected_len(inputLen) + 1; size_t encodedLength = base64_encode_expected_len(inputLen) + 1;
#endif #endif
// prepare buffer of correct length, returning an empty string on failure // prepare buffer of correct length, returning an empty string on failure
char* buffer = (char*) malloc(encodedLength * sizeof(char)); char* buffer = (char*)malloc(encodedLength * sizeof(char));
if (buffer == nullptr) { if (buffer == nullptr) {
return ""; return "";
} }
@ -113,10 +114,10 @@ String ArduinoJsonJWT::encode(const char *cstr, int inputLen) {
// convert to arduino string, freeing buffer // convert to arduino string, freeing buffer
String value = String(buffer); String value = String(buffer);
free(buffer); free(buffer);
buffer=nullptr; buffer = nullptr;
// remove padding and convert to URL safe form // remove padding and convert to URL safe form
while (value.length() > 0 && value.charAt(value.length() - 1) == '='){ while (value.length() > 0 && value.charAt(value.length() - 1) == '=') {
value.remove(value.length() - 1); value.remove(value.length() - 1);
} }
value.replace('+', '-'); value.replace('+', '-');
@ -129,7 +130,7 @@ String ArduinoJsonJWT::encode(const char *cstr, int inputLen) {
String ArduinoJsonJWT::decode(String value) { String ArduinoJsonJWT::decode(String value) {
// convert to standard base64 // convert to standard base64
value.replace('-', '+'); value.replace('-', '+');
value.replace( '_', '/'); value.replace('_', '/');
// prepare buffer of correct length // prepare buffer of correct length
char buffer[base64_decode_expected_len(value.length()) + 1]; char buffer[base64_decode_expected_len(value.length()) + 1];
@ -139,5 +140,5 @@ String ArduinoJsonJWT::decode(String value) {
buffer[len] = 0; buffer[len] = 0;
// return as string // return as string
return String(buffer); return String(buffer);
} }

View File

@ -6,33 +6,31 @@
#include <libb64/cdecode.h> #include <libb64/cdecode.h>
#include <libb64/cencode.h> #include <libb64/cencode.h>
#if defined(ESP_PLATFORM) #if defined(ESP_PLATFORM)
#include <mbedtls/md.h> #include <mbedtls/md.h>
#else #else
#include <bearssl/bearssl_hmac.h> #include <bearssl/bearssl_hmac.h>
#endif #endif
class ArduinoJsonJWT { class ArduinoJsonJWT {
private:
private:
String _secret; String _secret;
const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";
const int JWT_HEADER_SIZE = JWT_HEADER.length(); const int JWT_HEADER_SIZE = JWT_HEADER.length();
String sign(String &value); String sign(String& value);
static String encode(const char *cstr, int len); static String encode(const char* cstr, int len);
static String decode(String value); static String decode(String value);
public: public:
ArduinoJsonJWT(String secret); ArduinoJsonJWT(String secret);
void setSecret(String secret); void setSecret(String secret);
String getSecret(); String getSecret();
String buildJWT(JsonObject &payload); String buildJWT(JsonObject& payload);
void parseJWT(String jwt, JsonDocument &jsonDocument); void parseJWT(String jwt, JsonDocument& jsonDocument);
}; };
#endif #endif

View File

@ -1,31 +1,33 @@
#ifndef _AsyncJsonCallbackResponse_H_ #ifndef _AsyncJsonCallbackResponse_H_
#define _AsyncJsonCallbackResponse_H_ #define _AsyncJsonCallbackResponse_H_
#include <ESPAsyncWebServer.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <ESPAsyncWebServer.h>
/* /*
* Listens for a response being destroyed and calls a callback during said distruction. * Listens for a response being destroyed and calls a callback during said distruction.
* used so we can take action after the response has been rendered to the client. * used so we can take action after the response has been rendered to the client.
* *
* Avoids having to fork ESPAsyncWebServer with a callback feature, but not nice! * Avoids having to fork ESPAsyncWebServer with a callback feature, but not nice!
*/ */
typedef std::function<void()> AsyncJsonCallback; typedef std::function<void()> AsyncJsonCallback;
class AsyncJsonCallbackResponse : public AsyncJsonResponse class AsyncJsonCallbackResponse : public AsyncJsonResponse {
{ private:
AsyncJsonCallback _callback;
private: public:
AsyncJsonCallback _callback; AsyncJsonCallbackResponse(AsyncJsonCallback callback,
bool isArray = false,
size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) :
AsyncJsonResponse(isArray, maxJsonBufferSize),
_callback{callback} {
}
public: ~AsyncJsonCallbackResponse() {
AsyncJsonCallbackResponse(AsyncJsonCallback callback, bool isArray = false, size_t maxJsonBufferSize = DYNAMIC_JSON_DOCUMENT_SIZE) _callback();
: AsyncJsonResponse(isArray, maxJsonBufferSize), _callback{callback} {} }
~AsyncJsonCallbackResponse()
{
_callback();
}
}; };
#endif // end _AsyncJsonCallbackResponse_H_ #endif // end _AsyncJsonCallbackResponse_H_

View File

@ -1,120 +1,131 @@
#ifndef Async_Json_Request_Web_Handler_H_ #ifndef Async_Json_Request_Web_Handler_H_
#define Async_Json_Request_Web_Handler_H_ #define Async_Json_Request_Web_Handler_H_
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <ESPAsyncWebServer.h>
#define ASYNC_JSON_REQUEST_DEFAULT_MAX_SIZE 1024 #define ASYNC_JSON_REQUEST_DEFAULT_MAX_SIZE 1024
#define ASYNC_JSON_REQUEST_MIMETYPE "application/json" #define ASYNC_JSON_REQUEST_MIMETYPE "application/json"
/* /*
* Handy little utility for dealing with small JSON request body payloads. * Handy little utility for dealing with small JSON request body payloads.
* *
* Need to be careful using this as we are somewhat limited by RAM. * Need to be careful using this as we are somewhat limited by RAM.
* *
* Really only of use where there is a determinate payload size. * Really only of use where there is a determinate payload size.
*/ */
typedef std::function<void(AsyncWebServerRequest *request, JsonDocument &jsonDocument)> JsonRequestCallback; typedef std::function<void(AsyncWebServerRequest* request, JsonDocument& jsonDocument)> JsonRequestCallback;
class AsyncJsonWebHandler: public AsyncWebHandler { class AsyncJsonWebHandler : public AsyncWebHandler {
private:
WebRequestMethodComposite _method;
JsonRequestCallback _onRequest;
size_t _maxContentLength;
private: protected:
WebRequestMethodComposite _method; String _uri;
JsonRequestCallback _onRequest;
size_t _maxContentLength;
protected: public:
String _uri; AsyncJsonWebHandler() :
_method(HTTP_POST | HTTP_PUT | HTTP_PATCH),
public:
AsyncJsonWebHandler() :
_method(HTTP_POST|HTTP_PUT|HTTP_PATCH),
_onRequest(nullptr), _onRequest(nullptr),
_maxContentLength(ASYNC_JSON_REQUEST_DEFAULT_MAX_SIZE), _maxContentLength(ASYNC_JSON_REQUEST_DEFAULT_MAX_SIZE),
_uri() {} _uri() {
}
~AsyncJsonWebHandler() {} ~AsyncJsonWebHandler() {
}
void setUri(const String& uri) { _uri = uri; } void setUri(const String& uri) {
void setMethod(WebRequestMethodComposite method) { _method = method; } _uri = uri;
void setMaxContentLength(size_t maxContentLength) { _maxContentLength = maxContentLength; } }
void onRequest(JsonRequestCallback fn) { _onRequest = fn; } void setMethod(WebRequestMethodComposite method) {
_method = method;
}
void setMaxContentLength(size_t maxContentLength) {
_maxContentLength = maxContentLength;
}
void onRequest(JsonRequestCallback fn) {
_onRequest = fn;
}
virtual bool canHandle(AsyncWebServerRequest *request) override final { virtual bool canHandle(AsyncWebServerRequest* request) override final {
if(!_onRequest) if (!_onRequest)
return false; return false;
if(!(_method & request->method())) if (!(_method & request->method()))
return false; return false;
if(_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri+"/"))) if (_uri.length() && (_uri != request->url() && !request->url().startsWith(_uri + "/")))
return false; return false;
if (!request->contentType().equalsIgnoreCase(ASYNC_JSON_REQUEST_MIMETYPE)) if (!request->contentType().equalsIgnoreCase(ASYNC_JSON_REQUEST_MIMETYPE))
return false; return false;
request->addInterestingHeader("ANY"); request->addInterestingHeader("ANY");
return true; return true;
} }
virtual void handleRequest(AsyncWebServerRequest *request) override final { virtual void handleRequest(AsyncWebServerRequest* request) override final {
// no request configured // no request configured
if(!_onRequest) { if (!_onRequest) {
Serial.print("No request callback was configured for endpoint: "); Serial.print("No request callback was configured for endpoint: ");
Serial.println(_uri); Serial.println(_uri);
request->send(500);
return;
}
// we have been handed too much data, return a 413 (payload too large)
if (request->contentLength() > _maxContentLength) {
request->send(413);
return;
}
// parse JSON and if possible handle the request
if (request->_tempObject) {
DynamicJsonDocument jsonDocument(_maxContentLength);
DeserializationError error = deserializeJson(jsonDocument, (uint8_t *) request->_tempObject);
if (error == DeserializationError::Ok) {
_onRequest(request, jsonDocument);
}else{
request->send(400);
}
return;
}
// fallthrough, we have a null pointer, return 500.
// this can be due to running out of memory or never receiving body data.
request->send(500); request->send(500);
return;
} }
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) override final { // we have been handed too much data, return a 413 (payload too large)
if (_onRequest) { if (request->contentLength() > _maxContentLength) {
// don't allocate if data is too large request->send(413);
if (total > _maxContentLength){ return;
return; }
}
// try to allocate memory on first call // parse JSON and if possible handle the request
// NB: the memory allocated here is freed by ~AsyncWebServerRequest if (request->_tempObject) {
if(index == 0 && !request->_tempObject){ DynamicJsonDocument jsonDocument(_maxContentLength);
request->_tempObject = malloc(total); DeserializationError error = deserializeJson(jsonDocument, (uint8_t*)request->_tempObject);
} if (error == DeserializationError::Ok) {
_onRequest(request, jsonDocument);
} else {
request->send(400);
}
return;
}
// copy the data into the buffer, if we have a buffer! // fallthrough, we have a null pointer, return 500.
if (request->_tempObject) { // this can be due to running out of memory or never receiving body data.
memcpy((uint8_t *) request->_tempObject+index, data, len); request->send(500);
} }
virtual void handleBody(AsyncWebServerRequest* request,
uint8_t* data,
size_t len,
size_t index,
size_t total) override final {
if (_onRequest) {
// don't allocate if data is too large
if (total > _maxContentLength) {
return;
}
// try to allocate memory on first call
// NB: the memory allocated here is freed by ~AsyncWebServerRequest
if (index == 0 && !request->_tempObject) {
request->_tempObject = malloc(total);
}
// copy the data into the buffer, if we have a buffer!
if (request->_tempObject) {
memcpy((uint8_t*)request->_tempObject + index, data, len);
} }
} }
}
virtual bool isRequestHandlerTrivial() override final { virtual bool isRequestHandlerTrivial() override final {
return _onRequest ? false : true; return _onRequest ? false : true;
} }
}; };
#endif // end Async_Json_Request_Web_Handler_H_ #endif // end Async_Json_Request_Web_Handler_H_

View File

@ -1,36 +1,41 @@
#include <AuthenticationService.h> #include <AuthenticationService.h>
AuthenticationService::AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager) : _securityManager(securityManager) { AuthenticationService::AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager) :
server->on(VERIFY_AUTHORIZATION_PATH, HTTP_GET, std::bind(&AuthenticationService::verifyAuthorization, this, std::placeholders::_1)); _securityManager(securityManager) {
server->on(VERIFY_AUTHORIZATION_PATH,
HTTP_GET,
std::bind(&AuthenticationService::verifyAuthorization, this, std::placeholders::_1));
_signInHandler.setUri(SIGN_IN_PATH); _signInHandler.setUri(SIGN_IN_PATH);
_signInHandler.setMethod(HTTP_POST); _signInHandler.setMethod(HTTP_POST);
_signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE); _signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE);
_signInHandler.onRequest(std::bind(&AuthenticationService::signIn, this, std::placeholders::_1, std::placeholders::_2)); _signInHandler.onRequest(
std::bind(&AuthenticationService::signIn, this, std::placeholders::_1, std::placeholders::_2));
server->addHandler(&_signInHandler); server->addHandler(&_signInHandler);
} }
AuthenticationService::~AuthenticationService() {} AuthenticationService::~AuthenticationService() {
}
/** /**
* Verifys that the request supplied a valid JWT. * Verifys that the request supplied a valid JWT.
*/ */
void AuthenticationService::verifyAuthorization(AsyncWebServerRequest *request) { void AuthenticationService::verifyAuthorization(AsyncWebServerRequest* request) {
Authentication authentication = _securityManager->authenticateRequest(request); Authentication authentication = _securityManager->authenticateRequest(request);
request->send(authentication.isAuthenticated() ? 200: 401); request->send(authentication.isAuthenticated() ? 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. * 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, JsonDocument &jsonDocument){ void AuthenticationService::signIn(AsyncWebServerRequest* request, JsonDocument& jsonDocument) {
if (jsonDocument.is<JsonObject>()) { if (jsonDocument.is<JsonObject>()) {
String username = jsonDocument["username"]; String username = jsonDocument["username"];
String password = jsonDocument["password"]; String password = jsonDocument["password"];
Authentication authentication = _securityManager->authenticate(username, password); Authentication authentication = _securityManager->authenticate(username, password);
if (authentication.isAuthenticated()) { if (authentication.isAuthenticated()) {
User* user = authentication.getUser(); User* user = authentication.getUser();
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_AUTHENTICATION_SIZE); AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_AUTHENTICATION_SIZE);
JsonObject jsonObject = response->getRoot(); JsonObject jsonObject = response->getRoot();
jsonObject["access_token"] = _securityManager->generateJWT(user); jsonObject["access_token"] = _securityManager->generateJWT(user);
response->setLength(); response->setLength();
@ -38,7 +43,6 @@ void AuthenticationService::signIn(AsyncWebServerRequest *request, JsonDocument
return; return;
} }
} }
AsyncWebServerResponse *response = request->beginResponse(401); AsyncWebServerResponse* response = request->beginResponse(401);
request->send(response); request->send(response);
} }

View File

@ -1,32 +1,28 @@
#ifndef AuthenticationService_H_ #ifndef AuthenticationService_H_
#define AuthenticationService_H_ #define AuthenticationService_H_
#include <SecurityManager.h>
#include <ESPAsyncWebServer.h>
#include <AsyncJsonWebHandler.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <AsyncJsonWebHandler.h>
#include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization" #define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization"
#define SIGN_IN_PATH "/rest/signIn" #define SIGN_IN_PATH "/rest/signIn"
#define MAX_AUTHENTICATION_SIZE 256 #define MAX_AUTHENTICATION_SIZE 256
class AuthenticationService { class AuthenticationService {
public:
AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager);
~AuthenticationService();
public: private:
SecurityManager* _securityManager;
AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager); AsyncJsonWebHandler _signInHandler;
~AuthenticationService();
private:
SecurityManager* _securityManager;
AsyncJsonWebHandler _signInHandler;
// endpoint functions
void signIn(AsyncWebServerRequest *request, JsonDocument &jsonDocument);
void verifyAuthorization(AsyncWebServerRequest *request);
// endpoint functions
void signIn(AsyncWebServerRequest* request, JsonDocument& jsonDocument);
void verifyAuthorization(AsyncWebServerRequest* request);
}; };
#endif // end SecurityManager_h #endif // end SecurityManager_h

View File

@ -1,18 +1,18 @@
#include <ESP8266React.h> #include <ESP8266React.h>
ESP8266React::ESP8266React(AsyncWebServer* server, FS* fs): ESP8266React::ESP8266React(AsyncWebServer* server, FS* fs) :
_securitySettingsService(server, fs), _securitySettingsService(server, fs),
_wifiSettingsService(server, fs, &_securitySettingsService), _wifiSettingsService(server, fs, &_securitySettingsService),
_apSettingsService(server, fs, &_securitySettingsService), _apSettingsService(server, fs, &_securitySettingsService),
_ntpSettingsService(server, fs, &_securitySettingsService), _ntpSettingsService(server, fs, &_securitySettingsService),
_otaSettingsService(server, fs, &_securitySettingsService), _otaSettingsService(server, fs, &_securitySettingsService),
_restartService(server, &_securitySettingsService), _restartService(server, &_securitySettingsService),
_authenticationService(server, &_securitySettingsService), _authenticationService(server, &_securitySettingsService),
_wifiScanner(server, &_securitySettingsService), _wifiScanner(server, &_securitySettingsService),
_wifiStatus(server, &_securitySettingsService), _wifiStatus(server, &_securitySettingsService),
_ntpStatus(server, &_securitySettingsService), _ntpStatus(server, &_securitySettingsService),
_apStatus(server, &_securitySettingsService), _apStatus(server, &_securitySettingsService),
_systemStatus(server, &_securitySettingsService) { _systemStatus(server, &_securitySettingsService) {
// Serve static resources from /www/ // Serve static resources from /www/
server->serveStatic("/js/", SPIFFS, "/www/js/"); server->serveStatic("/js/", SPIFFS, "/www/js/");
server->serveStatic("/css/", SPIFFS, "/www/css/"); server->serveStatic("/css/", SPIFFS, "/www/css/");
@ -22,7 +22,7 @@ ESP8266React::ESP8266React(AsyncWebServer* server, FS* fs):
// Serving all other get requests with "/www/index.htm" // Serving all other get requests with "/www/index.htm"
// OPTIONS get a straight up 200 response // OPTIONS get a straight up 200 response
server->onNotFound([](AsyncWebServerRequest *request) { server->onNotFound([](AsyncWebServerRequest* request) {
if (request->method() == HTTP_GET) { if (request->method() == HTTP_GET) {
request->send(SPIFFS, "/www/index.html"); request->send(SPIFFS, "/www/index.html");
} else if (request->method() == HTTP_OPTIONS) { } else if (request->method() == HTTP_OPTIONS) {
@ -32,12 +32,12 @@ ESP8266React::ESP8266React(AsyncWebServer* server, FS* fs):
} }
}); });
// Disable CORS if required // Disable CORS if required
#if defined(ENABLE_CORS) #if defined(ENABLE_CORS)
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", CORS_ORIGIN); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", CORS_ORIGIN);
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true");
#endif #endif
} }
void ESP8266React::begin() { void ESP8266React::begin() {
@ -48,7 +48,7 @@ void ESP8266React::begin() {
_otaSettingsService.begin(); _otaSettingsService.begin();
} }
void ESP8266React::loop() { void ESP8266React::loop() {
_wifiSettingsService.loop(); _wifiSettingsService.loop();
_apSettingsService.loop(); _apSettingsService.loop();
_ntpSettingsService.loop(); _ntpSettingsService.loop();

View File

@ -4,59 +4,54 @@
#include <Arduino.h> #include <Arduino.h>
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h> #include <AsyncTCP.h>
#include <AsyncTCP.h> #include <SPIFFS.h>
#include <SPIFFS.h> #include <WiFi.h>
#endif #endif
#include <FS.h>
#include <SecuritySettingsService.h>
#include <WiFiSettingsService.h>
#include <APSettingsService.h> #include <APSettingsService.h>
#include <NTPSettingsService.h>
#include <OTASettingsService.h>
#include <AuthenticationService.h>
#include <WiFiScanner.h>
#include <WiFiStatus.h>
#include <NTPStatus.h>
#include <APStatus.h> #include <APStatus.h>
#include <SystemStatus.h> #include <AuthenticationService.h>
#include <FS.h>
#include <NTPSettingsService.h>
#include <NTPStatus.h>
#include <OTASettingsService.h>
#include <RestartService.h> #include <RestartService.h>
#include <SecuritySettingsService.h>
#include <SystemStatus.h>
#include <WiFiScanner.h>
#include <WiFiSettingsService.h>
#include <WiFiStatus.h>
class ESP8266React { class ESP8266React {
public:
ESP8266React(AsyncWebServer* server, FS* fs);
public: void begin();
void loop();
ESP8266React(AsyncWebServer* server, FS* fs); SecurityManager* getSecurityManager() {
return &_securitySettingsService;
void begin(); }
void loop();
SecurityManager* getSecurityManager(){
return &_securitySettingsService;
}
private: private:
SecuritySettingsService _securitySettingsService;
SecuritySettingsService _securitySettingsService; WiFiSettingsService _wifiSettingsService;
APSettingsService _apSettingsService;
WiFiSettingsService _wifiSettingsService; NTPSettingsService _ntpSettingsService;
APSettingsService _apSettingsService; OTASettingsService _otaSettingsService;
NTPSettingsService _ntpSettingsService; RestartService _restartService;
OTASettingsService _otaSettingsService; AuthenticationService _authenticationService;
RestartService _restartService;
AuthenticationService _authenticationService;
WiFiScanner _wifiScanner;
WiFiStatus _wifiStatus;
NTPStatus _ntpStatus;
APStatus _apStatus;
SystemStatus _systemStatus;
WiFiScanner _wifiScanner;
WiFiStatus _wifiStatus;
NTPStatus _ntpStatus;
APStatus _apStatus;
SystemStatus _systemStatus;
}; };
#endif #endif

View File

@ -1,22 +1,28 @@
#include <NTPSettingsService.h> #include <NTPSettingsService.h>
NTPSettingsService::NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : AdminSettingsService(server, fs, securityManager, NTP_SETTINGS_SERVICE_PATH, NTP_SETTINGS_FILE) { NTPSettingsService::NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) :
AdminSettingsService(server, fs, securityManager, NTP_SETTINGS_SERVICE_PATH, NTP_SETTINGS_FILE) {
#if defined(ESP8266) #if defined(ESP8266)
_onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(
_onStationModeGotIPHandler = WiFi.onStationModeGotIP(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1)); std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1));
_onStationModeGotIPHandler =
WiFi.onStationModeGotIP(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1));
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
WiFi.onEvent(std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); WiFi.onEvent(
WiFi.onEvent(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2),
WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
WiFi.onEvent(std::bind(&NTPSettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2),
WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
#endif #endif
NTP.onNTPSyncEvent ([this](NTPSyncEvent_t ntpEvent) { NTP.onNTPSyncEvent([this](NTPSyncEvent_t ntpEvent) {
_ntpEvent = ntpEvent; _ntpEvent = ntpEvent;
_syncEventTriggered = true; _syncEventTriggered = true;
}); });
} }
NTPSettingsService::~NTPSettingsService() {} NTPSettingsService::~NTPSettingsService() {
}
void NTPSettingsService::loop() { void NTPSettingsService::loop() {
// detect when we need to re-configure NTP and do it in the main loop // detect when we need to re-configure NTP and do it in the main loop
@ -41,12 +47,12 @@ void NTPSettingsService::readFromJsonObject(JsonObject& root) {
// validate server is specified, resorting to default // validate server is specified, resorting to default
_server.trim(); _server.trim();
if (!_server){ if (!_server) {
_server = NTP_SETTINGS_SERVICE_DEFAULT_SERVER; _server = NTP_SETTINGS_SERVICE_DEFAULT_SERVER;
} }
// make sure interval is in bounds // make sure interval is in bounds
if (_interval < NTP_SETTINGS_MIN_INTERVAL){ if (_interval < NTP_SETTINGS_MIN_INTERVAL) {
_interval = NTP_SETTINGS_MIN_INTERVAL; _interval = NTP_SETTINGS_MIN_INTERVAL;
} else if (_interval > NTP_SETTINGS_MAX_INTERVAL) { } else if (_interval > NTP_SETTINGS_MAX_INTERVAL) {
_interval = NTP_SETTINGS_MAX_INTERVAL; _interval = NTP_SETTINGS_MAX_INTERVAL;
@ -98,14 +104,14 @@ void NTPSettingsService::configureNTP() {
} }
void NTPSettingsService::processSyncEvent(NTPSyncEvent_t ntpEvent) { void NTPSettingsService::processSyncEvent(NTPSyncEvent_t ntpEvent) {
if (ntpEvent) { if (ntpEvent) {
Serial.print ("Time Sync error: "); Serial.print("Time Sync error: ");
if (ntpEvent == noResponse) if (ntpEvent == noResponse)
Serial.println ("NTP server not reachable"); Serial.println("NTP server not reachable");
else if (ntpEvent == invalidAddress) else if (ntpEvent == invalidAddress)
Serial.println ("Invalid NTP server address"); Serial.println("Invalid NTP server address");
} else { } else {
Serial.print ("Got NTP time: "); Serial.print("Got NTP time: ");
Serial.println (NTP.getTimeDateString (NTP.getLastNTPSync ())); Serial.println(NTP.getTimeDateString(NTP.getLastNTPSync()));
} }
} }

View File

@ -3,8 +3,8 @@
#include <AdminSettingsService.h> #include <AdminSettingsService.h>
#include <TimeLib.h>
#include <NtpClientLib.h> #include <NtpClientLib.h>
#include <TimeLib.h>
// default time server // default time server
#define NTP_SETTINGS_SERVICE_DEFAULT_SERVER "pool.ntp.org" #define NTP_SETTINGS_SERVICE_DEFAULT_SERVER "pool.ntp.org"
@ -18,43 +18,38 @@
#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings" #define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings"
class NTPSettingsService : public AdminSettingsService { class NTPSettingsService : public AdminSettingsService {
public:
NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
~NTPSettingsService();
public: void loop();
NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); protected:
~NTPSettingsService(); void readFromJsonObject(JsonObject& root);
void writeToJsonObject(JsonObject& root);
void onConfigUpdated();
void loop(); private:
String _server;
int _interval;
protected: bool _reconfigureNTP = false;
bool _syncEventTriggered = false;
void readFromJsonObject(JsonObject& root); NTPSyncEvent_t _ntpEvent;
void writeToJsonObject(JsonObject& root);
void onConfigUpdated();
private:
String _server;
int _interval;
bool _reconfigureNTP = false;
bool _syncEventTriggered = false;
NTPSyncEvent_t _ntpEvent;
#if defined(ESP8266) #if defined(ESP8266)
WiFiEventHandler _onStationModeDisconnectedHandler; WiFiEventHandler _onStationModeDisconnectedHandler;
WiFiEventHandler _onStationModeGotIPHandler; WiFiEventHandler _onStationModeGotIPHandler;
void onStationModeGotIP(const WiFiEventStationModeGotIP& event); void onStationModeGotIP(const WiFiEventStationModeGotIP& event);
void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event);
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info);
void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
#endif #endif
void configureNTP(); void configureNTP();
void processSyncEvent(NTPSyncEvent_t ntpEvent); void processSyncEvent(NTPSyncEvent_t ntpEvent);
}; };
#endif // end NTPSettingsService_h #endif // end NTPSettingsService_h

View File

@ -1,27 +1,28 @@
#include <NTPStatus.h> #include <NTPStatus.h>
NTPStatus::NTPStatus(AsyncWebServer* server, SecurityManager* securityManager) { NTPStatus::NTPStatus(AsyncWebServer* server, SecurityManager* securityManager) {
server->on(NTP_STATUS_SERVICE_PATH, HTTP_GET, server->on(NTP_STATUS_SERVICE_PATH,
securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED) HTTP_GET,
); securityManager->wrapRequest(std::bind(&NTPStatus::ntpStatus, this, std::placeholders::_1),
AuthenticationPredicates::IS_AUTHENTICATED));
} }
void NTPStatus::ntpStatus(AsyncWebServerRequest *request) { void NTPStatus::ntpStatus(AsyncWebServerRequest* request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE); AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_NTP_STATUS_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
// request time now first, this can sometimes force a sync // request time now first, this can sometimes force a sync
time_t timeNow = now(); time_t timeNow = now();
timeStatus_t status = timeStatus(); timeStatus_t status = timeStatus();
time_t lastSync = NTP.getLastNTPSync(); time_t lastSync = NTP.getLastNTPSync();
root["status"] = (int) status; root["status"] = (int)status;
root["last_sync"] = lastSync; root["last_sync"] = lastSync;
root["server"] = NTP.getNtpServerName(); root["server"] = NTP.getNtpServerName();
root["interval"] = NTP.getInterval(); root["interval"] = NTP.getInterval();
root["uptime"] = NTP.getUptime(); root["uptime"] = NTP.getUptime();
// only add now to response if we have successfully synced // only add now to response if we have successfully synced
if (status != timeNotSet){ if (status != timeNotSet) {
root["now"] = timeNow; root["now"] = timeNow;
} }

View File

@ -2,33 +2,29 @@
#define NTPStatus_h #define NTPStatus_h
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h> #include <AsyncTCP.h>
#include <AsyncTCP.h> #include <WiFi.h>
#endif #endif
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <TimeLib.h> #include <ESPAsyncWebServer.h>
#include <NtpClientLib.h> #include <NtpClientLib.h>
#include <SecurityManager.h> #include <SecurityManager.h>
#include <TimeLib.h>
#define MAX_NTP_STATUS_SIZE 1024 #define MAX_NTP_STATUS_SIZE 1024
#define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus" #define NTP_STATUS_SERVICE_PATH "/rest/ntpStatus"
class NTPStatus { class NTPStatus {
public:
NTPStatus(AsyncWebServer* server, SecurityManager* securityManager);
public: private:
void ntpStatus(AsyncWebServerRequest* request);
NTPStatus(AsyncWebServer* server, SecurityManager* securityManager);
private:
void ntpStatus(AsyncWebServerRequest *request);
}; };
#endif // end NTPStatus_h #endif // end NTPStatus_h

View File

@ -1,17 +1,21 @@
#include <OTASettingsService.h> #include <OTASettingsService.h>
OTASettingsService::OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : AdminSettingsService(server, fs, securityManager, OTA_SETTINGS_SERVICE_PATH, OTA_SETTINGS_FILE) { OTASettingsService::OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) :
AdminSettingsService(server, fs, securityManager, OTA_SETTINGS_SERVICE_PATH, OTA_SETTINGS_FILE) {
#if defined(ESP8266) #if defined(ESP8266)
_onStationModeGotIPHandler = WiFi.onStationModeGotIP(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1)); _onStationModeGotIPHandler =
WiFi.onStationModeGotIP(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1));
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
WiFi.onEvent(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); WiFi.onEvent(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1, std::placeholders::_2),
WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
#endif #endif
} }
OTASettingsService::~OTASettingsService() {} OTASettingsService::~OTASettingsService() {
}
void OTASettingsService::loop() { void OTASettingsService::loop() {
if (_enabled && _arduinoOTA){ if (_enabled && _arduinoOTA) {
_arduinoOTA->handle(); _arduinoOTA->handle();
} }
} }
@ -26,7 +30,7 @@ void OTASettingsService::readFromJsonObject(JsonObject& root) {
_password = root["password"] | DEFAULT_OTA_PASSWORD; _password = root["password"] | DEFAULT_OTA_PASSWORD;
// provide defaults // provide defaults
if (_port < 0){ if (_port < 0) {
_port = DEFAULT_OTA_PORT; _port = DEFAULT_OTA_PORT;
} }
} }
@ -38,7 +42,7 @@ void OTASettingsService::writeToJsonObject(JsonObject& root) {
} }
void OTASettingsService::configureArduinoOTA() { void OTASettingsService::configureArduinoOTA() {
if (_arduinoOTA){ if (_arduinoOTA) {
#if defined(ESP_PLATFORM) #if defined(ESP_PLATFORM)
_arduinoOTA->end(); _arduinoOTA->end();
#endif #endif
@ -50,22 +54,23 @@ void OTASettingsService::configureArduinoOTA() {
_arduinoOTA = new ArduinoOTAClass; _arduinoOTA = new ArduinoOTAClass;
_arduinoOTA->setPort(_port); _arduinoOTA->setPort(_port);
_arduinoOTA->setPassword(_password.c_str()); _arduinoOTA->setPassword(_password.c_str());
_arduinoOTA->onStart([]() { _arduinoOTA->onStart([]() { Serial.println("Starting"); });
Serial.println("Starting"); _arduinoOTA->onEnd([]() { Serial.println("\nEnd"); });
});
_arduinoOTA->onEnd([]() {
Serial.println("\nEnd");
});
_arduinoOTA->onProgress([](unsigned int progress, unsigned int total) { _arduinoOTA->onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100))); Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
}); });
_arduinoOTA->onError([](ota_error_t error) { _arduinoOTA->onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error); Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); if (error == OTA_AUTH_ERROR)
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); Serial.println("Auth Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_BEGIN_ERROR)
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); Serial.println("Begin Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed"); else if (error == OTA_CONNECT_ERROR)
Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR)
Serial.println("Receive Failed");
else if (error == OTA_END_ERROR)
Serial.println("End Failed");
}); });
_arduinoOTA->begin(); _arduinoOTA->begin();
} }
@ -74,9 +79,9 @@ void OTASettingsService::configureArduinoOTA() {
#if defined(ESP8266) #if defined(ESP8266)
void OTASettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) { void OTASettingsService::onStationModeGotIP(const WiFiEventStationModeGotIP& event) {
configureArduinoOTA(); configureArduinoOTA();
} }
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
void OTASettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { void OTASettingsService::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
configureArduinoOTA(); configureArduinoOTA();
} }
#endif #endif

View File

@ -4,13 +4,13 @@
#include <AdminSettingsService.h> #include <AdminSettingsService.h>
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266mDNS.h> #include <ESP8266mDNS.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <ESPmDNS.h> #include <ESPmDNS.h>
#endif #endif
#include <WiFiUdp.h>
#include <ArduinoOTA.h> #include <ArduinoOTA.h>
#include <WiFiUdp.h>
// Emergency defaults // Emergency defaults
#define DEFAULT_OTA_PORT 8266 #define DEFAULT_OTA_PORT 8266
@ -20,36 +20,31 @@
#define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings" #define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings"
class OTASettingsService : public AdminSettingsService { class OTASettingsService : public AdminSettingsService {
public:
OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
~OTASettingsService();
public: void loop();
OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); protected:
~OTASettingsService(); void onConfigUpdated();
void readFromJsonObject(JsonObject& root);
void writeToJsonObject(JsonObject& root);
void loop(); private:
ArduinoOTAClass* _arduinoOTA;
bool _enabled;
int _port;
String _password;
protected: void configureArduinoOTA();
void onConfigUpdated();
void readFromJsonObject(JsonObject& root);
void writeToJsonObject(JsonObject& root);
private:
ArduinoOTAClass *_arduinoOTA;
bool _enabled;
int _port;
String _password;
void configureArduinoOTA();
#if defined(ESP8266) #if defined(ESP8266)
WiFiEventHandler _onStationModeGotIPHandler; WiFiEventHandler _onStationModeGotIPHandler;
void onStationModeGotIP(const WiFiEventStationModeGotIP& event); void onStationModeGotIP(const WiFiEventStationModeGotIP& event);
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info);
#endif #endif
}; };
#endif // end OTASettingsService_h #endif // end OTASettingsService_h

View File

@ -1,9 +1,10 @@
#include <RestartService.h> #include <RestartService.h>
RestartService::RestartService(AsyncWebServer* server, SecurityManager* securityManager) { RestartService::RestartService(AsyncWebServer* server, SecurityManager* securityManager) {
server->on(RESTART_SERVICE_PATH, HTTP_POST, securityManager->wrapRequest( server->on(RESTART_SERVICE_PATH,
std::bind(&RestartService::restart, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN HTTP_POST,
)); securityManager->wrapRequest(std::bind(&RestartService::restart, this, std::placeholders::_1),
AuthenticationPredicates::IS_ADMIN));
} }
void RestartService::restart(AsyncWebServerRequest* request) { void RestartService::restart(AsyncWebServerRequest* request) {

View File

@ -2,11 +2,11 @@
#define RestartService_h #define RestartService_h
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h> #include <AsyncTCP.h>
#include <AsyncTCP.h> #include <WiFi.h>
#endif #endif
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
@ -15,15 +15,11 @@
#define RESTART_SERVICE_PATH "/rest/restart" #define RESTART_SERVICE_PATH "/rest/restart"
class RestartService { class RestartService {
public:
RestartService(AsyncWebServer* server, SecurityManager* securityManager);
public: private:
void restart(AsyncWebServerRequest* request);
RestartService(AsyncWebServer* server, SecurityManager* securityManager);
private:
void restart(AsyncWebServerRequest *request);
}; };
#endif // end RestartService_h #endif // end RestartService_h

View File

@ -1,14 +1,14 @@
#include <SecurityManager.h> #include <SecurityManager.h>
Authentication SecurityManager::authenticateRequest(AsyncWebServerRequest *request) { Authentication SecurityManager::authenticateRequest(AsyncWebServerRequest *request) {
AsyncWebHeader* authorizationHeader = request->getHeader(AUTHORIZATION_HEADER); AsyncWebHeader *authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
if (authorizationHeader) { if (authorizationHeader) {
String value = authorizationHeader->value(); String value = authorizationHeader->value();
if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)){ if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) {
value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN); value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN);
return authenticateJWT(value); return authenticateJWT(value);
} }
} }
return Authentication(); return Authentication();
} }
@ -19,7 +19,7 @@ Authentication SecurityManager::authenticateJWT(String jwt) {
JsonObject parsedPayload = payloadDocument.as<JsonObject>(); JsonObject parsedPayload = payloadDocument.as<JsonObject>();
String username = parsedPayload["username"]; String username = parsedPayload["username"];
for (User _user : _users) { for (User _user : _users) {
if (_user.getUsername() == username && validatePayload(parsedPayload, &_user)){ if (_user.getUsername() == username && validatePayload(parsedPayload, &_user)) {
return Authentication(_user); return Authentication(_user);
} }
} }
@ -29,7 +29,7 @@ Authentication SecurityManager::authenticateJWT(String jwt) {
Authentication SecurityManager::authenticate(String username, String password) { Authentication SecurityManager::authenticate(String username, String password) {
for (User _user : _users) { for (User _user : _users) {
if (_user.getUsername() == username && _user.getPassword() == password){ if (_user.getUsername() == username && _user.getPassword() == password) {
return Authentication(_user); return Authentication(_user);
} }
} }
@ -38,25 +38,26 @@ Authentication SecurityManager::authenticate(String username, String password) {
inline void populateJWTPayload(JsonObject &payload, User *user) { inline void populateJWTPayload(JsonObject &payload, User *user) {
payload["username"] = user->getUsername(); payload["username"] = user->getUsername();
payload["admin"] = user -> isAdmin(); payload["admin"] = user->isAdmin();
} }
boolean SecurityManager::validatePayload(JsonObject &parsedPayload, User *user) { boolean SecurityManager::validatePayload(JsonObject &parsedPayload, User *user) {
DynamicJsonDocument _jsonDocument(MAX_JWT_SIZE); DynamicJsonDocument _jsonDocument(MAX_JWT_SIZE);
JsonObject payload = _jsonDocument.to<JsonObject>(); JsonObject payload = _jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user); populateJWTPayload(payload, user);
return payload == parsedPayload; return payload == parsedPayload;
} }
String SecurityManager::generateJWT(User *user) { String SecurityManager::generateJWT(User *user) {
DynamicJsonDocument _jsonDocument(MAX_JWT_SIZE); DynamicJsonDocument _jsonDocument(MAX_JWT_SIZE);
JsonObject payload = _jsonDocument.to<JsonObject>(); JsonObject payload = _jsonDocument.to<JsonObject>();
populateJWTPayload(payload, user); populateJWTPayload(payload, user);
return _jwtHandler.buildJWT(payload); return _jwtHandler.buildJWT(payload);
} }
ArRequestHandlerFunction SecurityManager::wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) { ArRequestHandlerFunction SecurityManager::wrapRequest(ArRequestHandlerFunction onRequest,
return [this, onRequest, predicate](AsyncWebServerRequest *request){ AuthenticationPredicate predicate) {
return [this, onRequest, predicate](AsyncWebServerRequest *request) {
Authentication authentication = authenticateRequest(request); Authentication authentication = authenticateRequest(request);
if (!predicate(authentication)) { if (!predicate(authentication)) {
request->send(401); request->send(401);
@ -64,5 +65,4 @@ ArRequestHandlerFunction SecurityManager::wrapRequest(ArRequestHandlerFunction o
} }
onRequest(request); onRequest(request);
}; };
} }

View File

@ -1,9 +1,9 @@
#ifndef SecurityManager_h #ifndef SecurityManager_h
#define SecurityManager_h #define SecurityManager_h
#include <list>
#include <ArduinoJsonJWT.h> #include <ArduinoJsonJWT.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <list>
#define DEFAULT_JWT_SECRET "esp8266-react" #define DEFAULT_JWT_SECRET "esp8266-react"
@ -14,97 +14,97 @@
#define MAX_JWT_SIZE 128 #define MAX_JWT_SIZE 128
class User { class User {
private: private:
String _username; String _username;
String _password; String _password;
bool _admin; bool _admin;
public:
User(String username, String password, bool admin): _username(username), _password(password), _admin(admin) {} public:
String getUsername() { User(String username, String password, bool admin) : _username(username), _password(password), _admin(admin) {
return _username; }
} String getUsername() {
String getPassword() { return _username;
return _password; }
} String getPassword() {
bool isAdmin() { return _password;
return _admin; }
} bool isAdmin() {
return _admin;
}
}; };
class Authentication { class Authentication {
private: private:
User *_user; User* _user;
boolean _authenticated; boolean _authenticated;
public:
Authentication(User& user): _user(new User(user)), _authenticated(true) {} public:
Authentication() : _user(nullptr), _authenticated(false) {} Authentication(User& user) : _user(new User(user)), _authenticated(true) {
~Authentication() { }
delete(_user); Authentication() : _user(nullptr), _authenticated(false) {
} }
User* getUser() { ~Authentication() {
return _user; delete (_user);
} }
bool isAuthenticated() { User* getUser() {
return _authenticated; return _user;
} }
bool isAuthenticated() {
return _authenticated;
}
}; };
typedef std::function<boolean(Authentication &authentication)> AuthenticationPredicate; typedef std::function<boolean(Authentication& authentication)> AuthenticationPredicate;
class AuthenticationPredicates { class AuthenticationPredicates {
public: public:
static bool NONE_REQUIRED(Authentication &authentication) { static bool NONE_REQUIRED(Authentication& authentication) {
return true; return true;
}; };
static bool IS_AUTHENTICATED(Authentication &authentication) { static bool IS_AUTHENTICATED(Authentication& authentication) {
return authentication.isAuthenticated(); return authentication.isAuthenticated();
}; };
static bool IS_ADMIN(Authentication &authentication) { static bool IS_ADMIN(Authentication& authentication) {
return authentication.isAuthenticated() && authentication.getUser()->isAdmin(); return authentication.isAuthenticated() && authentication.getUser()->isAdmin();
}; };
}; };
class SecurityManager { class SecurityManager {
public:
/*
* Authenticate, returning the user if found
*/
Authentication authenticate(String username, String password);
public: /*
* Check the request header for the Authorization token
*/
Authentication authenticateRequest(AsyncWebServerRequest* request);
/* /*
* Authenticate, returning the user if found * Generate a JWT for the user provided
*/ */
Authentication authenticate(String username, String password); String generateJWT(User* user);
/* /**
* Check the request header for the Authorization token * Wrap the provided request to provide validation against an AuthenticationPredicate.
*/ */
Authentication authenticateRequest(AsyncWebServerRequest *request); ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
/*
* Generate a JWT for the user provided
*/
String generateJWT(User *user);
/** protected:
* Wrap the provided request to provide validation against an AuthenticationPredicate. ArduinoJsonJWT _jwtHandler = ArduinoJsonJWT(DEFAULT_JWT_SECRET);
*/ std::list<User> _users;
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
protected: private:
/*
ArduinoJsonJWT _jwtHandler = ArduinoJsonJWT(DEFAULT_JWT_SECRET); * Lookup the user by JWT
std::list<User> _users; */
Authentication authenticateJWT(String jwt);
private:
/*
* Lookup the user by JWT
*/
Authentication authenticateJWT(String jwt);
/*
* Verify the payload is correct
*/
boolean validatePayload(JsonObject &parsedPayload, User *user);
/*
* Verify the payload is correct
*/
boolean validatePayload(JsonObject& parsedPayload, User* user);
}; };
#endif // end SecurityManager_h #endif // end SecurityManager_h

View File

@ -1,16 +1,20 @@
#include <SecuritySettingsService.h> #include <SecuritySettingsService.h>
SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : AdminSettingsService(server, fs, this, SECURITY_SETTINGS_PATH, SECURITY_SETTINGS_FILE), SecurityManager() {} SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) :
SecuritySettingsService::~SecuritySettingsService() {} AdminSettingsService(server, fs, this, SECURITY_SETTINGS_PATH, SECURITY_SETTINGS_FILE),
SecurityManager() {
}
SecuritySettingsService::~SecuritySettingsService() {
}
void SecuritySettingsService::readFromJsonObject(JsonObject& root) { void SecuritySettingsService::readFromJsonObject(JsonObject& root) {
// secret // secret
_jwtHandler.setSecret(root["jwt_secret"] | DEFAULT_JWT_SECRET); _jwtHandler.setSecret(root["jwt_secret"] | DEFAULT_JWT_SECRET);
// users // users
_users.clear(); _users.clear();
if (root["users"].is<JsonArray>()) { if (root["users"].is<JsonArray>()) {
for (JsonVariant user : root["users"].as<JsonArray>()) { for (JsonVariant user : root["users"].as<JsonArray>()) {
_users.push_back(User(user["username"], user["password"], user["admin"])); _users.push_back(User(user["username"], user["password"], user["admin"]));
} }
} }

View File

@ -8,17 +8,13 @@
#define SECURITY_SETTINGS_PATH "/rest/securitySettings" #define SECURITY_SETTINGS_PATH "/rest/securitySettings"
class SecuritySettingsService : public AdminSettingsService, public SecurityManager { class SecuritySettingsService : public AdminSettingsService, public SecurityManager {
public:
SecuritySettingsService(AsyncWebServer* server, FS* fs);
~SecuritySettingsService();
public: protected:
void readFromJsonObject(JsonObject& root);
SecuritySettingsService(AsyncWebServer* server, FS* fs); void writeToJsonObject(JsonObject& root);
~SecuritySettingsService();
protected:
void readFromJsonObject(JsonObject& root);
void writeToJsonObject(JsonObject& root);
}; };
#endif // end SecuritySettingsService_h #endif // end SecuritySettingsService_h

View File

@ -1,26 +1,24 @@
#ifndef SettingsPersistence_h #ifndef SettingsPersistence_h
#define SettingsPersistence_h #define SettingsPersistence_h
#include <ArduinoJson.h>
#include <AsyncJson.h>
#include <AsyncJsonWebHandler.h>
#include <ESPAsyncWebServer.h> #include <ESPAsyncWebServer.h>
#include <FS.h> #include <FS.h>
#include <ArduinoJson.h>
#include <AsyncJsonWebHandler.h>
#include <AsyncJson.h>
/** /**
* At the moment, not expecting settings service to have to deal with large JSON * At the moment, not expecting settings service to have to deal with large JSON
* files this could be made configurable fairly simply, it's exposed on * files this could be made configurable fairly simply, it's exposed on
* AsyncJsonWebHandler with a setter. * AsyncJsonWebHandler with a setter.
*/ */
#define MAX_SETTINGS_SIZE 1024 #define MAX_SETTINGS_SIZE 1024
/* /*
* Mixin for classes which need to save settings to/from a file on the the file system as JSON. * Mixin for classes which need to save settings to/from a file on the the file system as JSON.
*/ */
class SettingsPersistence { class SettingsPersistence {
protected:
protected:
// will store and retrieve config from the file system // will store and retrieve config from the file system
FS* _fs; FS* _fs;
@ -58,11 +56,11 @@ protected:
if (size <= MAX_SETTINGS_SIZE) { if (size <= MAX_SETTINGS_SIZE) {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(MAX_SETTINGS_SIZE); DynamicJsonDocument jsonDocument = DynamicJsonDocument(MAX_SETTINGS_SIZE);
DeserializationError error = deserializeJson(jsonDocument, configFile); DeserializationError error = deserializeJson(jsonDocument, configFile);
if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()){ if (error == DeserializationError::Ok && jsonDocument.is<JsonObject>()) {
JsonObject root = jsonDocument.as<JsonObject>(); JsonObject root = jsonDocument.as<JsonObject>();
readFromJsonObject(root); readFromJsonObject(root);
configFile.close(); configFile.close();
return; return;
} }
} }
configFile.close(); configFile.close();
@ -73,26 +71,26 @@ protected:
applyDefaultConfig(); applyDefaultConfig();
} }
// serialization routene, from local config to JsonObject
virtual void readFromJsonObject(JsonObject& root) {
}
virtual void writeToJsonObject(JsonObject& root) {
}
// serialization routene, from local config to JsonObject // We assume the readFromJsonObject supplies sensible defaults if an empty object
virtual void readFromJsonObject(JsonObject& root){} // is supplied, this virtual function allows that to be changed.
virtual void writeToJsonObject(JsonObject& root){} virtual void applyDefaultConfig() {
DynamicJsonDocument jsonDocument = DynamicJsonDocument(MAX_SETTINGS_SIZE);
JsonObject root = jsonDocument.to<JsonObject>();
readFromJsonObject(root);
}
// We assume the readFromJsonObject supplies sensible defaults if an empty object public:
// is supplied, this virtual function allows that to be changed. SettingsPersistence(FS* fs, char const* filePath) : _fs(fs), _filePath(filePath) {
virtual void applyDefaultConfig(){ }
DynamicJsonDocument jsonDocument = DynamicJsonDocument(MAX_SETTINGS_SIZE);
JsonObject root = jsonDocument.to<JsonObject>();
readFromJsonObject(root);
}
public:
SettingsPersistence(FS* fs, char const* filePath):
_fs(fs), _filePath(filePath) {}
virtual ~SettingsPersistence() {}
virtual ~SettingsPersistence() {
}
}; };
#endif // end SettingsPersistence #endif // end SettingsPersistence

View File

@ -5,65 +5,67 @@
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h>
#include <AsyncTCP.h> #include <AsyncTCP.h>
#include <WiFi.h>
#endif #endif
#include <SecurityManager.h>
#include <SettingsPersistence.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <AsyncJsonWebHandler.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <AsyncJsonCallbackResponse.h> #include <AsyncJsonCallbackResponse.h>
#include <AsyncJsonWebHandler.h>
#include <ESPAsyncWebServer.h>
#include <SecurityManager.h>
#include <SettingsPersistence.h>
/* /*
* Abstraction of a service which stores it's settings as JSON in a file system. * Abstraction of a service which stores it's settings as JSON in a file system.
*/ */
class SettingsService : public SettingsPersistence { class SettingsService : public SettingsPersistence {
public:
SettingsService(AsyncWebServer* server, FS* fs, char const* servicePath, char const* filePath) :
SettingsPersistence(fs, filePath),
_servicePath(servicePath) {
server->on(_servicePath, HTTP_GET, std::bind(&SettingsService::fetchConfig, this, std::placeholders::_1));
_updateHandler.setUri(servicePath);
_updateHandler.setMethod(HTTP_POST);
_updateHandler.setMaxContentLength(MAX_SETTINGS_SIZE);
_updateHandler.onRequest(
std::bind(&SettingsService::updateConfig, this, std::placeholders::_1, std::placeholders::_2));
server->addHandler(&_updateHandler);
}
public: virtual ~SettingsService() {
}
SettingsService(AsyncWebServer* server, FS* fs, char const* servicePath, char const* filePath): SettingsPersistence(fs, filePath), _servicePath(servicePath) { void begin() {
server->on(_servicePath, HTTP_GET, std::bind(&SettingsService::fetchConfig, this, std::placeholders::_1)); // read the initial data from the file system
readFromFS();
}
_updateHandler.setUri(servicePath); protected:
_updateHandler.setMethod(HTTP_POST);
_updateHandler.setMaxContentLength(MAX_SETTINGS_SIZE);
_updateHandler.onRequest(std::bind(&SettingsService::updateConfig, this, std::placeholders::_1, std::placeholders::_2));
server->addHandler(&_updateHandler);
}
virtual ~SettingsService() {}
void begin() {
// read the initial data from the file system
readFromFS();
}
protected:
char const* _servicePath; char const* _servicePath;
AsyncJsonWebHandler _updateHandler; AsyncJsonWebHandler _updateHandler;
virtual void fetchConfig(AsyncWebServerRequest *request) { virtual void fetchConfig(AsyncWebServerRequest* request) {
// handle the request // handle the request
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_SETTINGS_SIZE); AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_SETTINGS_SIZE);
JsonObject jsonObject = response->getRoot(); JsonObject jsonObject = response->getRoot();
writeToJsonObject(jsonObject); writeToJsonObject(jsonObject);
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }
virtual void updateConfig(AsyncWebServerRequest *request, JsonDocument &jsonDocument) { virtual void updateConfig(AsyncWebServerRequest* request, JsonDocument& jsonDocument) {
// handle the request // handle the request
if (jsonDocument.is<JsonObject>()){ if (jsonDocument.is<JsonObject>()) {
JsonObject newConfig = jsonDocument.as<JsonObject>(); JsonObject newConfig = jsonDocument.as<JsonObject>();
readFromJsonObject(newConfig); readFromJsonObject(newConfig);
writeToFS(); writeToFS();
// write settings back with a callback to reconfigure the wifi // write settings back with a callback to reconfigure the wifi
AsyncJsonCallbackResponse * response = new AsyncJsonCallbackResponse([this] () {onConfigUpdated();}, false, MAX_SETTINGS_SIZE); AsyncJsonCallbackResponse* response =
JsonObject jsonObject = response->getRoot(); new AsyncJsonCallbackResponse([this]() { onConfigUpdated(); }, false, MAX_SETTINGS_SIZE);
JsonObject jsonObject = response->getRoot();
writeToJsonObject(jsonObject); writeToJsonObject(jsonObject);
response->setLength(); response->setLength();
request->send(response); request->send(response);
@ -73,8 +75,8 @@ protected:
} }
// implement to perform action when config has been updated // implement to perform action when config has been updated
virtual void onConfigUpdated(){} virtual void onConfigUpdated() {
}
}; };
#endif // end SettingsService #endif // end SettingsService

View File

@ -2,54 +2,53 @@
#define Service_h #define Service_h
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h> #include <AsyncTCP.h>
#include <AsyncTCP.h> #include <WiFi.h>
#endif #endif
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <AsyncJsonWebHandler.h>
#include <AsyncJsonCallbackResponse.h> #include <AsyncJsonCallbackResponse.h>
#include <AsyncJsonWebHandler.h>
#include <ESPAsyncWebServer.h>
/** /**
* At the moment, not expecting services to have to deal with large JSON * At the moment, not expecting services to have to deal with large JSON
* files this could be made configurable fairly simply, it's exposed on * files this could be made configurable fairly simply, it's exposed on
* AsyncJsonWebHandler with a setter. * AsyncJsonWebHandler with a setter.
*/ */
#define MAX_SETTINGS_SIZE 1024 #define MAX_SETTINGS_SIZE 1024
/* /*
* Abstraction of a service which reads and writes data from an endpoint. * Abstraction of a service which reads and writes data from an endpoint.
* *
* Not currently used, but indended for use by features which do not * Not currently used, but indended for use by features which do not
* require setting persistance. * require setting persistance.
*/ */
class SimpleService { class SimpleService {
private:
private:
AsyncJsonWebHandler _updateHandler; AsyncJsonWebHandler _updateHandler;
void fetchConfig(AsyncWebServerRequest *request) { void fetchConfig(AsyncWebServerRequest* request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_SETTINGS_SIZE); AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_SETTINGS_SIZE);
JsonObject jsonObject = response->getRoot(); JsonObject jsonObject = response->getRoot();
writeToJsonObject(jsonObject); writeToJsonObject(jsonObject);
response->setLength(); response->setLength();
request->send(response); request->send(response);
} }
void updateConfig(AsyncWebServerRequest *request, JsonDocument &jsonDocument) { void updateConfig(AsyncWebServerRequest* request, JsonDocument& jsonDocument) {
if (jsonDocument.is<JsonObject>()) { if (jsonDocument.is<JsonObject>()) {
JsonObject newConfig = jsonDocument.as<JsonObject>(); JsonObject newConfig = jsonDocument.as<JsonObject>();
readFromJsonObject(newConfig); readFromJsonObject(newConfig);
// write settings back with a callback to reconfigure the wifi // write settings back with a callback to reconfigure the wifi
AsyncJsonCallbackResponse * response = new AsyncJsonCallbackResponse([this] () {onConfigUpdated();}, false, MAX_SETTINGS_SIZE); AsyncJsonCallbackResponse* response =
JsonObject jsonObject = response->getRoot(); new AsyncJsonCallbackResponse([this]() { onConfigUpdated(); }, false, MAX_SETTINGS_SIZE);
JsonObject jsonObject = response->getRoot();
writeToJsonObject(jsonObject); writeToJsonObject(jsonObject);
response->setLength(); response->setLength();
request->send(response); request->send(response);
@ -58,29 +57,31 @@ private:
} }
} }
protected: protected:
// reads the local config from the
virtual void readFromJsonObject(JsonObject& root) {
}
virtual void writeToJsonObject(JsonObject& root) {
}
// reads the local config from the // implement to perform action when config has been updated
virtual void readFromJsonObject(JsonObject& root) {} virtual void onConfigUpdated() {
virtual void writeToJsonObject(JsonObject& root) {} }
// implement to perform action when config has been updated public:
virtual void onConfigUpdated() {} SimpleService(AsyncWebServer* server, char const* servicePath) {
server->on(servicePath, HTTP_GET, std::bind(&SimpleService::fetchConfig, this, std::placeholders::_1));
public: _updateHandler.setUri(servicePath);
_updateHandler.setMethod(HTTP_POST);
SimpleService(AsyncWebServer* server, char const* servicePath) { _updateHandler.setMaxContentLength(MAX_SETTINGS_SIZE);
server->on(servicePath, HTTP_GET, std::bind(&SimpleService::fetchConfig, this, std::placeholders::_1)); _updateHandler.onRequest(
std::bind(&SimpleService::updateConfig, this, std::placeholders::_1, std::placeholders::_2));
_updateHandler.setUri(servicePath); server->addHandler(&_updateHandler);
_updateHandler.setMethod(HTTP_POST); }
_updateHandler.setMaxContentLength(MAX_SETTINGS_SIZE);
_updateHandler.onRequest(std::bind(&SimpleService::updateConfig, this, std::placeholders::_1, std::placeholders::_2));
server->addHandler(&_updateHandler);
}
virtual ~SimpleService() {}
virtual ~SimpleService() {
}
}; };
#endif // end SimpleService #endif // end SimpleService

View File

@ -1,20 +1,21 @@
#include <SystemStatus.h> #include <SystemStatus.h>
SystemStatus::SystemStatus(AsyncWebServer* server, SecurityManager* securityManager) { SystemStatus::SystemStatus(AsyncWebServer* server, SecurityManager* securityManager) {
server->on(SYSTEM_STATUS_SERVICE_PATH, HTTP_GET, server->on(SYSTEM_STATUS_SERVICE_PATH,
securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED) HTTP_GET,
); securityManager->wrapRequest(std::bind(&SystemStatus::systemStatus, this, std::placeholders::_1),
AuthenticationPredicates::IS_AUTHENTICATED));
} }
void SystemStatus::systemStatus(AsyncWebServerRequest *request) { void SystemStatus::systemStatus(AsyncWebServerRequest* request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE); AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_ESP_STATUS_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
#if defined(ESP8266) #if defined(ESP8266)
root["esp_platform"] = "esp8266"; root["esp_platform"] = "esp8266";
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
root["esp_platform"] = "esp32"; root["esp_platform"] = "esp32";
#endif #endif
root["cpu_freq_mhz"] = ESP.getCpuFreqMHz(); root["cpu_freq_mhz"] = ESP.getCpuFreqMHz();
root["free_heap"] = ESP.getFreeHeap(); root["free_heap"] = ESP.getFreeHeap();
root["sketch_size"] = ESP.getSketchSize(); root["sketch_size"] = ESP.getSketchSize();
root["free_sketch_space"] = ESP.getFreeSketchSpace(); root["free_sketch_space"] = ESP.getFreeSketchSpace();

View File

@ -2,31 +2,27 @@
#define SystemStatus_h #define SystemStatus_h
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h> #include <AsyncTCP.h>
#include <AsyncTCP.h> #include <WiFi.h>
#endif #endif
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <ESPAsyncWebServer.h>
#include <SecurityManager.h> #include <SecurityManager.h>
#define MAX_ESP_STATUS_SIZE 1024 #define MAX_ESP_STATUS_SIZE 1024
#define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus" #define SYSTEM_STATUS_SERVICE_PATH "/rest/systemStatus"
class SystemStatus { class SystemStatus {
public:
SystemStatus(AsyncWebServer* server, SecurityManager* securityManager);
public: private:
void systemStatus(AsyncWebServerRequest* request);
SystemStatus(AsyncWebServer* server, SecurityManager* securityManager);
private:
void systemStatus(AsyncWebServerRequest *request);
}; };
#endif // end SystemStatus_h #endif // end SystemStatus_h

View File

@ -1,29 +1,31 @@
#include <WiFiScanner.h> #include <WiFiScanner.h>
WiFiScanner::WiFiScanner(AsyncWebServer *server, SecurityManager* securityManager) { WiFiScanner::WiFiScanner(AsyncWebServer* server, SecurityManager* securityManager) {
server->on(SCAN_NETWORKS_SERVICE_PATH, HTTP_GET, server->on(SCAN_NETWORKS_SERVICE_PATH,
securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN) HTTP_GET,
); securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, this, std::placeholders::_1),
server->on(LIST_NETWORKS_SERVICE_PATH, HTTP_GET, AuthenticationPredicates::IS_ADMIN));
securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, std::placeholders::_1), AuthenticationPredicates::IS_ADMIN) server->on(LIST_NETWORKS_SERVICE_PATH,
); HTTP_GET,
securityManager->wrapRequest(std::bind(&WiFiScanner::listNetworks, this, std::placeholders::_1),
AuthenticationPredicates::IS_ADMIN));
}; };
void WiFiScanner::scanNetworks(AsyncWebServerRequest *request) { void WiFiScanner::scanNetworks(AsyncWebServerRequest* request) {
if (WiFi.scanComplete() != -1){ if (WiFi.scanComplete() != -1) {
WiFi.scanDelete(); WiFi.scanDelete();
WiFi.scanNetworks(true); WiFi.scanNetworks(true);
} }
request->send(202); request->send(202);
} }
void WiFiScanner::listNetworks(AsyncWebServerRequest *request) { void WiFiScanner::listNetworks(AsyncWebServerRequest* request) {
int numNetworks = WiFi.scanComplete(); int numNetworks = WiFi.scanComplete();
if (numNetworks > -1){ if (numNetworks > -1) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_WIFI_SCANNER_SIZE); AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_WIFI_SCANNER_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
JsonArray networks = root.createNestedArray("networks"); JsonArray networks = root.createNestedArray("networks");
for (int i=0; i<numNetworks ; i++){ for (int i = 0; i < numNetworks; i++) {
JsonObject network = networks.createNestedObject(); JsonObject network = networks.createNestedObject();
network["rssi"] = WiFi.RSSI(i); network["rssi"] = WiFi.RSSI(i);
network["ssid"] = WiFi.SSID(i); network["ssid"] = WiFi.SSID(i);
@ -32,14 +34,14 @@ void WiFiScanner::listNetworks(AsyncWebServerRequest *request) {
#if defined(ESP8266) #if defined(ESP8266)
network["encryption_type"] = convertEncryptionType(WiFi.encryptionType(i)); network["encryption_type"] = convertEncryptionType(WiFi.encryptionType(i));
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
network["encryption_type"] = (uint8_t) WiFi.encryptionType(i); network["encryption_type"] = (uint8_t)WiFi.encryptionType(i);
#endif #endif
} }
response->setLength(); response->setLength();
request->send(response); request->send(response);
} else if (numNetworks == -1){ } else if (numNetworks == -1) {
request->send(202); request->send(202);
}else{ } else {
scanNetworks(request); scanNetworks(request);
} }
} }
@ -47,11 +49,11 @@ void WiFiScanner::listNetworks(AsyncWebServerRequest *request) {
#if defined(ESP8266) #if defined(ESP8266)
/* /*
* Convert encryption type to standard used by ESP32 rather than the translated form which the esp8266 libaries expose. * Convert encryption type to standard used by ESP32 rather than the translated form which the esp8266 libaries expose.
* *
* This allows us to use a single set of mappings in the UI. * This allows us to use a single set of mappings in the UI.
*/ */
uint8_t WiFiScanner::convertEncryptionType(uint8_t encryptionType){ uint8_t WiFiScanner::convertEncryptionType(uint8_t encryptionType) {
switch (encryptionType){ switch (encryptionType) {
case ENC_TYPE_NONE: case ENC_TYPE_NONE:
return AUTH_OPEN; return AUTH_OPEN;
case ENC_TYPE_WEP: case ENC_TYPE_WEP:

View File

@ -2,18 +2,18 @@
#define WiFiScanner_h #define WiFiScanner_h
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h> #include <AsyncTCP.h>
#include <AsyncTCP.h> #include <WiFi.h>
#endif #endif
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <TimeLib.h> #include <ESPAsyncWebServer.h>
#include <SecurityManager.h> #include <SecurityManager.h>
#include <TimeLib.h>
#define SCAN_NETWORKS_SERVICE_PATH "/rest/scanNetworks" #define SCAN_NETWORKS_SERVICE_PATH "/rest/scanNetworks"
#define LIST_NETWORKS_SERVICE_PATH "/rest/listNetworks" #define LIST_NETWORKS_SERVICE_PATH "/rest/listNetworks"
@ -21,20 +21,16 @@
#define MAX_WIFI_SCANNER_SIZE 1024 #define MAX_WIFI_SCANNER_SIZE 1024
class WiFiScanner { class WiFiScanner {
public:
WiFiScanner(AsyncWebServer* server, SecurityManager* securityManager);
public: private:
void scanNetworks(AsyncWebServerRequest* request);
WiFiScanner(AsyncWebServer *server, SecurityManager* securityManager); void listNetworks(AsyncWebServerRequest* request);
private:
void scanNetworks(AsyncWebServerRequest *request);
void listNetworks(AsyncWebServerRequest *request);
#if defined(ESP8266) #if defined(ESP8266)
uint8_t convertEncryptionType(uint8_t encryptionType); uint8_t convertEncryptionType(uint8_t encryptionType);
#endif #endif
}; };
#endif // end WiFiScanner_h #endif // end WiFiScanner_h

View File

@ -1,73 +1,78 @@
#include <WiFiSettingsService.h> #include <WiFiSettingsService.h>
WiFiSettingsService::WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : AdminSettingsService(server, fs, securityManager, WIFI_SETTINGS_SERVICE_PATH, WIFI_SETTINGS_FILE) { WiFiSettingsService::WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) :
AdminSettingsService(server, fs, securityManager, WIFI_SETTINGS_SERVICE_PATH, WIFI_SETTINGS_FILE) {
// We want the device to come up in opmode=0 (WIFI_OFF), when erasing the flash this is not the default. // We want the device to come up in opmode=0 (WIFI_OFF), when erasing the flash this is not the default.
// If needed, we save opmode=0 before disabling persistence so the device boots with WiFi disabled in the future. // If needed, we save opmode=0 before disabling persistence so the device boots with WiFi disabled in the future.
if (WiFi.getMode() != WIFI_OFF) { if (WiFi.getMode() != WIFI_OFF) {
WiFi.mode(WIFI_OFF); WiFi.mode(WIFI_OFF);
} }
// Disable WiFi config persistance and auto reconnect // Disable WiFi config persistance and auto reconnect
WiFi.persistent(false); WiFi.persistent(false);
WiFi.setAutoReconnect(false); WiFi.setAutoReconnect(false);
#if defined(ESP8266) #if defined(ESP8266)
_onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1)); _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(
std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1));
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
// Init the wifi driver on ESP32 // Init the wifi driver on ESP32
WiFi.mode(WIFI_MODE_MAX); WiFi.mode(WIFI_MODE_MAX);
WiFi.mode(WIFI_MODE_NULL); WiFi.mode(WIFI_MODE_NULL);
WiFi.onEvent(std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2), WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); WiFi.onEvent(
std::bind(&WiFiSettingsService::onStationModeDisconnected, this, std::placeholders::_1, std::placeholders::_2),
WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
#endif #endif
} }
WiFiSettingsService::~WiFiSettingsService() {} WiFiSettingsService::~WiFiSettingsService() {
}
void WiFiSettingsService::begin() { void WiFiSettingsService::begin() {
SettingsService::begin(); SettingsService::begin();
reconfigureWiFiConnection(); reconfigureWiFiConnection();
} }
void WiFiSettingsService::readFromJsonObject(JsonObject& root){ void WiFiSettingsService::readFromJsonObject(JsonObject& root) {
_ssid = root["ssid"] | ""; _ssid = root["ssid"] | "";
_password = root["password"] | ""; _password = root["password"] | "";
_hostname = root["hostname"] | ""; _hostname = root["hostname"] | "";
_staticIPConfig = root["static_ip_config"] | false; _staticIPConfig = root["static_ip_config"] | false;
// extended settings // extended settings
readIP(root, "local_ip", _localIP); readIP(root, "local_ip", _localIP);
readIP(root, "gateway_ip", _gatewayIP); readIP(root, "gateway_ip", _gatewayIP);
readIP(root, "subnet_mask", _subnetMask); readIP(root, "subnet_mask", _subnetMask);
readIP(root, "dns_ip_1", _dnsIP1); readIP(root, "dns_ip_1", _dnsIP1);
readIP(root, "dns_ip_2", _dnsIP2); readIP(root, "dns_ip_2", _dnsIP2);
// Swap around the dns servers if 2 is populated but 1 is not // Swap around the dns servers if 2 is populated but 1 is not
if (_dnsIP1 == INADDR_NONE && _dnsIP2 != INADDR_NONE){ if (_dnsIP1 == INADDR_NONE && _dnsIP2 != INADDR_NONE) {
_dnsIP1 = _dnsIP2; _dnsIP1 = _dnsIP2;
_dnsIP2 = INADDR_NONE; _dnsIP2 = INADDR_NONE;
} }
// Turning off static ip config if we don't meet the minimum requirements // Turning off static ip config if we don't meet the minimum requirements
// of ipAddress, gateway and subnet. This may change to static ip only // of ipAddress, gateway and subnet. This may change to static ip only
// as sensible defaults can be assumed for gateway and subnet // as sensible defaults can be assumed for gateway and subnet
if (_staticIPConfig && (_localIP == INADDR_NONE || _gatewayIP == INADDR_NONE || _subnetMask == INADDR_NONE)){ if (_staticIPConfig && (_localIP == INADDR_NONE || _gatewayIP == INADDR_NONE || _subnetMask == INADDR_NONE)) {
_staticIPConfig = false; _staticIPConfig = false;
} }
} }
void WiFiSettingsService::writeToJsonObject(JsonObject& root){ void WiFiSettingsService::writeToJsonObject(JsonObject& root) {
// connection settings // connection settings
root["ssid"] = _ssid; root["ssid"] = _ssid;
root["password"] = _password; root["password"] = _password;
root["hostname"] = _hostname; root["hostname"] = _hostname;
root["static_ip_config"] = _staticIPConfig; root["static_ip_config"] = _staticIPConfig;
// extended settings // extended settings
writeIP(root, "local_ip", _localIP); writeIP(root, "local_ip", _localIP);
writeIP(root, "gateway_ip", _gatewayIP); writeIP(root, "gateway_ip", _gatewayIP);
writeIP(root, "subnet_mask", _subnetMask); writeIP(root, "subnet_mask", _subnetMask);
writeIP(root, "dns_ip_1", _dnsIP1); writeIP(root, "dns_ip_1", _dnsIP1);
writeIP(root, "dns_ip_2", _dnsIP2); writeIP(root, "dns_ip_2", _dnsIP2);
} }
void WiFiSettingsService::onConfigUpdated() { void WiFiSettingsService::onConfigUpdated() {
@ -75,21 +80,21 @@ void WiFiSettingsService::onConfigUpdated() {
} }
void WiFiSettingsService::reconfigureWiFiConnection() { void WiFiSettingsService::reconfigureWiFiConnection() {
// disconnect and de-configure wifi // disconnect and de-configure wifi
WiFi.disconnect(true); WiFi.disconnect(true);
// reset last connection attempt to force loop to reconnect immediately // reset last connection attempt to force loop to reconnect immediately
_lastConnectionAttempt = 0; _lastConnectionAttempt = 0;
} }
void WiFiSettingsService::readIP(JsonObject& root, String key, IPAddress& _ip){ void WiFiSettingsService::readIP(JsonObject& root, String key, IPAddress& _ip) {
if (!root[key].is<String>() || !_ip.fromString(root[key].as<String>())){ if (!root[key].is<String>() || !_ip.fromString(root[key].as<String>())) {
_ip = INADDR_NONE; _ip = INADDR_NONE;
} }
} }
void WiFiSettingsService::writeIP(JsonObject& root, String key, IPAddress& _ip){ void WiFiSettingsService::writeIP(JsonObject& root, String key, IPAddress& _ip) {
if (_ip != INADDR_NONE){ if (_ip != INADDR_NONE) {
root[key] = _ip.toString(); root[key] = _ip.toString();
} }
} }
@ -98,7 +103,7 @@ void WiFiSettingsService::loop() {
unsigned long currentMillis = millis(); unsigned long currentMillis = millis();
if (!_lastConnectionAttempt || (unsigned long)(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) { if (!_lastConnectionAttempt || (unsigned long)(currentMillis - _lastConnectionAttempt) >= WIFI_RECONNECTION_DELAY) {
_lastConnectionAttempt = currentMillis; _lastConnectionAttempt = currentMillis;
manageSTA(); manageSTA();
} }
} }
@ -109,13 +114,13 @@ void WiFiSettingsService::manageSTA() {
} }
// Connect or reconnect as required // Connect or reconnect as required
if ((WiFi.getMode() & WIFI_STA) == 0) { if ((WiFi.getMode() & WIFI_STA) == 0) {
Serial.println("Connecting to WiFi."); Serial.println("Connecting to WiFi.");
if (_staticIPConfig) { if (_staticIPConfig) {
// configure for static IP // configure for static IP
WiFi.config(_localIP, _gatewayIP, _subnetMask, _dnsIP1, _dnsIP2); WiFi.config(_localIP, _gatewayIP, _subnetMask, _dnsIP1, _dnsIP2);
} else { } else {
// configure for DHCP // configure for DHCP
#if defined(ESP8266) #if defined(ESP8266)
WiFi.config(INADDR_ANY, INADDR_ANY, INADDR_ANY); WiFi.config(INADDR_ANY, INADDR_ANY, INADDR_ANY);
WiFi.hostname(_hostname); WiFi.hostname(_hostname);
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
@ -137,4 +142,3 @@ void WiFiSettingsService::onStationModeDisconnected(WiFiEvent_t event, WiFiEvent
WiFi.disconnect(true); WiFi.disconnect(true);
} }
#endif #endif

View File

@ -9,50 +9,46 @@
#define WIFI_RECONNECTION_DELAY 1000 * 60 #define WIFI_RECONNECTION_DELAY 1000 * 60
class WiFiSettingsService : public AdminSettingsService { class WiFiSettingsService : public AdminSettingsService {
public:
WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
~WiFiSettingsService();
public: void begin();
void loop();
WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); protected:
~WiFiSettingsService(); void readFromJsonObject(JsonObject& root);
void writeToJsonObject(JsonObject& root);
void onConfigUpdated();
void begin(); private:
void loop(); // connection settings
String _ssid;
String _password;
String _hostname;
bool _staticIPConfig;
protected: // for the mangement delay loop
unsigned long _lastConnectionAttempt;
void readFromJsonObject(JsonObject& root); // optional configuration for static IP address
void writeToJsonObject(JsonObject& root); IPAddress _localIP;
void onConfigUpdated(); IPAddress _gatewayIP;
IPAddress _subnetMask;
private: IPAddress _dnsIP1;
// connection settings IPAddress _dnsIP2;
String _ssid;
String _password;
String _hostname;
bool _staticIPConfig;
// for the mangement delay loop
unsigned long _lastConnectionAttempt;
// optional configuration for static IP address
IPAddress _localIP;
IPAddress _gatewayIP;
IPAddress _subnetMask;
IPAddress _dnsIP1;
IPAddress _dnsIP2;
#if defined(ESP8266) #if defined(ESP8266)
WiFiEventHandler _onStationModeDisconnectedHandler; WiFiEventHandler _onStationModeDisconnectedHandler;
void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event);
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
#endif #endif
void readIP(JsonObject& root, String key, IPAddress& _ip);
void writeIP(JsonObject& root, String key, IPAddress& _ip);
void reconfigureWiFiConnection();
void manageSTA();
void readIP(JsonObject& root, String key, IPAddress& _ip);
void writeIP(JsonObject& root, String key, IPAddress& _ip);
void reconfigureWiFiConnection();
void manageSTA();
}; };
#endif // end WiFiSettingsService_h #endif // end WiFiSettingsService_h

View File

@ -1,17 +1,18 @@
#include <WiFiStatus.h> #include <WiFiStatus.h>
WiFiStatus::WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager) { WiFiStatus::WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager) {
server->on(WIFI_STATUS_SERVICE_PATH, HTTP_GET, server->on(WIFI_STATUS_SERVICE_PATH,
securityManager->wrapRequest(std::bind(&WiFiStatus::wifiStatus, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED) HTTP_GET,
); securityManager->wrapRequest(std::bind(&WiFiStatus::wifiStatus, this, std::placeholders::_1),
AuthenticationPredicates::IS_AUTHENTICATED));
#if defined(ESP8266) #if defined(ESP8266)
_onStationModeConnectedHandler = WiFi.onStationModeConnected(onStationModeConnected); _onStationModeConnectedHandler = WiFi.onStationModeConnected(onStationModeConnected);
_onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(onStationModeDisconnected); _onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(onStationModeDisconnected);
_onStationModeGotIPHandler = WiFi.onStationModeGotIP(onStationModeGotIP); _onStationModeGotIPHandler = WiFi.onStationModeGotIP(onStationModeGotIP);
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
WiFi.onEvent(onStationModeConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED); WiFi.onEvent(onStationModeConnected, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
WiFi.onEvent(onStationModeDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED); WiFi.onEvent(onStationModeDisconnected, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
WiFi.onEvent(onStationModeGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP); WiFi.onEvent(onStationModeGotIP, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
#endif #endif
} }
@ -50,12 +51,12 @@ void WiFiStatus::onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info) {
} }
#endif #endif
void WiFiStatus::wifiStatus(AsyncWebServerRequest *request) { void WiFiStatus::wifiStatus(AsyncWebServerRequest* request) {
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_WIFI_STATUS_SIZE); AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_WIFI_STATUS_SIZE);
JsonObject root = response->getRoot(); JsonObject root = response->getRoot();
wl_status_t status = WiFi.status(); wl_status_t status = WiFi.status();
root["status"] = (uint8_t) status; root["status"] = (uint8_t)status;
if (status == WL_CONNECTED){ if (status == WL_CONNECTED) {
root["local_ip"] = WiFi.localIP().toString(); root["local_ip"] = WiFi.localIP().toString();
root["mac_address"] = WiFi.macAddress(); root["mac_address"] = WiFi.macAddress();
root["rssi"] = WiFi.RSSI(); root["rssi"] = WiFi.RSSI();
@ -66,10 +67,10 @@ void WiFiStatus::wifiStatus(AsyncWebServerRequest *request) {
root["gateway_ip"] = WiFi.gatewayIP().toString(); root["gateway_ip"] = WiFi.gatewayIP().toString();
IPAddress dnsIP1 = WiFi.dnsIP(0); IPAddress dnsIP1 = WiFi.dnsIP(0);
IPAddress dnsIP2 = WiFi.dnsIP(1); IPAddress dnsIP2 = WiFi.dnsIP(1);
if (dnsIP1 != INADDR_NONE){ if (dnsIP1 != INADDR_NONE) {
root["dns_ip_1"] = dnsIP1.toString(); root["dns_ip_1"] = dnsIP1.toString();
} }
if (dnsIP2 != INADDR_NONE){ if (dnsIP2 != INADDR_NONE) {
root["dns_ip_2"] = dnsIP2.toString(); root["dns_ip_2"] = dnsIP2.toString();
} }
} }

View File

@ -2,16 +2,16 @@
#define WiFiStatus_h #define WiFiStatus_h
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h> #include <ESPAsyncTCP.h>
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
#include <WiFi.h> #include <AsyncTCP.h>
#include <AsyncTCP.h> #include <WiFi.h>
#endif #endif
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <AsyncJson.h> #include <AsyncJson.h>
#include <ESPAsyncWebServer.h>
#include <IPAddress.h> #include <IPAddress.h>
#include <SecurityManager.h> #include <SecurityManager.h>
@ -19,31 +19,27 @@
#define WIFI_STATUS_SERVICE_PATH "/rest/wifiStatus" #define WIFI_STATUS_SERVICE_PATH "/rest/wifiStatus"
class WiFiStatus { class WiFiStatus {
public:
WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager);
public: private:
WiFiStatus(AsyncWebServer* server, SecurityManager* securityManager);
private:
#if defined(ESP8266) #if defined(ESP8266)
// handler refrences for logging important WiFi events over serial // handler refrences for logging important WiFi events over serial
WiFiEventHandler _onStationModeConnectedHandler; WiFiEventHandler _onStationModeConnectedHandler;
WiFiEventHandler _onStationModeDisconnectedHandler; WiFiEventHandler _onStationModeDisconnectedHandler;
WiFiEventHandler _onStationModeGotIPHandler; WiFiEventHandler _onStationModeGotIPHandler;
// static functions for logging WiFi events to the UART // static functions for logging WiFi events to the UART
static void onStationModeConnected(const WiFiEventStationModeConnected& event); static void onStationModeConnected(const WiFiEventStationModeConnected& event);
static void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event); static void onStationModeDisconnected(const WiFiEventStationModeDisconnected& event);
static void onStationModeGotIP(const WiFiEventStationModeGotIP& event); static void onStationModeGotIP(const WiFiEventStationModeGotIP& event);
#elif defined(ESP_PLATFORM) #elif defined(ESP_PLATFORM)
// static functions for logging WiFi events to the UART // static functions for logging WiFi events to the UART
static void onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info); static void onStationModeConnected(WiFiEvent_t event, WiFiEventInfo_t info);
static void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info); static void onStationModeDisconnected(WiFiEvent_t event, WiFiEventInfo_t info);
static void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info); static void onStationModeGotIP(WiFiEvent_t event, WiFiEventInfo_t info);
#endif #endif
void wifiStatus(AsyncWebServerRequest *request); void wifiStatus(AsyncWebServerRequest* request);
}; };
#endif // end WiFiStatus_h #endif // end WiFiStatus_h

View File

@ -1,10 +1,12 @@
#include <DemoProject.h> #include <DemoProject.h>
DemoProject::DemoProject(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) : AdminSettingsService(server, fs, securityManager, DEMO_SETTINGS_PATH, DEMO_SETTINGS_FILE) { DemoProject::DemoProject(AsyncWebServer* server, FS* fs, SecurityManager* securityManager) :
pinMode(BLINK_LED, OUTPUT); AdminSettingsService(server, fs, securityManager, DEMO_SETTINGS_PATH, DEMO_SETTINGS_FILE) {
pinMode(BLINK_LED, OUTPUT);
} }
DemoProject::~DemoProject() {} DemoProject::~DemoProject() {
}
void DemoProject::loop() { void DemoProject::loop() {
unsigned delay = MAX_DELAY / 255 * (255 - _blinkSpeed); unsigned delay = MAX_DELAY / 255 * (255 - _blinkSpeed);
@ -23,4 +25,3 @@ void DemoProject::writeToJsonObject(JsonObject& root) {
// connection settings // connection settings
root["blink_speed"] = _blinkSpeed; root["blink_speed"] = _blinkSpeed;
} }

View File

@ -11,24 +11,19 @@
#define DEMO_SETTINGS_PATH "/rest/demoSettings" #define DEMO_SETTINGS_PATH "/rest/demoSettings"
class DemoProject : public AdminSettingsService { class DemoProject : public AdminSettingsService {
public:
DemoProject(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
~DemoProject();
public: void loop();
DemoProject(AsyncWebServer* server, FS* fs, SecurityManager* securityManager); private:
~DemoProject(); unsigned long _lastBlink = 0;
uint8_t _blinkSpeed = 255;
void loop();
private: protected:
void readFromJsonObject(JsonObject& root);
unsigned long _lastBlink = 0; void writeToJsonObject(JsonObject& root);
uint8_t _blinkSpeed = 255;
protected:
void readFromJsonObject(JsonObject& root);
void writeToJsonObject(JsonObject& root);
}; };
#endif #endif

View File

@ -1,5 +1,5 @@
#include <ESP8266React.h>
#include <DemoProject.h> #include <DemoProject.h>
#include <ESP8266React.h>
#include <FS.h> #include <FS.h>
#define SERIAL_BAUD_RATE 115200 #define SERIAL_BAUD_RATE 115200