diff --git a/interface/src/authentication/Authentication.js b/interface/src/authentication/Authentication.js
index d379fb2..2de39e7 100644
--- a/interface/src/authentication/Authentication.js
+++ b/interface/src/authentication/Authentication.js
@@ -47,7 +47,7 @@ export function redirectingAuthorizedFetch(url, params) {
return new Promise(function (resolve, reject) {
authorizedFetch(url, params).then(response => {
if (response.status === 401) {
- history.go("/");
+ history.push("/");
} else {
resolve(response);
}
diff --git a/interface/src/components/RestComponent.js b/interface/src/components/RestComponent.js
index 4d29d40..288bd35 100644
--- a/interface/src/components/RestComponent.js
+++ b/interface/src/components/RestComponent.js
@@ -1,6 +1,6 @@
import React from 'react';
-import {withNotifier} from '../components/SnackbarNotification';
-
+import { withNotifier } from '../components/SnackbarNotification';
+import { redirectingAuthorizedFetch } from '../authentication/Authentication';
/*
* It is unlikely this application will grow complex enough to require redux.
*
@@ -16,11 +16,11 @@ export const restComponent = (endpointUrl, FormComponent) => {
constructor(props) {
super(props);
- this.state={
- data:null,
- fetched: false,
- errorMessage:null
- };
+ this.state = {
+ data: null,
+ fetched: false,
+ errorMessage: null
+ };
this.setState = this.setState.bind(this);
this.loadData = this.loadData.bind(this);
@@ -30,78 +30,78 @@ export const restComponent = (endpointUrl, FormComponent) => {
setData(data) {
this.setState({
- data:data,
- fetched: true,
- errorMessage:null
- });
+ data: data,
+ fetched: true,
+ errorMessage: null
+ });
}
loadData() {
this.setState({
- data:null,
- fetched: false,
- errorMessage:null
- });
- fetch(endpointUrl)
+ data: null,
+ fetched: false,
+ errorMessage: null
+ });
+ redirectingAuthorizedFetch(endpointUrl)
.then(response => {
if (response.status === 200) {
return response.json();
}
throw Error("Invalid status code: " + response.status);
})
- .then(json => {this.setState({data: json, fetched:true})})
- .catch(error =>{
+ .then(json => { this.setState({ data: json, fetched: true }) })
+ .catch(error => {
this.props.raiseNotification("Problem fetching: " + error.message);
- this.setState({data: null, fetched:true, errorMessage:error.message});
+ this.setState({ data: null, fetched: true, errorMessage: error.message });
});
}
saveData(e) {
- this.setState({fetched: false});
- fetch(endpointUrl, {
+ this.setState({ fetched: false });
+ redirectingAuthorizedFetch(endpointUrl, {
method: 'POST',
body: JSON.stringify(this.state.data),
headers: new Headers({
'Content-Type': 'application/json'
})
})
- .then(response => {
- if (response.status === 200) {
- return response.json();
- }
- throw Error("Invalid status code: " + response.status);
- })
- .then(json => {
- this.props.raiseNotification("Changes successfully applied.");
- this.setState({data: json, fetched:true});
- }).catch(error => {
- this.props.raiseNotification("Problem saving: " + error.message);
- this.setState({data: null, fetched:true, errorMessage:error.message});
- });
+ .then(response => {
+ if (response.status === 200) {
+ return response.json();
+ }
+ throw Error("Invalid status code: " + response.status);
+ })
+ .then(json => {
+ this.props.raiseNotification("Changes successfully applied.");
+ this.setState({ data: json, fetched: true });
+ }).catch(error => {
+ this.props.raiseNotification("Problem saving: " + error.message);
+ this.setState({ data: null, fetched: true, errorMessage: error.message });
+ });
}
handleValueChange = name => event => {
const { data } = this.state;
data[name] = event.target.value;
- this.setState({data});
+ this.setState({ data });
};
handleCheckboxChange = name => event => {
const { data } = this.state;
data[name] = event.target.checked;
- this.setState({data});
+ this.setState({ data });
}
render() {
return ;
+ handleValueChange={this.handleValueChange}
+ handleCheckboxChange={this.handleCheckboxChange}
+ setData={this.setData}
+ saveData={this.saveData}
+ loadData={this.loadData}
+ {...this.state}
+ {...this.props}
+ />;
}
}
diff --git a/interface/src/containers/WiFiNetworkScanner.js b/interface/src/containers/WiFiNetworkScanner.js
index e32e222..0f064d5 100644
--- a/interface/src/containers/WiFiNetworkScanner.js
+++ b/interface/src/containers/WiFiNetworkScanner.js
@@ -5,6 +5,7 @@ import { SCAN_NETWORKS_ENDPOINT, LIST_NETWORKS_ENDPOINT } from '../constants/E
import SectionContent from '../components/SectionContent';
import WiFiNetworkSelector from '../forms/WiFiNetworkSelector';
import {withNotifier} from '../components/SnackbarNotification';
+import { redirectingAuthorizedFetch } from '../authentication/Authentication';
const NUM_POLLS = 10
const POLLING_FREQUENCY = 500
@@ -38,7 +39,7 @@ class WiFiNetworkScanner extends Component {
scanNetworks() {
this.pollCount = 0;
this.setState({scanningForNetworks:true, networkList: null, errorMessage:null});
- fetch(SCAN_NETWORKS_ENDPOINT).then(response => {
+ redirectingAuthorizedFetch(SCAN_NETWORKS_ENDPOINT).then(response => {
if (response.status === 202) {
this.schedulePollTimeout();
return;
@@ -70,7 +71,7 @@ class WiFiNetworkScanner extends Component {
}
pollNetworkList() {
- fetch(LIST_NETWORKS_ENDPOINT)
+ redirectingAuthorizedFetch(LIST_NETWORKS_ENDPOINT)
.then(response => {
if (response.status === 200) {
return response.json();
diff --git a/src/APSettingsService.cpp b/src/APSettingsService.cpp
index 895766d..a5f966d 100644
--- a/src/APSettingsService.cpp
+++ b/src/APSettingsService.cpp
@@ -1,6 +1,6 @@
#include
-APSettingsService::APSettingsService(AsyncWebServer* server, FS* fs) : SettingsService(server, fs, 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) {
onConfigUpdated();
}
diff --git a/src/APSettingsService.h b/src/APSettingsService.h
index 3fcd2a7..252df7f 100644
--- a/src/APSettingsService.h
+++ b/src/APSettingsService.h
@@ -19,11 +19,11 @@
#define AP_SETTINGS_FILE "/config/apSettings.json"
#define AP_SETTINGS_SERVICE_PATH "/rest/apSettings"
-class APSettingsService : public SettingsService {
+class APSettingsService : public AdminSettingsService {
public:
- APSettingsService(AsyncWebServer* server, FS* fs);
+ APSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
~APSettingsService();
void loop();
diff --git a/src/ArduinoJsonJWT.cpp b/src/ArduinoJsonJWT.cpp
index fddfc03..0c945dc 100644
--- a/src/ArduinoJsonJWT.cpp
+++ b/src/ArduinoJsonJWT.cpp
@@ -3,7 +3,11 @@
ArduinoJsonJWT::ArduinoJsonJWT(String secret) : _secret(secret) { }
void ArduinoJsonJWT::setSecret(String secret){
- _secret = secret;
+ _secret = secret;
+}
+
+String ArduinoJsonJWT::getSecret(){
+ return _secret;
}
/*
diff --git a/src/ArduinoJsonJWT.h b/src/ArduinoJsonJWT.h
index 829041b..8d29e39 100644
--- a/src/ArduinoJsonJWT.h
+++ b/src/ArduinoJsonJWT.h
@@ -31,6 +31,8 @@ public:
ArduinoJsonJWT(String secret);
void setSecret(String secret);
+ String getSecret();
+
String buildJWT(JsonObject &payload);
void parseJWT(String jwt, JsonDocument &jsonDocument);
};
diff --git a/src/AsyncAuthJsonWebHandler.h b/src/AsyncAuthJsonWebHandler.h
deleted file mode 100644
index a767a9a..0000000
--- a/src/AsyncAuthJsonWebHandler.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef AsyncAuthJsonWebHandler_H_
-#define AsyncAuthJsonWebHandler_H_
-
-#include
-#include
-#include
-#include
-
-typedef std::function AuthenticationJsonRequestCallback;
-
-/**
- * Extends AsyncJsonWebHandler with a wrapper which verifies the user is authenticated.
- *
- * TODO - Extend with role checking support, possibly with a callback to verify the user.
- */
-class AsyncAuthJsonWebHandler: public AsyncJsonWebHandler {
-
- private:
- SecurityManager *_securityManager;
- using AsyncJsonWebHandler::onRequest;
-
- public:
-
- AsyncAuthJsonWebHandler() :
- AsyncJsonWebHandler(), _securityManager(NULL) {}
-
- ~AsyncAuthJsonWebHandler() {}
-
- void setSecurityManager(SecurityManager *securityManager) {
- _securityManager = securityManager;
- }
-
- void onRequest(AuthenticationJsonRequestCallback callback) {
- AsyncJsonWebHandler::onRequest([this, callback](AsyncWebServerRequest *request, JsonDocument &jsonDocument) {
- if(!_securityManager) {
- Serial.print("Security manager not configured for endpoint: ");
- Serial.println(_uri);
- request->send(500);
- return;
- }
- Authentication authentication = _securityManager->authenticateRequest(request);
- if (!authentication.isAuthenticated()) {
- request->send(401);
- return;
- }
- callback(request, jsonDocument, authentication);
- });
- }
-
-};
-
-#endif // end AsyncAuthJsonWebHandler_H_
\ No newline at end of file
diff --git a/src/AuthenticationService.cpp b/src/AuthenticationService.cpp
index 1a9e321..c67f091 100644
--- a/src/AuthenticationService.cpp
+++ b/src/AuthenticationService.cpp
@@ -6,7 +6,7 @@ AuthenticationService::AuthenticationService(AsyncWebServer* server, SecurityMan
_signInHandler.setUri(SIGN_IN_PATH);
_signInHandler.setMethod(HTTP_POST);
- _signInHandler.setMaxContentLength(MAX_SECURITY_MANAGER_SETTINGS_SIZE);
+ _signInHandler.setMaxContentLength(MAX_AUTHENTICATION_SIZE);
_signInHandler.onRequest(std::bind(&AuthenticationService::signIn, this, std::placeholders::_1, std::placeholders::_2));
server->addHandler(&_signInHandler);
}
@@ -31,7 +31,7 @@ void AuthenticationService::signIn(AsyncWebServerRequest *request, JsonDocument
Authentication authentication = _securityManager->authenticate(username, password);
if (authentication.isAuthenticated()) {
User* user = authentication.getUser();
- AsyncJsonResponse * response = new AsyncJsonResponse(MAX_USERS_SIZE);
+ AsyncJsonResponse * response = new AsyncJsonResponse(MAX_AUTHENTICATION_SIZE);
JsonObject jsonObject = response->getRoot();
jsonObject["access_token"] = _securityManager->generateJWT(user);
response->setLength();
diff --git a/src/AuthenticationService.h b/src/AuthenticationService.h
index 71d44f9..15d2941 100644
--- a/src/AuthenticationService.h
+++ b/src/AuthenticationService.h
@@ -2,6 +2,9 @@
#define AuthenticationService_H_
#include
+#include
+#include
+#include
#define VERIFY_AUTHORIZATION_PATH "/rest/verifyAuthorization"
#define SIGN_IN_PATH "/rest/signIn"
diff --git a/src/NTPSettingsService.cpp b/src/NTPSettingsService.cpp
index 99962a2..b09e025 100644
--- a/src/NTPSettingsService.cpp
+++ b/src/NTPSettingsService.cpp
@@ -1,6 +1,6 @@
#include
-NTPSettingsService::NTPSettingsService(AsyncWebServer* server, FS* fs) : SettingsService(server, fs, 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)
_onStationModeDisconnectedHandler = WiFi.onStationModeDisconnected(std::bind(&NTPSettingsService::onStationModeDisconnected, this, std::placeholders::_1));
diff --git a/src/NTPSettingsService.h b/src/NTPSettingsService.h
index c3624e7..e24f237 100644
--- a/src/NTPSettingsService.h
+++ b/src/NTPSettingsService.h
@@ -17,11 +17,11 @@
#define NTP_SETTINGS_FILE "/config/ntpSettings.json"
#define NTP_SETTINGS_SERVICE_PATH "/rest/ntpSettings"
-class NTPSettingsService : public SettingsService {
+class NTPSettingsService : public AdminSettingsService {
public:
- NTPSettingsService(AsyncWebServer* server, FS* fs);
+ NTPSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
~NTPSettingsService();
void loop();
diff --git a/src/OTASettingsService.cpp b/src/OTASettingsService.cpp
index 64e98c2..2c9490a 100644
--- a/src/OTASettingsService.cpp
+++ b/src/OTASettingsService.cpp
@@ -1,6 +1,6 @@
#include
-OTASettingsService::OTASettingsService(AsyncWebServer* server, FS* fs) : SettingsService(server, fs, 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)
_onStationModeGotIPHandler = WiFi.onStationModeGotIP(std::bind(&OTASettingsService::onStationModeGotIP, this, std::placeholders::_1));
#elif defined(ESP_PLATFORM)
diff --git a/src/OTASettingsService.h b/src/OTASettingsService.h
index 32e76ed..27993a8 100644
--- a/src/OTASettingsService.h
+++ b/src/OTASettingsService.h
@@ -19,11 +19,11 @@
#define OTA_SETTINGS_FILE "/config/otaSettings.json"
#define OTA_SETTINGS_SERVICE_PATH "/rest/otaSettings"
-class OTASettingsService : public SettingsService {
+class OTASettingsService : public AdminSettingsService {
public:
- OTASettingsService(AsyncWebServer* server, FS* fs);
+ OTASettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
~OTASettingsService();
void loop();
diff --git a/src/SecurityManager.cpp b/src/SecurityManager.cpp
index 36bd636..e973d0d 100644
--- a/src/SecurityManager.cpp
+++ b/src/SecurityManager.cpp
@@ -1,41 +1,5 @@
#include
-SecurityManager::SecurityManager(AsyncWebServer* server, FS* fs) : SettingsService(server, fs, SECURITY_SETTINGS_PATH, SECURITY_SETTINGS_FILE) {}
-SecurityManager::~SecurityManager() {}
-
-void SecurityManager::readFromJsonObject(JsonObject& root) {
- // secret
- _jwtSecret = root["jwt_secret"] | DEFAULT_JWT_SECRET;
- _jwtHandler.setSecret(_jwtSecret);
-
- // users
- _users.clear();
- if (root["users"].is()) {
- for (JsonVariant user : root["users"].as()) {
- _users.push_back(User(user["username"], user["password"], user["admin"]));
- }
- }
-}
-
-
-void SecurityManager::writeToJsonObject(JsonObject& root) {
- // secret
- root["jwt_secret"] = _jwtSecret;
-
- // users
- JsonArray users = root.createNestedArray("users");
- for (User _user : _users) {
- JsonObject user = users.createNestedObject();
- user["username"] = _user.getUsername();
- user["password"] = _user.getPassword();
- user["admin"] = _user.isAdmin();
- }
-}
-
-void SecurityManager::begin() {
- readFromFS();
-}
-
Authentication SecurityManager::authenticateRequest(AsyncWebServerRequest *request) {
AsyncWebHeader* authorizationHeader = request->getHeader(AUTHORIZATION_HEADER);
if (authorizationHeader) {
@@ -90,3 +54,15 @@ String SecurityManager::generateJWT(User *user) {
populateJWTPayload(payload, user);
return _jwtHandler.buildJWT(payload);
}
+
+ArRequestHandlerFunction SecurityManager::wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) {
+ return [this, onRequest, predicate](AsyncWebServerRequest *request){
+ Authentication authentication = authenticateRequest(request);
+ if (!predicate(authentication)) {
+ request->send(401);
+ return;
+ }
+ onRequest(request);
+ };
+}
+
\ No newline at end of file
diff --git a/src/SecurityManager.h b/src/SecurityManager.h
index c289b81..0b35fb9 100644
--- a/src/SecurityManager.h
+++ b/src/SecurityManager.h
@@ -2,27 +2,16 @@
#define SecurityManager_h
#include
-
-#include
-#include
-#include
#include
+#include
#define DEFAULT_JWT_SECRET "esp8266-react"
-#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
-
-#define SECURITY_SETTINGS_PATH "/rest/securitySettings"
-
#define AUTHORIZATION_HEADER "Authorization"
#define AUTHORIZATION_HEADER_PREFIX "Bearer "
#define AUTHORIZATION_HEADER_PREFIX_LEN 7
#define MAX_JWT_SIZE 128
-#define MAX_SECURITY_MANAGER_SETTINGS_SIZE 512
-#define SECURITY_MANAGER_MAX_USERS 5
-
-#define MAX_USERS_SIZE 1024
class User {
private:
@@ -62,15 +51,25 @@ class Authentication {
}
};
-class SecurityManager : public SettingsService {
+typedef std::function AuthenticationPredicate;
+
+class AuthenticationPredicates {
+ public:
+ static bool NONE_REQUIRED(Authentication &authentication) {
+ return true;
+ };
+ static bool IS_AUTHENTICATED(Authentication &authentication) {
+ return authentication.isAuthenticated();
+ };
+ static bool IS_ADMIN(Authentication &authentication) {
+ return authentication.isAuthenticated() && authentication.getUser()->isAdmin();
+ };
+};
+
+class SecurityManager {
public:
- SecurityManager(AsyncWebServer* server, FS* fs);
- ~SecurityManager();
-
- void begin();
-
/*
* Authenticate, returning the user if found
*/
@@ -86,21 +85,17 @@ class SecurityManager : public SettingsService {
*/
String generateJWT(User *user);
+ /**
+ * Wrap the provided request to provide validation against an AuthenticationPredicate.
+ */
+ ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate);
+
protected:
- void readFromJsonObject(JsonObject& root);
- void writeToJsonObject(JsonObject& root);
-
- private:
- // jwt handler
ArduinoJsonJWT _jwtHandler = ArduinoJsonJWT(DEFAULT_JWT_SECRET);
-
- // access point settings
- String _jwtSecret;
std::list _users;
- // endpoint functions
- void fetchUsers(AsyncWebServerRequest *request);
+ private:
/*
* Lookup the user by JWT
diff --git a/src/SecuritySettingsService.cpp b/src/SecuritySettingsService.cpp
new file mode 100644
index 0000000..51c355d
--- /dev/null
+++ b/src/SecuritySettingsService.cpp
@@ -0,0 +1,35 @@
+#include
+
+SecuritySettingsService::SecuritySettingsService(AsyncWebServer* server, FS* fs) : AdminSettingsService(server, fs, this, SECURITY_SETTINGS_PATH, SECURITY_SETTINGS_FILE), SecurityManager() {}
+SecuritySettingsService::~SecuritySettingsService() {}
+
+void SecuritySettingsService::readFromJsonObject(JsonObject& root) {
+ // secret
+ _jwtHandler.setSecret(root["jwt_secret"] | DEFAULT_JWT_SECRET);
+
+ // users
+ _users.clear();
+ if (root["users"].is()) {
+ for (JsonVariant user : root["users"].as()) {
+ _users.push_back(User(user["username"], user["password"], user["admin"]));
+ }
+ }
+}
+
+void SecuritySettingsService::writeToJsonObject(JsonObject& root) {
+ // secret
+ root["jwt_secret"] = _jwtHandler.getSecret();
+
+ // users
+ JsonArray users = root.createNestedArray("users");
+ for (User _user : _users) {
+ JsonObject user = users.createNestedObject();
+ user["username"] = _user.getUsername();
+ user["password"] = _user.getPassword();
+ user["admin"] = _user.isAdmin();
+ }
+}
+
+void SecuritySettingsService::begin() {
+ readFromFS();
+}
diff --git a/src/SecuritySettingsService.h b/src/SecuritySettingsService.h
new file mode 100644
index 0000000..7e5b622
--- /dev/null
+++ b/src/SecuritySettingsService.h
@@ -0,0 +1,26 @@
+#ifndef SecuritySettingsService_h
+#define SecuritySettingsService_h
+
+#include
+#include
+
+#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
+#define SECURITY_SETTINGS_PATH "/rest/securitySettings"
+
+class SecuritySettingsService : public AdminSettingsService, public SecurityManager {
+
+ public:
+
+ SecuritySettingsService(AsyncWebServer* server, FS* fs);
+ ~SecuritySettingsService();
+
+ void begin();
+
+ protected:
+
+ void readFromJsonObject(JsonObject& root);
+ void writeToJsonObject(JsonObject& root);
+
+};
+
+#endif // end SecuritySettingsService_h
\ No newline at end of file
diff --git a/src/SettingsService.h b/src/SettingsService.h
index 5d7cab0..ecf0d6a 100644
--- a/src/SettingsService.h
+++ b/src/SettingsService.h
@@ -9,54 +9,19 @@
#include
#endif
+#include
#include
#include
#include
#include
#include
+
/*
* Abstraction of a service which stores it's settings as JSON in a file system.
*/
class SettingsService : public SettingsPersistence {
-private:
-
- AsyncJsonWebHandler _updateHandler;
-
- void fetchConfig(AsyncWebServerRequest *request){
- AsyncJsonResponse * response = new AsyncJsonResponse(MAX_SETTINGS_SIZE);
- JsonObject jsonObject = response->getRoot();
- writeToJsonObject(jsonObject);
- response->setLength();
- request->send(response);
- }
-
- void updateConfig(AsyncWebServerRequest *request, JsonDocument &jsonDocument) {
- if (jsonDocument.is()){
- JsonObject newConfig = jsonDocument.as();
- readFromJsonObject(newConfig);
- writeToFS();
-
- // write settings back with a callback to reconfigure the wifi
- AsyncJsonCallbackResponse * response = new AsyncJsonCallbackResponse([this] () {onConfigUpdated();}, MAX_SETTINGS_SIZE);
- JsonObject jsonObject = response->getRoot();
- writeToJsonObject(jsonObject);
- response->setLength();
- request->send(response);
- } else {
- request->send(400);
- }
- }
-
- protected:
-
- // will serve setting endpoints from here
- AsyncWebServer* _server;
-
- // implement to perform action when config has been updated
- virtual void onConfigUpdated(){}
-
public:
SettingsService(AsyncWebServer* server, FS* fs, char const* servicePath, char const* filePath):
@@ -79,6 +44,81 @@ private:
readFromFS();
}
+protected:
+ // will serve setting endpoints from here
+ AsyncWebServer* _server;
+
+ AsyncJsonWebHandler _updateHandler;
+
+ virtual void fetchConfig(AsyncWebServerRequest *request) {
+ // handle the request
+ AsyncJsonResponse * response = new AsyncJsonResponse(MAX_SETTINGS_SIZE);
+ JsonObject jsonObject = response->getRoot();
+ writeToJsonObject(jsonObject);
+ response->setLength();
+ request->send(response);
+ }
+
+ virtual void updateConfig(AsyncWebServerRequest *request, JsonDocument &jsonDocument) {
+ // handle the request
+ if (jsonDocument.is()){
+ JsonObject newConfig = jsonDocument.as();
+ readFromJsonObject(newConfig);
+ writeToFS();
+
+ // write settings back with a callback to reconfigure the wifi
+ AsyncJsonCallbackResponse * response = new AsyncJsonCallbackResponse([this] () {onConfigUpdated();}, MAX_SETTINGS_SIZE);
+ JsonObject jsonObject = response->getRoot();
+ writeToJsonObject(jsonObject);
+ response->setLength();
+ request->send(response);
+ } else {
+ request->send(400);
+ }
+ }
+
+ // implement to perform action when config has been updated
+ virtual void onConfigUpdated(){}
+
+};
+
+class AdminSettingsService : public SettingsService {
+ public:
+ AdminSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager, char const* servicePath, char const* filePath):
+ SettingsService(server, fs, servicePath, filePath), _securityManager(securityManager) {
+ }
+
+ protected:
+ // will validate the requests with the security manager
+ SecurityManager* _securityManager;
+
+ void fetchConfig(AsyncWebServerRequest *request) {
+ // verify the request against the predicate
+ Authentication authentication = _securityManager->authenticateRequest(request);
+ if (!getAuthenticationPredicate()(authentication)) {
+ request->send(401);
+ return;
+ }
+ // delegate to underlying implemetation
+ SettingsService::fetchConfig(request);
+ }
+
+ void updateConfig(AsyncWebServerRequest *request, JsonDocument &jsonDocument) {
+ // verify the request against the predicate
+ Authentication authentication = _securityManager->authenticateRequest(request);
+ if (!getAuthenticationPredicate()(authentication)) {
+ request->send(401);
+ return;
+ }
+ // delegate to underlying implemetation
+ SettingsService::updateConfig(request, jsonDocument);
+ }
+
+ // override to override the default authentication predicate, IS_ADMIN
+ AuthenticationPredicate getAuthenticationPredicate() {
+ return AuthenticationPredicates::IS_ADMIN;
+ }
+
};
#endif // end SettingsService
diff --git a/src/WiFiScanner.cpp b/src/WiFiScanner.cpp
index 7bf1591..e3277a5 100644
--- a/src/WiFiScanner.cpp
+++ b/src/WiFiScanner.cpp
@@ -1,8 +1,12 @@
#include
-WiFiScanner::WiFiScanner(AsyncWebServer *server) : _server(server) {
- _server->on(SCAN_NETWORKS_SERVICE_PATH, HTTP_GET, std::bind(&WiFiScanner::scanNetworks, this, std::placeholders::_1));
- _server->on(LIST_NETWORKS_SERVICE_PATH, HTTP_GET, std::bind(&WiFiScanner::listNetworks, this, std::placeholders::_1));
+WiFiScanner::WiFiScanner(AsyncWebServer *server, SecurityManager* securityManager) : _server(server) {
+ _server->on(SCAN_NETWORKS_SERVICE_PATH, HTTP_GET,
+ securityManager->wrapRequest(std::bind(&WiFiScanner::scanNetworks, 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) {
diff --git a/src/WiFiScanner.h b/src/WiFiScanner.h
index 10c4d7a..ca14db0 100644
--- a/src/WiFiScanner.h
+++ b/src/WiFiScanner.h
@@ -13,6 +13,7 @@
#include
#include
#include
+#include
#define SCAN_NETWORKS_SERVICE_PATH "/rest/scanNetworks"
#define LIST_NETWORKS_SERVICE_PATH "/rest/listNetworks"
@@ -23,7 +24,7 @@ class WiFiScanner {
public:
- WiFiScanner(AsyncWebServer *server);
+ WiFiScanner(AsyncWebServer *server, SecurityManager* securityManager);
private:
diff --git a/src/WiFiSettingsService.cpp b/src/WiFiSettingsService.cpp
index 2f65d5e..9e6c5c2 100644
--- a/src/WiFiSettingsService.cpp
+++ b/src/WiFiSettingsService.cpp
@@ -1,6 +1,6 @@
#include
-WiFiSettingsService::WiFiSettingsService(AsyncWebServer* server, FS* fs) : SettingsService(server, fs, 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) {}
WiFiSettingsService::~WiFiSettingsService() {}
diff --git a/src/WiFiSettingsService.h b/src/WiFiSettingsService.h
index 18949c6..1675471 100644
--- a/src/WiFiSettingsService.h
+++ b/src/WiFiSettingsService.h
@@ -7,11 +7,11 @@
#define WIFI_SETTINGS_FILE "/config/wifiSettings.json"
#define WIFI_SETTINGS_SERVICE_PATH "/rest/wifiSettings"
-class WiFiSettingsService : public SettingsService {
+class WiFiSettingsService : public AdminSettingsService {
public:
- WiFiSettingsService(AsyncWebServer* server, FS* fs);
+ WiFiSettingsService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager);
~WiFiSettingsService();
void begin();
diff --git a/src/main.cpp b/src/main.cpp
index 694fde9..9bfbe87 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -11,7 +11,7 @@
#include
-#include
+#include
#include
#include
#include
@@ -27,15 +27,14 @@
AsyncWebServer server(80);
-SecurityManager securityManager = SecurityManager(&server, &SPIFFS);
+SecuritySettingsService securitySettingsService = SecuritySettingsService(&server, &SPIFFS);
+WiFiSettingsService wifiSettingsService = WiFiSettingsService(&server, &SPIFFS, &securitySettingsService);
+APSettingsService apSettingsService = APSettingsService(&server, &SPIFFS, &securitySettingsService);
+NTPSettingsService ntpSettingsService = NTPSettingsService(&server, &SPIFFS, &securitySettingsService);
+OTASettingsService otaSettingsService = OTASettingsService(&server, &SPIFFS, &securitySettingsService);
+AuthenticationService authenticationService = AuthenticationService(&server, &securitySettingsService);
-WiFiSettingsService wifiSettingsService = WiFiSettingsService(&server, &SPIFFS);
-APSettingsService apSettingsService = APSettingsService(&server, &SPIFFS);
-NTPSettingsService ntpSettingsService = NTPSettingsService(&server, &SPIFFS);
-OTASettingsService otaSettingsService = OTASettingsService(&server, &SPIFFS);
-AuthenticationService authenticationService = AuthenticationService(&server, &securityManager);
-
-WiFiScanner wifiScanner = WiFiScanner(&server);
+WiFiScanner wifiScanner = WiFiScanner(&server, &securitySettingsService);
WiFiStatus wifiStatus = WiFiStatus(&server);
NTPStatus ntpStatus = NTPStatus(&server);
APStatus apStatus = APStatus(&server);
@@ -48,8 +47,8 @@ void setup() {
Serial.begin(SERIAL_BAUD_RATE);
SPIFFS.begin();
- // start security manager
- securityManager.begin();
+ // start security settings service first
+ securitySettingsService.begin();
// start services
ntpSettingsService.begin();