From 778bbe3b813e7da81d412d2d89fd1e4fe61335f0 Mon Sep 17 00:00:00 2001 From: Proddy Date: Wed, 27 Dec 2023 22:41:19 +0100 Subject: [PATCH] some standalone compile updates for testing --- interface/package.json | 2 +- interface/yarn.lock | 10 +- lib_standalone/ESP8266React.h | 7 +- lib_standalone/PsychicHttp.h | 505 +++++++++++++++++++++ lib_standalone/SecurityManager.h | 38 +- lib_standalone/SecuritySettingsService.cpp | 72 +-- lib_standalone/SecuritySettingsService.h | 44 +- platformio.ini | 7 +- src/emsesp.cpp | 6 +- src/test/test.cpp | 12 +- 10 files changed, 600 insertions(+), 103 deletions(-) create mode 100644 lib_standalone/PsychicHttp.h diff --git a/interface/package.json b/interface/package.json index c9f3a0562..5489c4e60 100644 --- a/interface/package.json +++ b/interface/package.json @@ -68,7 +68,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "preact": "^10.19.3", "prettier": "^3.1.1", - "rollup-plugin-visualizer": "^5.11.0", + "rollup-plugin-visualizer": "^5.12.0", "terser": "^5.26.0", "vite": "^5.0.10", "vite-plugin-imagemin": "^0.6.1", diff --git a/interface/yarn.lock b/interface/yarn.lock index 4fd6b45a2..2164fce85 100644 --- a/interface/yarn.lock +++ b/interface/yarn.lock @@ -1866,7 +1866,7 @@ __metadata: react-icons: "npm:^4.12.0" react-router-dom: "npm:^6.21.1" react-toastify: "npm:^9.1.3" - rollup-plugin-visualizer: "npm:^5.11.0" + rollup-plugin-visualizer: "npm:^5.12.0" sockette: "npm:^2.0.6" terser: "npm:^5.26.0" typesafe-i18n: "npm:^5.26.2" @@ -7450,9 +7450,9 @@ __metadata: languageName: node linkType: hard -"rollup-plugin-visualizer@npm:^5.11.0": - version: 5.11.0 - resolution: "rollup-plugin-visualizer@npm:5.11.0" +"rollup-plugin-visualizer@npm:^5.12.0": + version: 5.12.0 + resolution: "rollup-plugin-visualizer@npm:5.12.0" dependencies: open: "npm:^8.4.0" picomatch: "npm:^2.3.1" @@ -7465,7 +7465,7 @@ __metadata: optional: true bin: rollup-plugin-visualizer: dist/bin/cli.js - checksum: 947238aa22706a47a4d3e8ce616855f0e5cb969ed9f61b9a268eaede0a86f461ecb38e27b4e6bf00f4b5e3f63677667f65e0d4af89a659a5160f74add1f192bb + checksum: 47358feb672291d6edcfd94197577c192a84c24cb644119425dae8241fb6f5a52556efd0c501f38b276c07534642a80c0885ef681babb474e83c7b5a3b475b84 languageName: node linkType: hard diff --git a/lib_standalone/ESP8266React.h b/lib_standalone/ESP8266React.h index cb5b4e58e..0339b1e97 100644 --- a/lib_standalone/ESP8266React.h +++ b/lib_standalone/ESP8266React.h @@ -82,7 +82,7 @@ class DummySettings { class DummySettingsService : public StatefulService { public: - DummySettingsService(AsyncWebServer * server, FS * fs, SecurityManager * securityManager){}; + DummySettingsService(PsychicHttpServer * server, FS * fs, SecurityManager * securityManager){}; void begin(); void loop(); @@ -95,16 +95,17 @@ class DummySettingsService : public StatefulService { class ESP8266React { public: - ESP8266React(AsyncWebServer * server, FS * fs) + ESP8266React(PsychicHttpServer * server, FS * fs) : _settings(server, fs, nullptr) , _securitySettingsService(server, fs){}; void begin() { - // initialize mqtt _mqttClient = new espMqttClient(); }; void loop(){}; + void registerURI(){}; + SecurityManager * getSecurityManager() { return &_securitySettingsService; } diff --git a/lib_standalone/PsychicHttp.h b/lib_standalone/PsychicHttp.h new file mode 100644 index 000000000..270d96601 --- /dev/null +++ b/lib_standalone/PsychicHttp.h @@ -0,0 +1,505 @@ +/* + * PsychicHttp.h + * Standalone hacky version + */ + +#ifndef PsychicHttp_h +#define PsychicHttp_h + +#include "Arduino.h" +#include +#include +#include +#include + +#define esp_err_t uint8_t + +class PsychicHttpServer; +class PsychicHttpsServer; +class PsychicEndpoint; +class PsychicHandler; +class PsychicStaticFileHandler; +class PsychicClient; +class PsychicRequest; +class PsychicResponse; +class PsychicEventSource; +class PsychicEventSourceResponse; +class PsychicEventSourceClient; +class PsychicResponse; + +typedef std::function PsychicEventSourceClientCallback; +typedef std::function PsychicJsonRequestCallback; +typedef std::function PsychicRequestFilterFunction; +typedef std::function PsychicClientCallback; +typedef std::function PsychicHttpRequestCallback; +typedef std::function PsychicJsonRequestCallback; // added by proddy + +/* Request Methods */ +#define HTTP_METHOD_MAP(XX) \ + XX(1, GET, GET) \ + XX(3, POST, POST) + +enum http_method { +#define XX(num, name, string) HTTP_##name = num, + HTTP_METHOD_MAP(XX) +#undef XX +}; + +constexpr const char * JSON_MIMETYPE = "application/json"; + +enum HTTPAuthMethod { BASIC_AUTH, DIGEST_AUTH }; + +typedef void * httpd_handle_t; + +typedef struct httpd_req { + httpd_handle_t handle; /*!< Handle to server instance */ + int method; /*!< The type of HTTP request, -1 if unsupported method */ + const char uri[513]; /*!< The URI of this request (1 byte extra for null termination) */ + size_t content_len; /*!< Length of the request body */ + void * aux; /*!< Internally used members */ + +} httpd_req_t; + +typedef std::map SessionData; +enum Disposition { NONE, INLINE, ATTACHMENT, FORM_DATA }; + +struct ContentDisposition { + Disposition disposition; + String filename; + String name; +}; + +class PsychicWebParameter { + private: + String _name; + String _value; + size_t _size; + bool _isForm; + bool _isFile; + + public: + PsychicWebParameter(const String & name, const String & value, bool form = false, bool file = false, size_t size = 0) + : _name(name) + , _value(value) + , _size(size) + , _isForm(form) + , _isFile(file) { + } + const String & name() const { + return _name; + } + const String & value() const { + return _value; + } + size_t size() const { + return _size; + } + bool isPost() const { + return _isForm; + } + bool isFile() const { + return _isFile; + } +}; + +class PsychicRequest { + protected: + PsychicHttpServer * _server; + httpd_req_t * _req; + SessionData * _session; + PsychicClient * _client; + + http_method _method; + String _uri; + String _query; + String _body; + + std::list _params; + + void _addParams(const String & params); + void _parseGETParams(); + void _parsePOSTParams(); + + const String _extractParam(const String & authReq, const String & param, const char delimit); + const String _getRandomHexString(); + + public: + PsychicRequest(PsychicHttpServer * server, httpd_req_t * req); + virtual ~PsychicRequest(); + + void * _tempObject; + + PsychicHttpServer * server(); + httpd_req_t * request(); + virtual PsychicClient * client(); + + bool isMultipart(); + esp_err_t loadBody(); + + const String header(const char * name); + bool hasHeader(const char * name); + + static void freeSession(void * ctx); + bool hasSessionKey(const String & key); + const String getSessionKey(const String & key); + void setSessionKey(const String & key, const String & value); + + bool hasCookie(const char * key); + const String getCookie(const char * key); + + http_method method() { + return HTTP_GET; + } // returns the HTTP method used as enum value (eg. HTTP_GET) + const String methodStr(); // returns the HTTP method used as a string (eg. "GET") + const String path(); // returns the request path (eg /page?foo=bar returns "/page") + const String & uri(); // returns the full request uri (eg /page?foo=bar) + const String & query(); // returns the request query data (eg /page?foo=bar returns "foo=bar") + const String host(); // returns the requested host (request to http://psychic.local/foo will return "psychic.local") + const String contentType(); // returns the Content-Type header value + size_t contentLength(); // returns the Content-Length header value + const String & body(); // returns the body of the request + const ContentDisposition getContentDisposition(); + + const String & queryString() { + return query(); + } //compatability function. same as query() + const String & url() { + return uri(); + } //compatability function. same as uri() + + void loadParams(); + PsychicWebParameter * addParam(PsychicWebParameter * param); + PsychicWebParameter * addParam(const String & name, const String & value, bool decode = true); + bool hasParam(const char * key); + PsychicWebParameter * getParam(const char * name); + + const String getFilename(); + + bool authenticate(const char * username, const char * password); + esp_err_t requestAuthentication(HTTPAuthMethod mode, const char * realm, const char * authFailMsg); + + esp_err_t redirect(const char * url); + esp_err_t reply(int code); + esp_err_t reply(const char * content); + esp_err_t reply(int code, const char * contentType, const char * content); +}; + +class PsychicHandler { + protected: + PsychicRequestFilterFunction _filter; + PsychicHttpServer * _server; + + String _username; + String _password; + HTTPAuthMethod _method; + String _realm; + String _authFailMsg; + + std::list _clients; + + public: + PsychicHandler(); + ~PsychicHandler(); + + PsychicHandler * setFilter(PsychicRequestFilterFunction fn); + bool filter(PsychicRequest * request); + + PsychicHandler * + setAuthentication(const char * username, const char * password, HTTPAuthMethod method = BASIC_AUTH, const char * realm = "", const char * authFailMsg = ""); + bool needsAuthentication(PsychicRequest * request); + esp_err_t authenticate(PsychicRequest * request); + + virtual bool isWebSocket() { + return false; + }; + + PsychicClient * checkForNewClient(PsychicClient * client); + void checkForClosedClient(PsychicClient * client); + + virtual void addClient(PsychicClient * client); + virtual void removeClient(PsychicClient * client); + virtual PsychicClient * getClient(int socket); + virtual PsychicClient * getClient(PsychicClient * client); + virtual void openCallback(PsychicClient * client){}; + virtual void closeCallback(PsychicClient * client){}; + + bool hasClient(PsychicClient * client); + int count() { + return _clients.size(); + }; + const std::list & getClientList(); + + virtual bool canHandle(PsychicRequest * request) { + return true; + }; + virtual esp_err_t handleRequest(PsychicRequest * request) = 0; +}; + +class PsychicEndpoint { + friend PsychicHttpServer; + + private: + PsychicHttpServer * _server; + String _uri; + http_method _method; + PsychicHandler * _handler; + + public: + PsychicEndpoint(); + PsychicEndpoint(PsychicHttpServer * server, http_method method, const char * uri); + + PsychicEndpoint * setHandler(PsychicHandler * handler); + PsychicHandler * handler(); + + PsychicEndpoint * setFilter(PsychicRequestFilterFunction fn); + PsychicEndpoint * + setAuthentication(const char * username, const char * password, HTTPAuthMethod method = BASIC_AUTH, const char * realm = "", const char * authFailMsg = ""); + + String uri(); + + static esp_err_t requestCallback(httpd_req_t * req); +}; + +typedef struct httpd_config { + uint16_t max_uri_handlers; /*!< Maximum allowed uri handlers */ +} httpd_config_t; + +httpd_config_t config; + +class PsychicHttpServer { + public: + PsychicHttpServer(); + ~PsychicHttpServer(); + + httpd_handle_t server; + httpd_config_t config; + + unsigned long maxUploadSize; + unsigned long maxRequestBodySize; + + PsychicEndpoint * defaultEndpoint; + PsychicHandler & addHandler(PsychicHandler * handler); + + esp_err_t listen(uint16_t port); + + PsychicEndpoint * on(const char * uri); + PsychicEndpoint * on(const char * uri, http_method method); + PsychicEndpoint * on(const char * uri, PsychicHttpRequestCallback onRequest); + PsychicEndpoint * on(const char * uri, http_method method, PsychicHttpRequestCallback onRequest); + PsychicEndpoint * on(const char * uri, PsychicHandler * handler); + PsychicEndpoint * on(const char * uri, http_method method, PsychicHandler * handler); + PsychicEndpoint * on(const char * uri, http_method method, PsychicJsonRequestCallback onRequest); // added proddy + + void onNotFound(PsychicHttpRequestCallback fn); + + void onOpen(PsychicClientCallback handler); + void onClose(PsychicClientCallback handler); + static esp_err_t openCallback(httpd_handle_t hd, int sockfd); + static void closeCallback(httpd_handle_t hd, int sockfd); +}; + +class PsychicHttpsServer : public PsychicHttpServer { + public: + PsychicHttpsServer(); + ~PsychicHttpsServer(); + + uint8_t listen(uint16_t port, const char * cert, const char * private_key); +}; + +struct HTTPHeader { + char * field; + char * value; +}; + +class DefaultHeaders { + std::list _headers; + + public: + DefaultHeaders() { + } + + void addHeader(const String & field, const String & value) { + addHeader(field.c_str(), value.c_str()); + } + + void addHeader(const char * field, const char * value) { + HTTPHeader header; + _headers.push_back(header); + } + + const std::list & getHeaders() { + return _headers; + } + + DefaultHeaders(DefaultHeaders const &) = delete; + DefaultHeaders & operator=(DefaultHeaders const &) = delete; + + static DefaultHeaders & Instance() { + static DefaultHeaders instance; + return instance; + } +}; + +class PsychicWebHandler : public PsychicHandler { + protected: + PsychicHttpRequestCallback _requestCallback; + + public: + PsychicWebHandler(); + ~PsychicWebHandler(); + + virtual bool canHandle(PsychicRequest * request) override; + virtual esp_err_t handleRequest(PsychicRequest * request) override; + PsychicWebHandler * onRequest(PsychicHttpRequestCallback fn); +}; + +class PsychicResponse { + protected: + PsychicRequest * _request; + + int _code; + char _status[60]; + std::list _headers; + int64_t _contentLength; + const char * _body; + + public: + PsychicResponse(PsychicRequest * request); + virtual ~PsychicResponse(); + + void setCode(int code); + + void setContentType(const char * contentType); + void setContentLength(int64_t contentLength) { + _contentLength = contentLength; + } + int64_t getContentLength(int64_t contentLength) { + return _contentLength; + } + + void addHeader(const char * field, const char * value); + void setCookie(const char * key, const char * value, unsigned long max_age = 60 * 60 * 24 * 30, const char * extras = ""); + void setContent(const char * content); + void setContent(const uint8_t * content, size_t len); + const char * getContent(); + size_t getContentLength(); + esp_err_t send(); + void sendHeaders(); + esp_err_t sendChunk(uint8_t * chunk, size_t chunksize); + esp_err_t finishChunking(); +}; + +class PsychicJsonResponse : public PsychicResponse { + protected: +#ifdef ARDUINOJSON_5_COMPATIBILITY + DynamicJsonBuffer _jsonBuffer; +#else + DynamicJsonDocument _jsonBuffer; +#endif + + JsonVariant _root; + size_t _contentLength; + bool _msgPack; // added by proddy + + public: + PsychicJsonResponse(PsychicRequest * request, bool isArray = false, size_t maxJsonBufferSize = 4096, bool msgPack = false) // added by proddy + : PsychicResponse(request) + , _jsonBuffer(maxJsonBufferSize) + , _msgPack(msgPack) // added by proddy + { + } + + ~PsychicJsonResponse() { + } + + JsonVariant & getRoot() { + return _root; + } + + size_t getLength() { + // return measureJson(_root); + return (_msgPack) ? measureMsgPack(_root) : measureJson(_root); // added by proddy + } + + esp_err_t send() { + return 0; + } +}; + +class PsychicClient { + protected: + httpd_handle_t _server; + int _socket; + + public: + PsychicClient(httpd_handle_t server, int socket); + ~PsychicClient(); + + //no idea if this is the right way to do it or not, but lets see. + //pointer to our derived class (eg. PsychicWebSocketConnection) + void * _friend; + + bool isNew = false; + + bool operator==(PsychicClient & rhs) const { + return _socket == rhs.socket(); + } + + httpd_handle_t server(); + int socket(); + esp_err_t close(); + + IPAddress localIP(); + IPAddress remoteIP(); +}; + +class PsychicEventSourceClient : public PsychicClient { + protected: + uint32_t _lastId; + + public: + PsychicEventSourceClient(PsychicClient * client); + ~PsychicEventSourceClient(); + + uint32_t lastId() const { + return _lastId; + } + void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0); + void sendEvent(const char * event); +}; + +class PsychicEventSource : public PsychicHandler { + private: + PsychicEventSourceClientCallback _onOpen; + PsychicEventSourceClientCallback _onClose; + + public: + PsychicEventSource(); + ~PsychicEventSource(); + + PsychicEventSourceClient * getClient(int socket) override; + PsychicEventSourceClient * getClient(PsychicClient * client) override; + void addClient(PsychicClient * client) override; + void removeClient(PsychicClient * client) override; + void openCallback(PsychicClient * client) override; + void closeCallback(PsychicClient * client) override; + + PsychicEventSource * onOpen(PsychicEventSourceClientCallback fn); + PsychicEventSource * onClose(PsychicEventSourceClientCallback fn); + + esp_err_t handleRequest(PsychicRequest * request) override final; + + void send(const char * message, const char * event = NULL, uint32_t id = 0, uint32_t reconnect = 0); +}; + +class PsychicEventSourceResponse : public PsychicResponse { + public: + PsychicEventSourceResponse(PsychicRequest * request); + esp_err_t send(); +}; + +String generateEventMessage(const char * message, const char * event, uint32_t id, uint32_t reconnect); + + + +#endif \ No newline at end of file diff --git a/lib_standalone/SecurityManager.h b/lib_standalone/SecurityManager.h index 5532a07ba..81e8fd0cd 100644 --- a/lib_standalone/SecurityManager.h +++ b/lib_standalone/SecurityManager.h @@ -2,15 +2,12 @@ #define SecurityManager_h #include +#include #include #include -#ifndef FACTORY_JWT_SECRET -#define FACTORY_JWT_SECRET ESPUtils::defaultDeviceValue() -#endif - +#define FACTORY_JWT_SECRET "secret" #define ACCESS_TOKEN_PARAMATER "access_token" - #define AUTHORIZATION_HEADER "Authorization" #define AUTHORIZATION_HEADER_PREFIX "Bearer " #define AUTHORIZATION_HEADER_PREFIX_LEN 7 @@ -67,10 +64,35 @@ 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; - virtual String generateJWT(User * user) = 0; -#endif + + /* + * Generate a JWT for the user provided + */ + virtual String generateJWT(User * user) = 0; + + /* + * Check the request header for the Authorization token + */ + virtual Authentication authenticateRequest(PsychicRequest * request) = 0; + + /** + * Filter a request with the provided predicate, only returning true if the predicate matches. + */ + virtual PsychicRequestFilterFunction filterRequest(AuthenticationPredicate predicate) = 0; + + /** + * Wrap the provided request to provide validation against an AuthenticationPredicate. + */ + virtual PsychicHttpRequestCallback wrapRequest(PsychicHttpRequestCallback onRequest, AuthenticationPredicate predicate) = 0; + + /** + * Wrap the provided json request callback to provide validation against an AuthenticationPredicate. + */ + virtual PsychicJsonRequestCallback wrapCallback(PsychicJsonRequestCallback onRequest, AuthenticationPredicate predicate) = 0; }; #endif diff --git a/lib_standalone/SecuritySettingsService.cpp b/lib_standalone/SecuritySettingsService.cpp index bdbef134c..8a46af22f 100644 --- a/lib_standalone/SecuritySettingsService.cpp +++ b/lib_standalone/SecuritySettingsService.cpp @@ -4,12 +4,13 @@ #if FT_ENABLED(FT_SECURITY) -#include "../../src/emsesp_stub.h +#include "../../src/emsesp_stub.h" // proddy added 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) - , _jwtHandler(FACTORY_JWT_SECRET) { +// , _jwtHandler(FACTORY_JWT_SECRET) { +{ addUpdateHandler([&](const String & originId) { configureJWTHandler(); }, false); } @@ -18,29 +19,32 @@ void SecuritySettingsService::begin() { configureJWTHandler(); } -Authentication SecuritySettingsService::authenticateRequest(AsyncWebServerRequest * request) { - AsyncWebHeader * authorizationHeader = request->getHeader(AUTHORIZATION_HEADER); - if (authorizationHeader) { - String value = authorizationHeader->value(); +void SecuritySettingsService::registerURI() { + _httpEndpoint.registerURI(); + _server->on(GENERATE_TOKEN_PATH, HTTP_GET, wrapRequest(std::bind(&SecuritySettingsService::generateToken, this, _1), AuthenticationPredicates::IS_ADMIN)); +} + +Authentication SecuritySettingsService::authenticateRequest(PsychicRequest * request) { + if (request->hasHeader(AUTHORIZATION_HEADER)) { + auto value = request->header(AUTHORIZATION_HEADER); if (value.startsWith(AUTHORIZATION_HEADER_PREFIX)) { value = value.substring(AUTHORIZATION_HEADER_PREFIX_LEN); return authenticateJWT(value); } } else if (request->hasParam(ACCESS_TOKEN_PARAMATER)) { - AsyncWebParameter * tokenParamater = request->getParam(ACCESS_TOKEN_PARAMATER); - String value = tokenParamater->value(); + String value = request->getParam(ACCESS_TOKEN_PARAMATER)->value(); return authenticateJWT(value); } return Authentication(); } void SecuritySettingsService::configureJWTHandler() { - _jwtHandler.setSecret(_state.jwtSecret); + // _jwtHandler.setSecret(_state.jwtSecret); } Authentication SecuritySettingsService::authenticateJWT(String & jwt) { DynamicJsonDocument payloadDocument(MAX_JWT_SIZE); - _jwtHandler.parseJWT(jwt, payloadDocument); + // _jwtHandler.parseJWT(jwt, payloadDocument); if (payloadDocument.is()) { JsonObject parsedPayload = payloadDocument.as(); String username = parsedPayload["username"]; @@ -78,7 +82,7 @@ String SecuritySettingsService::generateJWT(User * user) { DynamicJsonDocument jsonDocument(MAX_JWT_SIZE); JsonObject payload = jsonDocument.to(); populateJWTPayload(payload, user); - return _jwtHandler.buildJWT(payload); + return ""; } ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPredicate predicate) { @@ -88,56 +92,26 @@ ArRequestFilterFunction SecuritySettingsService::filterRequest(AuthenticationPre }; } -ArRequestHandlerFunction SecuritySettingsService::wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate) { - return [this, onRequest, predicate](AsyncWebServerRequest * request) { +PsychicHttpRequestCallback SecuritySettingsService::wrapRequest(PsychicHttpRequestCallback onRequest, AuthenticationPredicate predicate) { + return [this, onRequest, predicate](PsychicRequest * request) { Authentication authentication = authenticateRequest(request); if (!predicate(authentication)) { - request->send(401); - return; + return request->reply(401); } - onRequest(request); + return onRequest(request); }; } -ArJsonRequestHandlerFunction SecuritySettingsService::wrapCallback(ArJsonRequestHandlerFunction onRequest, AuthenticationPredicate predicate) { - return [this, onRequest, predicate](AsyncWebServerRequest * request, JsonVariant & json) { +PsychicJsonRequestCallback SecuritySettingsService::wrapCallback(PsychicJsonRequestCallback onRequest, AuthenticationPredicate predicate) { + return [this, onRequest, predicate](PsychicRequest * request, JsonVariant & json) { Authentication authentication = authenticateRequest(request); if (!predicate(authentication)) { - request->send(401); - return; + return request->reply(401); } - onRequest(request, json); + return 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 #endif \ No newline at end of file diff --git a/lib_standalone/SecuritySettingsService.h b/lib_standalone/SecuritySettingsService.h index acc4dec7d..d9161cfb2 100644 --- a/lib_standalone/SecuritySettingsService.h +++ b/lib_standalone/SecuritySettingsService.h @@ -25,7 +25,8 @@ #define SECURITY_SETTINGS_FILE "/config/securitySettings.json" #define SECURITY_SETTINGS_PATH "/rest/securitySettings" -#if FT_ENABLED(FT_SECURITY) +#define GENERATE_TOKEN_SIZE 512 +#define GENERATE_TOKEN_PATH "/rest/generateToken" class SecuritySettings { public: @@ -66,41 +67,32 @@ class SecuritySettings { class SecuritySettingsService : public StatefulService, public SecurityManager { public: - SecuritySettingsService(AsyncWebServer * server, FS * fs); + SecuritySettingsService(PsychicHttpServer * server, FS * fs); void begin(); + void registerURI(); // Functions to implement SecurityManager - Authentication authenticate(const String & username, const String & password); - Authentication authenticateRequest(AsyncWebServerRequest * request); - String generateJWT(User * user); - ArRequestFilterFunction filterRequest(AuthenticationPredicate predicate); - ArRequestHandlerFunction wrapRequest(ArRequestHandlerFunction onRequest, AuthenticationPredicate predicate); - ArJsonRequestHandlerFunction wrapCallback(ArJsonRequestHandlerFunction callback, AuthenticationPredicate predicate); + Authentication authenticate(const String & username, const String & password); + Authentication authenticateRequest(PsychicRequest * request); + String generateJWT(User * user); + + PsychicRequestFilterFunction filterRequest(AuthenticationPredicate predicate); + PsychicHttpRequestCallback wrapRequest(PsychicHttpRequestCallback onRequest, AuthenticationPredicate predicate); + PsychicJsonRequestCallback wrapCallback(PsychicJsonRequestCallback onRequest, AuthenticationPredicate predicate); private: + PsychicHttpServer * _server; + HttpEndpoint _httpEndpoint; FSPersistence _fsPersistence; - ArduinoJsonJWT _jwtHandler; + // ArduinoJsonJWT _jwtHandler; + + esp_err_t generateToken(PsychicRequest * request); void configureJWTHandler(); - Authentication authenticateJWT(String & jwt); - boolean validatePayload(JsonObject & parsedPayload, User * user); -}; - -#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); + Authentication authenticateJWT(String & jwt); // Lookup the user by JWT + boolean validatePayload(JsonObject & parsedPayload, User * user); // Verify the payload is correct }; #endif -#endif diff --git a/platformio.ini b/platformio.ini index d603668c1..a41ae6381 100644 --- a/platformio.ini +++ b/platformio.ini @@ -185,7 +185,7 @@ build_flags = ; platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF5 ; platform_packages = https://github.com/espressif/arduino-esp32.git#3.0.0-alpha2 -platform = espressif32 +platform = espressif32@6.4.0 framework = arduino board = esp32dev board_build.filesystem = littlefs @@ -219,7 +219,8 @@ build_flags = -lpthread -std=gnu++11 -Og -ggdb build_src_flags = - -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-unused-lambda-capture -Wno-sign-compare + ; -Wall -Wextra -Werror -Wswitch-enum -Wno-unused-parameter -Wno-inconsistent-missing-override -Wno-unused-lambda-capture -Wno-sign-compare + ; -Wall -Wextra -Werror -Wno-missing-braces -I./lib_standalone -I./lib/ArduinoJson/src @@ -230,7 +231,6 @@ build_src_flags = -I./lib/PButton -I./lib/espMqttClient/src -I./lib/espMqttClient/src/Transport - -I./lib/PsychicHttp build_src_filter = +<*> -<.git/> @@ -242,6 +242,5 @@ build_src_filter = +<../lib/PButton> +<../lib/espMqttClient/src> +<../lib/espMqttClient/src/Transport> - +<../lib/PsychicHttp> lib_compat_mode = off lib_ldf_mode = off \ No newline at end of file diff --git a/src/emsesp.cpp b/src/emsesp.cpp index e0dce39db..ad67f7268 100644 --- a/src/emsesp.cpp +++ b/src/emsesp.cpp @@ -1,7 +1,7 @@ /* * EMS-ESP - https://github.com/emsesp/EMS-ESP * Copyright 2020-2023 Paul Derbyshire - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or @@ -22,7 +22,9 @@ static_assert(uuid::thread_safe, "uuid-common must be thread-safe"); static_assert(uuid::log::thread_safe, "uuid-log must be thread-safe"); static_assert(uuid::console::thread_safe, "uuid-console must be thread-safe"); +#ifndef EMSESP_STANDALONE #include +#endif namespace emsesp { @@ -1439,7 +1441,9 @@ void EMSESP::setupWeb() { DefaultHeaders::Instance().addHeader("Server", "EMS-ESP"); +#ifndef EMSESP_STANDALONE WWWData::registerRoutes(handler); // add webServer.on() endpoints from the generated web code +#endif esp8266React.registerURI(); // load up the core system web endpoints diff --git a/src/test/test.cpp b/src/test/test.cpp index ce823ebc7..ac72bacfd 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -846,9 +846,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const run_test("boiler"); run_test("thermostat"); - PsychicRequest request; - DynamicJsonDocument doc(2000); - JsonVariant json; + PsychicRequest request; + DynamicJsonDocument doc(2000); + JsonVariant json; request.method(HTTP_GET); request.url("/api/boiler/values"); @@ -915,9 +915,9 @@ void Test::run_test(uuid::console::Shell & shell, const std::string & cmd, const run_test("boiler"); run_test("thermostat"); - PsychicRequest requestX; - DynamicJsonDocument docX(2000); - JsonVariant jsonX; + PsychicRequest requestX; + DynamicJsonDocument docX(2000); + JsonVariant jsonX; requestX.method(HTTP_GET);