WIP - some documentation
This commit is contained in:
parent
8a37302ced
commit
15ae0bb248
89
README.md
89
README.md
@ -42,8 +42,9 @@ Resource | Description
|
|||||||
---- | -----------
|
---- | -----------
|
||||||
[data/](data) | The file system image directory
|
[data/](data) | The file system image directory
|
||||||
[interface/](interface) | React based front end
|
[interface/](interface) | React based front end
|
||||||
[src/](src) | C++ back end for the ESP8266 device
|
[src/](src) | The main.cpp and demo project to get you started
|
||||||
[platformio.ini](platformio.ini) | PlatformIO project configuration file
|
[platformio.ini](platformio.ini) | PlatformIO project configuration file
|
||||||
|
[lib/framework/](lib/framework) | C++ back end for the ESP8266 device
|
||||||
|
|
||||||
### Building the firmware
|
### Building the firmware
|
||||||
|
|
||||||
@ -247,13 +248,90 @@ There is also a manifest file which contains the app name to use when adding the
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Back End Overview
|
## Back end overview
|
||||||
|
|
||||||
The back end is a set of REST endpoints hosted by a [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) instance. The source is split up by feature, for example [WiFiScanner.h](src/WiFiScanner.h) implements the end points for scanning for available networks.
|
The back end is a set of REST endpoints hosted by a [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer) instance. The ['lib/framework'](lib/framework) directory contains the majority of the back end code. The framework contains of a number of useful utility classes which you can use when extending it. The project also comes with a demo project to give you some help getting started.
|
||||||
|
|
||||||
There is an abstract class [SettingsService.h](src/SettingsService.h) that provides an easy means of adding configurable services/features to the device. It takes care of writing the settings as JSON to SPIFFS. All you need to do is extend the class with your required configuration and implement the functions which serialize the settings to/from JSON. JSON serialization utilizes the excellent [ArduinoJson](https://github.com/bblanchon/ArduinoJson) library.
|
The framework's source is split up by feature, for example [WiFiScanner.h](lib/framework/WiFiScanner.h) implements the end points for scanning for available networks where as [WiFiSettingsService.h](lib/framework/WiFiSettingsService.h) handles configuring the WiFi settings and managing the WiFi connection.
|
||||||
|
|
||||||
Here is a example of a service with username and password settings:
|
### Initializing the framework
|
||||||
|
|
||||||
|
The ['src/main.cpp'](src/main.cpp) file constructs the webserver and initializes the framework. You can add endpoints to the server here to support your IoT project. The main loop is also accessable so you can run your own code easily.
|
||||||
|
|
||||||
|
The following code creates the web server, esp8266React framework and the demo project instance:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
AsyncWebServer server(80);
|
||||||
|
ESP8266React framework(&SPIFFS);
|
||||||
|
DemoProject demoProject = DemoProject(&SPIFFS, framework.getSecurityManager());
|
||||||
|
```
|
||||||
|
|
||||||
|
Now in the `setup()` function the initialization is performed:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void setup() {
|
||||||
|
// start serial and filesystem
|
||||||
|
Serial.begin(SERIAL_BAUD_RATE);
|
||||||
|
SPIFFS.begin();
|
||||||
|
|
||||||
|
// set up the framework
|
||||||
|
framework.init(&server);
|
||||||
|
|
||||||
|
// begin the demo project
|
||||||
|
demoProject.init(&server);
|
||||||
|
|
||||||
|
// start the server
|
||||||
|
server.begin();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally the loop calls the framework's loop function to service the frameworks features. You can add your own code in here, as shown with the demo project:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void loop() {
|
||||||
|
// run the framework's loop function
|
||||||
|
framework.loop();
|
||||||
|
|
||||||
|
// run the demo project's loop function
|
||||||
|
demoProject.loop();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding endpoints
|
||||||
|
|
||||||
|
There are some simple classes that support adding configurable services/features to the device:
|
||||||
|
|
||||||
|
Class | Description
|
||||||
|
----- | -----------
|
||||||
|
[SimpleService.h](lib/framework/SimpleService.h) | Exposes an endpoint to read and write settings as JSON. Extend this class and implement the functions which serialize the settings to/from JSON.
|
||||||
|
[SettingsService.h](lib/framework/SettingsService.h) | As above, however this class also handles persisting the settings as JSON to the file system.
|
||||||
|
[AdminSettingsService.h](lib/framework/AdminSettingsService.h) | Extends SettingsService to secure the endpoint to administrators only, the authentication predicate can be overridden if required.
|
||||||
|
|
||||||
|
The demo project shows how these can be used, explore the framework classes for more examples.
|
||||||
|
|
||||||
|
### Security features
|
||||||
|
|
||||||
|
The framework has security features to prevent unauthorized use of the device. This is driven by [SecurityManager.h](lib/framework/SecurityManager.h).
|
||||||
|
|
||||||
|
On successful authentication, the /rest/signIn endpoint issues a JWT which is then sent using Bearer Authentication. The framework come with built in predicates for verifying a users access level. The built in AuthenticationPredicates can be found in [SecurityManager.h](lib/framework/SecurityManager.h):
|
||||||
|
|
||||||
|
Predicate | Description
|
||||||
|
-------------------- | -----------
|
||||||
|
NONE_REQUIRED | No authentication is required.
|
||||||
|
IS_AUTHENTICATED | Any authentication is permitted.
|
||||||
|
IS_AUTHENTICATED | Any authentication is permitted.
|
||||||
|
|
||||||
|
You can use the security manager to wrap any web handler with an authentication predicate:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
server->on("/rest/someService", HTTP_GET,
|
||||||
|
_securityManager->wrapRequest(std::bind(&SomeService::someService, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively you can extend [AdminSettingsService.h](lib/framework/AdminSettingsService.h) and optionally override `getAuthenticationPredicate()` to secure an endpoint.
|
||||||
|
|
||||||
|
## Extending the framework
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include <SettingsService.h>
|
#include <SettingsService.h>
|
||||||
@ -321,6 +399,7 @@ void reconfigureTheService() {
|
|||||||
|
|
||||||
* [React](https://reactjs.org/)
|
* [React](https://reactjs.org/)
|
||||||
* [Material-UI](https://material-ui-next.com/)
|
* [Material-UI](https://material-ui-next.com/)
|
||||||
|
* [notistack](https://github.com/iamhosseindhv/notistack)
|
||||||
* [Time](https://github.com/PaulStoffregen/Time)
|
* [Time](https://github.com/PaulStoffregen/Time)
|
||||||
* [NtpClient](https://github.com/gmag11/NtpClient)
|
* [NtpClient](https://github.com/gmag11/NtpClient)
|
||||||
* [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
|
* [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef APSettingsConfig_h
|
#ifndef APSettingsConfig_h
|
||||||
#define APSettingsConfig_h
|
#define APSettingsConfig_h
|
||||||
|
|
||||||
#include <SettingsService.h>
|
#include <AdminSettingsService.h>
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#include <IPAddress.h>
|
#include <IPAddress.h>
|
||||||
|
|
||||||
|
46
lib/framework/AdminSettingsService.h
Normal file
46
lib/framework/AdminSettingsService.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#ifndef AdminSettingsService_h
|
||||||
|
#define AdminSettingsService_h
|
||||||
|
|
||||||
|
#include <SettingsService.h>
|
||||||
|
|
||||||
|
class AdminSettingsService : public SettingsService {
|
||||||
|
|
||||||
|
public:
|
||||||
|
AdminSettingsService(FS* fs, SecurityManager* securityManager, char const* servicePath, char const* filePath):
|
||||||
|
SettingsService(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 this to replace the default authentication predicate, IS_ADMIN
|
||||||
|
AuthenticationPredicate getAuthenticationPredicate() {
|
||||||
|
return AuthenticationPredicates::IS_ADMIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // end AdminSettingsService
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef NTPSettingsService_h
|
#ifndef NTPSettingsService_h
|
||||||
#define NTPSettingsService_h
|
#define NTPSettingsService_h
|
||||||
|
|
||||||
#include <SettingsService.h>
|
#include <AdminSettingsService.h>
|
||||||
|
|
||||||
#include <TimeLib.h>
|
#include <TimeLib.h>
|
||||||
#include <NtpClientLib.h>
|
#include <NtpClientLib.h>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef OTASettingsService_h
|
#ifndef OTASettingsService_h
|
||||||
#define OTASettingsService_h
|
#define OTASettingsService_h
|
||||||
|
|
||||||
#include <SettingsService.h>
|
#include <AdminSettingsService.h>
|
||||||
|
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
#include <ESP8266mDNS.h>
|
#include <ESP8266mDNS.h>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef SecuritySettingsService_h
|
#ifndef SecuritySettingsService_h
|
||||||
#define SecuritySettingsService_h
|
#define SecuritySettingsService_h
|
||||||
|
|
||||||
#include <SettingsService.h>
|
#include <AdminSettingsService.h>
|
||||||
#include <SecurityManager.h>
|
#include <SecurityManager.h>
|
||||||
|
|
||||||
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
|
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"
|
||||||
|
@ -78,43 +78,4 @@ protected:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AdminSettingsService : public SettingsService {
|
|
||||||
public:
|
|
||||||
AdminSettingsService(FS* fs, SecurityManager* securityManager, char const* servicePath, char const* filePath):
|
|
||||||
SettingsService(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
|
#endif // end SettingsService
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef WiFiSettingsService_h
|
#ifndef WiFiSettingsService_h
|
||||||
#define WiFiSettingsService_h
|
#define WiFiSettingsService_h
|
||||||
|
|
||||||
#include <SettingsService.h>
|
#include <AdminSettingsService.h>
|
||||||
#include <IPAddress.h>
|
#include <IPAddress.h>
|
||||||
|
|
||||||
#define WIFI_SETTINGS_FILE "/config/wifiSettings.json"
|
#define WIFI_SETTINGS_FILE "/config/wifiSettings.json"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef DemoProject_h
|
#ifndef DemoProject_h
|
||||||
#define DemoProject_h
|
#define DemoProject_h
|
||||||
|
|
||||||
#include <SettingsService.h>
|
#include <AdminSettingsService.h>
|
||||||
|
|
||||||
#define BLINK_LED 2
|
#define BLINK_LED 2
|
||||||
#define MAX_DELAY 1000
|
#define MAX_DELAY 1000
|
||||||
|
@ -5,9 +5,8 @@
|
|||||||
#define SERIAL_BAUD_RATE 115200
|
#define SERIAL_BAUD_RATE 115200
|
||||||
|
|
||||||
AsyncWebServer server(80);
|
AsyncWebServer server(80);
|
||||||
ESP8266React espServer(&SPIFFS);
|
ESP8266React framework(&SPIFFS);
|
||||||
|
DemoProject demoProject = DemoProject(&SPIFFS, framework.getSecurityManager());
|
||||||
DemoProject demoProject = DemoProject(&SPIFFS, espServer.getSecurityManager());
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// start serial and filesystem
|
// start serial and filesystem
|
||||||
@ -15,7 +14,7 @@ void setup() {
|
|||||||
SPIFFS.begin();
|
SPIFFS.begin();
|
||||||
|
|
||||||
// set up the framework
|
// set up the framework
|
||||||
espServer.init(&server);
|
framework.init(&server);
|
||||||
|
|
||||||
// begin the demo project
|
// begin the demo project
|
||||||
demoProject.init(&server);
|
demoProject.init(&server);
|
||||||
@ -26,7 +25,7 @@ void setup() {
|
|||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
// run the framework's loop function
|
// run the framework's loop function
|
||||||
espServer.loop();
|
framework.loop();
|
||||||
|
|
||||||
// run the demo project's loop function
|
// run the demo project's loop function
|
||||||
demoProject.loop();
|
demoProject.loop();
|
||||||
|
Loading…
Reference in New Issue
Block a user