Allow features to be disabled at build time (#143)
* Add framework for built-time feature selection * Allow MQTT, NTP, OTA features to be disabled at build time * Allow Project screens to be disabled at build time * Allow security features to be disabled at build time * Switch to std::function for StatefulService function aliases for greater flexibility * Bump various UI lib versions * Update docs
This commit is contained in:
@ -96,4 +96,4 @@ class APSettingsService : public StatefulService<APSettings> {
|
||||
void handleDNS();
|
||||
};
|
||||
|
||||
#endif // end APSettingsConfig_h
|
||||
#endif // end APSettingsConfig_h
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include <AuthenticationService.h>
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
AuthenticationService::AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager) :
|
||||
_securityManager(securityManager),
|
||||
_signInHandler(SIGN_IN_PATH,
|
||||
@ -42,3 +44,5 @@ void AuthenticationService::signIn(AsyncWebServerRequest* request, JsonVariant&
|
||||
AsyncWebServerResponse* response = request->beginResponse(401);
|
||||
request->send(response);
|
||||
}
|
||||
|
||||
#endif // end FT_ENABLED(FT_SECURITY)
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef AuthenticationService_H_
|
||||
#define AuthenticationService_H_
|
||||
|
||||
#include <Features.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
@ -10,6 +11,8 @@
|
||||
|
||||
#define MAX_AUTHENTICATION_SIZE 256
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
class AuthenticationService {
|
||||
public:
|
||||
AuthenticationService(AsyncWebServer* server, SecurityManager* securityManager);
|
||||
@ -23,4 +26,5 @@ class AuthenticationService {
|
||||
void verifyAuthorization(AsyncWebServerRequest* request);
|
||||
};
|
||||
|
||||
#endif // end SecurityManager_h
|
||||
#endif // end FT_ENABLED(FT_SECURITY)
|
||||
#endif // end SecurityManager_h
|
||||
|
@ -1,20 +1,29 @@
|
||||
#include <ESP8266React.h>
|
||||
|
||||
ESP8266React::ESP8266React(AsyncWebServer* server, FS* fs) :
|
||||
_featureService(server),
|
||||
_securitySettingsService(server, fs),
|
||||
_wifiSettingsService(server, fs, &_securitySettingsService),
|
||||
_apSettingsService(server, fs, &_securitySettingsService),
|
||||
_ntpSettingsService(server, fs, &_securitySettingsService),
|
||||
_otaSettingsService(server, fs, &_securitySettingsService),
|
||||
_mqttSettingsService(server, fs, &_securitySettingsService),
|
||||
_restartService(server, &_securitySettingsService),
|
||||
_factoryResetService(server, fs, &_securitySettingsService),
|
||||
_authenticationService(server, &_securitySettingsService),
|
||||
_wifiScanner(server, &_securitySettingsService),
|
||||
_wifiStatus(server, &_securitySettingsService),
|
||||
_ntpStatus(server, &_securitySettingsService),
|
||||
_apSettingsService(server, fs, &_securitySettingsService),
|
||||
_apStatus(server, &_securitySettingsService, &_apSettingsService),
|
||||
#if FT_ENABLED(FT_NTP)
|
||||
_ntpSettingsService(server, fs, &_securitySettingsService),
|
||||
_ntpStatus(server, &_securitySettingsService),
|
||||
#endif
|
||||
#if FT_ENABLED(FT_OTA)
|
||||
_otaSettingsService(server, fs, &_securitySettingsService),
|
||||
#endif
|
||||
#if FT_ENABLED(FT_MQTT)
|
||||
_mqttSettingsService(server, fs, &_securitySettingsService),
|
||||
_mqttStatus(server, &_mqttSettingsService, &_securitySettingsService),
|
||||
#endif
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
_authenticationService(server, &_securitySettingsService),
|
||||
#endif
|
||||
_restartService(server, &_securitySettingsService),
|
||||
_factoryResetService(server, fs, &_securitySettingsService),
|
||||
_systemStatus(server, &_securitySettingsService) {
|
||||
#ifdef PROGMEM_WWW
|
||||
// Serve static resources from PROGMEM
|
||||
@ -69,17 +78,29 @@ ESP8266React::ESP8266React(AsyncWebServer* server, FS* fs) :
|
||||
}
|
||||
|
||||
void ESP8266React::begin() {
|
||||
_securitySettingsService.begin();
|
||||
_wifiSettingsService.begin();
|
||||
_apSettingsService.begin();
|
||||
#if FT_ENABLED(FT_NTP)
|
||||
_ntpSettingsService.begin();
|
||||
#endif
|
||||
#if FT_ENABLED(FT_OTA)
|
||||
_otaSettingsService.begin();
|
||||
#endif
|
||||
#if FT_ENABLED(FT_MQTT)
|
||||
_mqttSettingsService.begin();
|
||||
#endif
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
_securitySettingsService.begin();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ESP8266React::loop() {
|
||||
_wifiSettingsService.loop();
|
||||
_apSettingsService.loop();
|
||||
#if FT_ENABLED(FT_OTA)
|
||||
_otaSettingsService.loop();
|
||||
#endif
|
||||
#if FT_ENABLED(FT_MQTT)
|
||||
_mqttSettingsService.loop();
|
||||
#endif
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <ESPAsyncTCP.h>
|
||||
#endif
|
||||
|
||||
#include <FeaturesService.h>
|
||||
#include <APSettingsService.h>
|
||||
#include <APStatus.h>
|
||||
#include <AuthenticationService.h>
|
||||
@ -42,9 +43,11 @@ class ESP8266React {
|
||||
return &_securitySettingsService;
|
||||
}
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
StatefulService<SecuritySettings>* getSecuritySettingsService() {
|
||||
return &_securitySettingsService;
|
||||
}
|
||||
#endif
|
||||
|
||||
StatefulService<WiFiSettings>* getWiFiSettingsService() {
|
||||
return &_wifiSettingsService;
|
||||
@ -54,14 +57,19 @@ class ESP8266React {
|
||||
return &_apSettingsService;
|
||||
}
|
||||
|
||||
#if FT_ENABLED(FT_NTP)
|
||||
StatefulService<NTPSettings>* getNTPSettingsService() {
|
||||
return &_ntpSettingsService;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FT_ENABLED(FT_OTA)
|
||||
StatefulService<OTASettings>* getOTASettingsService() {
|
||||
return &_otaSettingsService;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FT_ENABLED(FT_MQTT)
|
||||
StatefulService<MqttSettings>* getMqttSettingsService() {
|
||||
return &_mqttSettingsService;
|
||||
}
|
||||
@ -69,28 +77,36 @@ class ESP8266React {
|
||||
AsyncMqttClient* getMqttClient() {
|
||||
return _mqttSettingsService.getMqttClient();
|
||||
}
|
||||
#endif
|
||||
|
||||
void factoryReset() {
|
||||
_factoryResetService.factoryReset();
|
||||
}
|
||||
|
||||
private:
|
||||
FeaturesService _featureService;
|
||||
SecuritySettingsService _securitySettingsService;
|
||||
WiFiSettingsService _wifiSettingsService;
|
||||
APSettingsService _apSettingsService;
|
||||
NTPSettingsService _ntpSettingsService;
|
||||
OTASettingsService _otaSettingsService;
|
||||
MqttSettingsService _mqttSettingsService;
|
||||
RestartService _restartService;
|
||||
FactoryResetService _factoryResetService;
|
||||
|
||||
AuthenticationService _authenticationService;
|
||||
|
||||
WiFiScanner _wifiScanner;
|
||||
WiFiStatus _wifiStatus;
|
||||
NTPStatus _ntpStatus;
|
||||
APSettingsService _apSettingsService;
|
||||
APStatus _apStatus;
|
||||
#if FT_ENABLED(FT_NTP)
|
||||
NTPSettingsService _ntpSettingsService;
|
||||
NTPStatus _ntpStatus;
|
||||
#endif
|
||||
#if FT_ENABLED(FT_OTA)
|
||||
OTASettingsService _otaSettingsService;
|
||||
#endif
|
||||
#if FT_ENABLED(FT_MQTT)
|
||||
MqttSettingsService _mqttSettingsService;
|
||||
MqttStatus _mqttStatus;
|
||||
#endif
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
AuthenticationService _authenticationService;
|
||||
#endif
|
||||
RestartService _restartService;
|
||||
FactoryResetService _factoryResetService;
|
||||
SystemStatus _systemStatus;
|
||||
};
|
||||
|
||||
|
31
lib/framework/Features.h
Normal file
31
lib/framework/Features.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef Features_h
|
||||
#define Features_h
|
||||
|
||||
#define FT_ENABLED(feature) feature
|
||||
|
||||
// project feature off by default
|
||||
#ifndef FT_PROJECT
|
||||
#define FT_PROJECT 0
|
||||
#endif
|
||||
|
||||
// security feature on by default
|
||||
#ifndef FT_SECURITY
|
||||
#define FT_SECURITY 1
|
||||
#endif
|
||||
|
||||
// mqtt feature on by default
|
||||
#ifndef FT_MQTT
|
||||
#define FT_MQTT 1
|
||||
#endif
|
||||
|
||||
// ntp feature on by default
|
||||
#ifndef FT_NTP
|
||||
#define FT_NTP 1
|
||||
#endif
|
||||
|
||||
// mqtt feature on by default
|
||||
#ifndef FT_OTA
|
||||
#define FT_OTA 1
|
||||
#endif
|
||||
|
||||
#endif
|
37
lib/framework/FeaturesService.cpp
Normal file
37
lib/framework/FeaturesService.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
#include <FeaturesService.h>
|
||||
|
||||
FeaturesService::FeaturesService(AsyncWebServer* server) {
|
||||
server->on(FEATURES_SERVICE_PATH, HTTP_GET, std::bind(&FeaturesService::features, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void FeaturesService::features(AsyncWebServerRequest* request) {
|
||||
AsyncJsonResponse* response = new AsyncJsonResponse(false, MAX_FEATURES_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
#if FT_ENABLED(FT_PROJECT)
|
||||
root["project"] = true;
|
||||
#else
|
||||
root["project"] = false;
|
||||
#endif
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
root["security"] = true;
|
||||
#else
|
||||
root["security"] = false;
|
||||
#endif
|
||||
#if FT_ENABLED(FT_MQTT)
|
||||
root["mqtt"] = true;
|
||||
#else
|
||||
root["mqtt"] = false;
|
||||
#endif
|
||||
#if FT_ENABLED(FT_NTP)
|
||||
root["ntp"] = true;
|
||||
#else
|
||||
root["ntp"] = false;
|
||||
#endif
|
||||
#if FT_ENABLED(FT_OTA)
|
||||
root["ota"] = true;
|
||||
#else
|
||||
root["ota"] = false;
|
||||
#endif
|
||||
response->setLength();
|
||||
request->send(response);
|
||||
}
|
29
lib/framework/FeaturesService.h
Normal file
29
lib/framework/FeaturesService.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef FeaturesService_h
|
||||
#define FeaturesService_h
|
||||
|
||||
#include <Features.h>
|
||||
|
||||
#ifdef ESP32
|
||||
#include <WiFi.h>
|
||||
#include <AsyncTCP.h>
|
||||
#elif defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
#endif
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncJson.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
|
||||
#define MAX_FEATURES_SIZE 256
|
||||
#define FEATURES_SERVICE_PATH "/rest/features"
|
||||
|
||||
class FeaturesService {
|
||||
public:
|
||||
FeaturesService(AsyncWebServer* server);
|
||||
|
||||
private:
|
||||
void features(AsyncWebServerRequest* request);
|
||||
};
|
||||
|
||||
#endif
|
@ -22,4 +22,4 @@ class RestartService {
|
||||
void restart(AsyncWebServerRequest* request);
|
||||
};
|
||||
|
||||
#endif // end RestartService_h
|
||||
#endif // end RestartService_h
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef SecurityManager_h
|
||||
#define SecurityManager_h
|
||||
|
||||
#include <Features.h>
|
||||
#include <ArduinoJsonJWT.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <ESPUtils.h>
|
||||
@ -62,21 +63,24 @@ class AuthenticationPredicates {
|
||||
|
||||
class SecurityManager {
|
||||
public:
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
/*
|
||||
* Authenticate, returning the user if found
|
||||
*/
|
||||
virtual Authentication authenticate(const String& username, const String& password) = 0;
|
||||
|
||||
/*
|
||||
* Check the request header for the Authorization token
|
||||
*/
|
||||
virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 0;
|
||||
|
||||
/*
|
||||
* Generate a JWT for the user provided
|
||||
*/
|
||||
virtual String generateJWT(User* user) = 0;
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check the request header for the Authorization token
|
||||
*/
|
||||
virtual Authentication authenticateRequest(AsyncWebServerRequest* request) = 0;
|
||||
|
||||
/**
|
||||
* Filter a request with the provided predicate, only returning true if the predicate matches.
|
||||
*/
|
||||
@ -91,8 +95,8 @@ class SecurityManager {
|
||||
/**
|
||||
* Wrap the provided json request callback to provide validation against an AuthenticationPredicate.
|
||||
*/
|
||||
virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback,
|
||||
virtual ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest,
|
||||
AuthenticationPredicate predicate) = 0;
|
||||
};
|
||||
|
||||
#endif // end SecurityManager_h
|
||||
#endif // end SecurityManager_h
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include <SecuritySettingsService.h>
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) :
|
||||
_httpEndpoint(SecuritySettings::read, SecuritySettings::update, this, server, SECURITY_SETTINGS_PATH, this),
|
||||
_fsPersistence(SecuritySettings::read, SecuritySettings::update, this, fs, SECURITY_SETTINGS_FILE),
|
||||
@ -94,14 +96,45 @@ ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFu
|
||||
};
|
||||
}
|
||||
|
||||
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction callback,
|
||||
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest,
|
||||
AuthenticationPredicate predicate) {
|
||||
return [this, callback, predicate](AsyncWebServerRequest* request, JsonVariant& json) {
|
||||
return [this, onRequest, predicate](AsyncWebServerRequest* request, JsonVariant& json) {
|
||||
Authentication authentication = authenticateRequest(request);
|
||||
if (!predicate(authentication)) {
|
||||
request->send(401);
|
||||
return;
|
||||
}
|
||||
callback(request, json);
|
||||
onRequest(request, json);
|
||||
};
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
User ADMIN_USER = User(FACTORY_ADMIN_USERNAME, FACTORY_ADMIN_PASSWORD, true);
|
||||
|
||||
SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : SecurityManager() {
|
||||
}
|
||||
SecuritySettingsService::~SecuritySettingsService() {
|
||||
}
|
||||
|
||||
ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) {
|
||||
return [this, predicate](AsyncWebServerRequest* request) { return true; };
|
||||
}
|
||||
|
||||
// Return the admin user on all request - disabling security features
|
||||
Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest* request) {
|
||||
return Authentication(ADMIN_USER);
|
||||
}
|
||||
|
||||
// Return the function unwrapped
|
||||
ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest,
|
||||
AuthenticationPredicate predicate) {
|
||||
return onRequest;
|
||||
}
|
||||
|
||||
ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest,
|
||||
AuthenticationPredicate predicate) {
|
||||
return onRequest;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef SecuritySettingsService_h
|
||||
#define SecuritySettingsService_h
|
||||
|
||||
#include <Features.h>
|
||||
#include <SecurityManager.h>
|
||||
#include <HttpEndpoint.h>
|
||||
#include <FSPersistence.h>
|
||||
@ -24,6 +25,8 @@
|
||||
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
|
||||
#define SECURITY_SETTINGS_PATH "/rest/securitySettings"
|
||||
|
||||
#if FT_ENABLED(FT_SECURITY)
|
||||
|
||||
class SecuritySettings {
|
||||
public:
|
||||
String jwtSecret;
|
||||
@ -93,4 +96,19 @@ class SecuritySettingsService : public StatefulService<SecuritySettings>, public
|
||||
boolean validatePayload(JsonObject& parsedPayload, User* user);
|
||||
};
|
||||
|
||||
#endif // end SecuritySettingsService_h
|
||||
#else
|
||||
|
||||
class SecuritySettingsService : public SecurityManager {
|
||||
public:
|
||||
SecuritySettingsService(AsyncWebServer* server, FS* fs);
|
||||
~SecuritySettingsService();
|
||||
|
||||
// minimal set of functions to support framework with security settings disabled
|
||||
Authentication authenticateRequest(AsyncWebServerRequest* request);
|
||||
ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate);
|
||||
ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
|
||||
};
|
||||
|
||||
#endif // end FT_ENABLED(FT_SECURITY)
|
||||
#endif // end SecuritySettingsService_h
|
||||
|
@ -21,11 +21,11 @@ enum class StateUpdateResult {
|
||||
ERROR // There was a problem updating the state, propagation should not take place
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using JsonStateUpdater = StateUpdateResult (*)(JsonObject& root, T& settings);
|
||||
template <typename T>
|
||||
using JsonStateUpdater = std::function<StateUpdateResult(JsonObject& root, T& settings)>;
|
||||
|
||||
template <class T>
|
||||
using JsonStateReader = void (*)(T& settings, JsonObject& root);
|
||||
template <typename T>
|
||||
using JsonStateReader = std::function<void(T& settings, JsonObject& root)>;
|
||||
|
||||
typedef size_t update_handler_id_t;
|
||||
typedef std::function<void(const String& originId)> StateUpdateCallback;
|
||||
|
@ -27,4 +27,4 @@ class SystemStatus {
|
||||
void systemStatus(AsyncWebServerRequest* request);
|
||||
};
|
||||
|
||||
#endif // end SystemStatus_h
|
||||
#endif // end SystemStatus_h
|
||||
|
@ -67,4 +67,4 @@ uint8_t WiFiScanner::convertEncryptionType(uint8_t encryptionType) {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user