Skip to content

Commit 15ae0bb

Browse files
committed
WIP - some documentation
1 parent 8a37302 commit 15ae0bb

10 files changed

+140
-55
lines changed

README.md

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ Resource | Description
4242
---- | -----------
4343
[data/](data) | The file system image directory
4444
[interface/](interface) | React based front end
45-
[src/](src) | C++ back end for the ESP8266 device
45+
[src/](src) | The main.cpp and demo project to get you started
4646
[platformio.ini](platformio.ini) | PlatformIO project configuration file
47+
[lib/framework/](lib/framework) | C++ back end for the ESP8266 device
4748

4849
### Building the firmware
4950

@@ -247,13 +248,90 @@ There is also a manifest file which contains the app name to use when adding the
247248
}
248249
```
249250

250-
## Back End Overview
251+
## Back end overview
251252

252-
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.
253+
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.
253254

254-
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.
255+
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.
255256

256-
Here is a example of a service with username and password settings:
257+
### Initializing the framework
258+
259+
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.
260+
261+
The following code creates the web server, esp8266React framework and the demo project instance:
262+
263+
```cpp
264+
AsyncWebServer server(80);
265+
ESP8266React framework(&SPIFFS);
266+
DemoProject demoProject = DemoProject(&SPIFFS, framework.getSecurityManager());
267+
```
268+
269+
Now in the `setup()` function the initialization is performed:
270+
271+
```cpp
272+
void setup() {
273+
// start serial and filesystem
274+
Serial.begin(SERIAL_BAUD_RATE);
275+
SPIFFS.begin();
276+
277+
// set up the framework
278+
framework.init(&server);
279+
280+
// begin the demo project
281+
demoProject.init(&server);
282+
283+
// start the server
284+
server.begin();
285+
}
286+
```
287+
288+
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:
289+
290+
```cpp
291+
void loop() {
292+
// run the framework's loop function
293+
framework.loop();
294+
295+
// run the demo project's loop function
296+
demoProject.loop();
297+
}
298+
```
299+
300+
### Adding endpoints
301+
302+
There are some simple classes that support adding configurable services/features to the device:
303+
304+
Class | Description
305+
----- | -----------
306+
[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.
307+
[SettingsService.h](lib/framework/SettingsService.h) | As above, however this class also handles persisting the settings as JSON to the file system.
308+
[AdminSettingsService.h](lib/framework/AdminSettingsService.h) | Extends SettingsService to secure the endpoint to administrators only, the authentication predicate can be overridden if required.
309+
310+
The demo project shows how these can be used, explore the framework classes for more examples.
311+
312+
### Security features
313+
314+
The framework has security features to prevent unauthorized use of the device. This is driven by [SecurityManager.h](lib/framework/SecurityManager.h).
315+
316+
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):
317+
318+
Predicate | Description
319+
-------------------- | -----------
320+
NONE_REQUIRED | No authentication is required.
321+
IS_AUTHENTICATED | Any authentication is permitted.
322+
IS_AUTHENTICATED | Any authentication is permitted.
323+
324+
You can use the security manager to wrap any web handler with an authentication predicate:
325+
326+
```cpp
327+
server->on("/rest/someService", HTTP_GET,
328+
_securityManager->wrapRequest(std::bind(&SomeService::someService, this, std::placeholders::_1), AuthenticationPredicates::IS_AUTHENTICATED)
329+
);
330+
```
331+
332+
Alternatively you can extend [AdminSettingsService.h](lib/framework/AdminSettingsService.h) and optionally override `getAuthenticationPredicate()` to secure an endpoint.
333+
334+
## Extending the framework
257335

258336
```cpp
259337
#include <SettingsService.h>
@@ -321,6 +399,7 @@ void reconfigureTheService() {
321399

322400
* [React](https://reactjs.org/)
323401
* [Material-UI](https://material-ui-next.com/)
402+
* [notistack](https://github.com/iamhosseindhv/notistack)
324403
* [Time](https://github.com/PaulStoffregen/Time)
325404
* [NtpClient](https://github.com/gmag11/NtpClient)
326405
* [ArduinoJson](https://github.com/bblanchon/ArduinoJson)

lib/framework/APSettingsService.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef APSettingsConfig_h
22
#define APSettingsConfig_h
33

4-
#include <SettingsService.h>
4+
#include <AdminSettingsService.h>
55
#include <DNSServer.h>
66
#include <IPAddress.h>
77

lib/framework/AdminSettingsService.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef AdminSettingsService_h
2+
#define AdminSettingsService_h
3+
4+
#include <SettingsService.h>
5+
6+
class AdminSettingsService : public SettingsService {
7+
8+
public:
9+
AdminSettingsService(FS* fs, SecurityManager* securityManager, char const* servicePath, char const* filePath):
10+
SettingsService(fs, servicePath, filePath), _securityManager(securityManager) {
11+
}
12+
13+
protected:
14+
// will validate the requests with the security manager
15+
SecurityManager* _securityManager;
16+
17+
void fetchConfig(AsyncWebServerRequest *request) {
18+
// verify the request against the predicate
19+
Authentication authentication = _securityManager->authenticateRequest(request);
20+
if (!getAuthenticationPredicate()(authentication)) {
21+
request->send(401);
22+
return;
23+
}
24+
// delegate to underlying implemetation
25+
SettingsService::fetchConfig(request);
26+
}
27+
28+
void updateConfig(AsyncWebServerRequest *request, JsonDocument &jsonDocument) {
29+
// verify the request against the predicate
30+
Authentication authentication = _securityManager->authenticateRequest(request);
31+
if (!getAuthenticationPredicate()(authentication)) {
32+
request->send(401);
33+
return;
34+
}
35+
// delegate to underlying implemetation
36+
SettingsService::updateConfig(request, jsonDocument);
37+
}
38+
39+
// override this to replace the default authentication predicate, IS_ADMIN
40+
AuthenticationPredicate getAuthenticationPredicate() {
41+
return AuthenticationPredicates::IS_ADMIN;
42+
}
43+
44+
};
45+
46+
#endif // end AdminSettingsService

lib/framework/NTPSettingsService.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef NTPSettingsService_h
22
#define NTPSettingsService_h
33

4-
#include <SettingsService.h>
4+
#include <AdminSettingsService.h>
55

66
#include <TimeLib.h>
77
#include <NtpClientLib.h>

lib/framework/OTASettingsService.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef OTASettingsService_h
22
#define OTASettingsService_h
33

4-
#include <SettingsService.h>
4+
#include <AdminSettingsService.h>
55

66
#if defined(ESP8266)
77
#include <ESP8266mDNS.h>

lib/framework/SecuritySettingsService.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef SecuritySettingsService_h
22
#define SecuritySettingsService_h
33

4-
#include <SettingsService.h>
4+
#include <AdminSettingsService.h>
55
#include <SecurityManager.h>
66

77
#define SECURITY_SETTINGS_FILE "/config/securitySettings.json"

lib/framework/SettingsService.h

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -78,43 +78,4 @@ class SettingsService : public SettingsPersistence {
7878

7979
};
8080

81-
class AdminSettingsService : public SettingsService {
82-
public:
83-
AdminSettingsService(FS* fs, SecurityManager* securityManager, char const* servicePath, char const* filePath):
84-
SettingsService(fs, servicePath, filePath), _securityManager(securityManager) {
85-
}
86-
87-
protected:
88-
// will validate the requests with the security manager
89-
SecurityManager* _securityManager;
90-
91-
void fetchConfig(AsyncWebServerRequest *request) {
92-
// verify the request against the predicate
93-
Authentication authentication = _securityManager->authenticateRequest(request);
94-
if (!getAuthenticationPredicate()(authentication)) {
95-
request->send(401);
96-
return;
97-
}
98-
// delegate to underlying implemetation
99-
SettingsService::fetchConfig(request);
100-
}
101-
102-
void updateConfig(AsyncWebServerRequest *request, JsonDocument &jsonDocument) {
103-
// verify the request against the predicate
104-
Authentication authentication = _securityManager->authenticateRequest(request);
105-
if (!getAuthenticationPredicate()(authentication)) {
106-
request->send(401);
107-
return;
108-
}
109-
// delegate to underlying implemetation
110-
SettingsService::updateConfig(request, jsonDocument);
111-
}
112-
113-
// override to override the default authentication predicate, IS_ADMIN
114-
AuthenticationPredicate getAuthenticationPredicate() {
115-
return AuthenticationPredicates::IS_ADMIN;
116-
}
117-
118-
};
119-
12081
#endif // end SettingsService

lib/framework/WiFiSettingsService.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef WiFiSettingsService_h
22
#define WiFiSettingsService_h
33

4-
#include <SettingsService.h>
4+
#include <AdminSettingsService.h>
55
#include <IPAddress.h>
66

77
#define WIFI_SETTINGS_FILE "/config/wifiSettings.json"

src/DemoProject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef DemoProject_h
22
#define DemoProject_h
33

4-
#include <SettingsService.h>
4+
#include <AdminSettingsService.h>
55

66
#define BLINK_LED 2
77
#define MAX_DELAY 1000

src/main.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,16 @@
55
#define SERIAL_BAUD_RATE 115200
66

77
AsyncWebServer server(80);
8-
ESP8266React espServer(&SPIFFS);
9-
10-
DemoProject demoProject = DemoProject(&SPIFFS, espServer.getSecurityManager());
8+
ESP8266React framework(&SPIFFS);
9+
DemoProject demoProject = DemoProject(&SPIFFS, framework.getSecurityManager());
1110

1211
void setup() {
1312
// start serial and filesystem
1413
Serial.begin(SERIAL_BAUD_RATE);
1514
SPIFFS.begin();
1615

1716
// set up the framework
18-
espServer.init(&server);
17+
framework.init(&server);
1918

2019
// begin the demo project
2120
demoProject.init(&server);
@@ -26,7 +25,7 @@ void setup() {
2625

2726
void loop() {
2827
// run the framework's loop function
29-
espServer.loop();
28+
framework.loop();
3029

3130
// run the demo project's loop function
3231
demoProject.loop();

0 commit comments

Comments
 (0)