diff --git a/CMakeLists.txt b/CMakeLists.txt index 418bf81..4f88b0a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.13) -project(owprov VERSION 2.7.0) +project(owprov VERSION 2.8.0) set(CMAKE_CXX_STANDARD 17) @@ -40,6 +40,7 @@ endif() add_definitions(-DAWS_CUSTOM_MEMORY_MANAGEMENT) find_package(OpenSSL REQUIRED) +find_package(ZLIB REQUIRED) find_package(Poco REQUIRED COMPONENTS Crypto JWT Net Util NetSSL Data DataSQLite) find_package(nlohmann_json REQUIRED) find_package(nlohmann_json_schema_validator REQUIRED) @@ -75,16 +76,55 @@ add_executable(owprov src/framework/OpenWifiTypes.h src/framework/orm.h src/framework/StorageClass.h + src/framework/MicroServiceErrorHandler.h + src/framework/UI_WebSocketClientServer.cpp + src/framework/UI_WebSocketClientServer.h + src/framework/utils.h + src/framework/utils.cpp + src/framework/AppServiceRegistry.h + src/framework/SubSystemServer.cpp + src/framework/SubSystemServer.h + src/framework/RESTAPI_utils.h + src/framework/WebSocketClientNotification.cpp + src/framework/AuthClient.cpp + src/framework/AuthClient.h + src/framework/MicroServiceNames.h + src/framework/MicroServiceFuncs.h + src/framework/OpenAPIRequests.cpp + src/framework/OpenAPIRequests.h + src/framework/MicroServiceFuncs.cpp + src/framework/ALBserver.cpp + src/framework/ALBserver.h + src/framework/KafkaManager.cpp + src/framework/KafkaManager.h + src/framework/RESTAPI_RateLimiter.h + src/framework/WebSocketLogger.h + src/framework/RESTAPI_GenericServerAccounting.h + src/framework/CIDR.h + src/framework/RESTAPI_Handler.cpp + src/framework/RESTAPI_Handler.h + src/framework/RESTAPI_ExtServer.h + src/framework/RESTAPI_ExtServer.cpp + src/framework/RESTAPI_IntServer.cpp + src/framework/RESTAPI_IntServer.h + src/framework/RESTAPI_SystemCommand.h + src/framework/RESTAPI_WebSocketServer.h + src/framework/EventBusManager.cpp + src/framework/EventBusManager.h + src/framework/RESTAPI_PartHandler.h + src/framework/MicroService.cpp + src/framework/MicroServiceExtra.h src/framework/ConfigurationValidator.cpp src/framework/ConfigurationValidator.h - src/framework/ow_constants.h - src/framework/MicroServiceErrorHandler.h - src/framework/WebSocketClientNotifications.h - src/framework/MicroServiceErrorHandler.h src/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp - src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h src/RESTObjects/RESTAPI_GWobjects.h src/RESTObjects/RESTAPI_GWobjects.cpp src/RESTObjects/RESTAPI_FMSObjects.h src/RESTObjects/RESTAPI_FMSObjects.cpp + src/RESTObjects/RESTAPI_CertObjects.cpp src/RESTObjects/RESTAPI_CertObjects.h + src/RESTObjects/RESTAPI_OWLSobjects.cpp src/RESTObjects/RESTAPI_OWLSobjects.h + src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h + src/RESTObjects/RESTAPI_AnalyticsObjects.cpp src/RESTObjects/RESTAPI_AnalyticsObjects.h + src/RESTObjects/RESTAPI_SubObjects.cpp src/RESTObjects/RESTAPI_SubObjects.h + src/RESTAPI/RESTAPI_routers.cpp src/Daemon.cpp src/Daemon.h src/Dashboard.h src/Dashboard.cpp diff --git a/build b/build index 368f89c..56a6051 100644 --- a/build +++ b/build @@ -1 +1 @@ -28 \ No newline at end of file +1 \ No newline at end of file diff --git a/src/APConfig.cpp b/src/APConfig.cpp index f72e477..3735d0d 100644 --- a/src/APConfig.cpp +++ b/src/APConfig.cpp @@ -5,6 +5,8 @@ #include "APConfig.h" #include "StorageService.h" +#include "Poco/JSON/Parser.h" + namespace OpenWifi { APConfig::APConfig(const std::string &SerialNumber, const std::string &DeviceType, Poco::Logger &L, bool Explain) diff --git a/src/AutoDiscovery.cpp b/src/AutoDiscovery.cpp index 9e08530..e564053 100644 --- a/src/AutoDiscovery.cpp +++ b/src/AutoDiscovery.cpp @@ -5,7 +5,9 @@ #include "AutoDiscovery.h" #include "framework/ow_constants.h" #include "framework/KafkaTopics.h" +#include "framework/KafkaManager.h" #include "StorageService.h" +#include "Poco/JSON/Parser.h" namespace OpenWifi { diff --git a/src/AutoDiscovery.h b/src/AutoDiscovery.h index c95cb59..8246b57 100644 --- a/src/AutoDiscovery.h +++ b/src/AutoDiscovery.h @@ -4,8 +4,11 @@ #pragma once -#include "framework/MicroService.h" #include "framework/OpenWifiTypes.h" +#include "framework/SubSystemServer.h" + +#include "Poco/NotificationQueue.h" +#include "Poco/Notification.h" namespace OpenWifi { diff --git a/src/Daemon.cpp b/src/Daemon.cpp index 735fdba..671bd6c 100644 --- a/src/Daemon.cpp +++ b/src/Daemon.cpp @@ -9,6 +9,7 @@ #include "Poco/Util/Application.h" #include "Poco/Util/Option.h" #include "Poco/Environment.h" +#include "Poco/Net/SSLManager.h" #include "Daemon.h" #include "StorageService.h" @@ -20,6 +21,7 @@ #include "Signup.h" #include "DeviceTypeCache.h" #include "FileDownloader.h" +#include "framework/UI_WebSocketClientServer.h" namespace OpenWifi { class Daemon *Daemon::instance_ = nullptr; @@ -38,7 +40,7 @@ namespace OpenWifi { SerialNumberCache(), AutoDiscovery(), JobController(), - WebSocketClientServer(), + UI_WebSocketClientServer(), FindCountryFromIP(), Signup(), FileDownloader() @@ -70,6 +72,11 @@ namespace OpenWifi { } } } + + void DaemonPostInitialization(Poco::Util::Application &self) { + Daemon()->PostInitialization(self); + } + } int main(int argc, char **argv) { diff --git a/src/Daemon.h b/src/Daemon.h index ce9d335..436eaba 100644 --- a/src/Daemon.h +++ b/src/Daemon.h @@ -16,6 +16,7 @@ #include "Dashboard.h" #include "framework/MicroService.h" +#include "framework/MicroServiceNames.h" #include "framework/OpenWifiTypes.h" #include "RESTObjects/RESTAPI_ProvObjects.h" #include "ProvWebSocketClient.h" @@ -54,8 +55,6 @@ namespace OpenWifi { }; inline Daemon * Daemon() { return Daemon::instance(); } - inline void DaemonPostInitialization(Poco::Util::Application &self) { - Daemon()->PostInitialization(self); - } + void DaemonPostInitialization(Poco::Util::Application &self); } diff --git a/src/DeviceTypeCache.h b/src/DeviceTypeCache.h index 242a9b5..fb6855a 100644 --- a/src/DeviceTypeCache.h +++ b/src/DeviceTypeCache.h @@ -6,7 +6,11 @@ #include -#include "framework/MicroService.h" +#include "framework/SubSystemServer.h" +#include "framework/AppServiceRegistry.h" +#include "framework/OpenAPIRequests.h" +#include "framework/MicroServiceNames.h" + #include "Poco/Timer.h" namespace OpenWifi { diff --git a/src/FileDownloader.h b/src/FileDownloader.h index 57ae2df..8f93942 100644 --- a/src/FileDownloader.h +++ b/src/FileDownloader.h @@ -3,7 +3,7 @@ // #pragma once -#include "framework/MicroService.h" +#include "framework/SubSystemServer.h" #include "Poco/Timer.h" namespace OpenWifi { diff --git a/src/FindCountry.h b/src/FindCountry.h index c600579..090d944 100644 --- a/src/FindCountry.h +++ b/src/FindCountry.h @@ -4,8 +4,11 @@ #pragma once -#include "framework/MicroService.h" #include "Poco/Net/IPAddress.h" + +#include "framework/SubSystemServer.h" +#include "framework/MicroServiceFuncs.h" + #include "nlohmann/json.hpp" namespace OpenWifi { @@ -23,7 +26,7 @@ namespace OpenWifi { public: static std::string Name() { return "ipinfo"; } inline bool Init() override { - Key_ = MicroService::instance().ConfigGetString("iptocountry.ipinfo.token", ""); + Key_ = MicroServiceConfigGetString("iptocountry.ipinfo.token", ""); return !Key_.empty(); } @@ -55,7 +58,7 @@ namespace OpenWifi { public: static std::string Name() { return "ipdata"; } inline bool Init() override { - Key_ = MicroService::instance().ConfigGetString("iptocountry.ipdata.apikey", ""); + Key_ = MicroServiceConfigGetString("iptocountry.ipdata.apikey", ""); return !Key_.empty(); } @@ -85,7 +88,7 @@ namespace OpenWifi { public: static std::string Name() { return "ip2location"; } inline bool Init() override { - Key_ = MicroService::instance().ConfigGetString("iptocountry.ip2location.apikey", ""); + Key_ = MicroServiceConfigGetString("iptocountry.ip2location.apikey", ""); return !Key_.empty(); } @@ -133,18 +136,22 @@ namespace OpenWifi { } inline int Start() final { - ProviderName_ = MicroService::instance().ConfigGetString("iptocountry.provider",""); + poco_notice(Logger(),"Starting..."); + ProviderName_ = MicroServiceConfigGetString("iptocountry.provider",""); if(!ProviderName_.empty()) { Provider_ = IPLocationProvider(ProviderName_); if(Provider_!= nullptr) { Enabled_ = Provider_->Init(); } } - Default_ = MicroService::instance().ConfigGetString("iptocountry.default", "US"); + Default_ = MicroServiceConfigGetString("iptocountry.default", "US"); return 0; } inline void Stop() final { + poco_notice(Logger(),"Stopping..."); + // Nothing to do - just to provide the same look at the others. + poco_notice(Logger(),"Stopped..."); } [[nodiscard]] static inline std::string ReformatAddress(const std::string & I ) diff --git a/src/JobController.cpp b/src/JobController.cpp index 0f86ed1..b00b9c3 100644 --- a/src/JobController.cpp +++ b/src/JobController.cpp @@ -3,6 +3,8 @@ // #include "JobController.h" +#include "framework/utils.h" +#include "fmt/format.h" namespace OpenWifi { diff --git a/src/JobController.h b/src/JobController.h index 31be3c6..1faff24 100644 --- a/src/JobController.h +++ b/src/JobController.h @@ -8,7 +8,8 @@ #include #include #include -#include "framework/MicroService.h" +#include "framework/SubSystemServer.h" +#include "RESTObjects/RESTAPI_SecurityObjects.h" namespace OpenWifi { diff --git a/src/Kafka_ProvUpdater.h b/src/Kafka_ProvUpdater.h index c3ce3fb..7c506d2 100644 --- a/src/Kafka_ProvUpdater.h +++ b/src/Kafka_ProvUpdater.h @@ -4,7 +4,8 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/KafkaTopics.h" +#include "framework/KafkaManager.h" #include "RESTObjects/RESTAPI_ProvObjects.h" namespace OpenWifi { diff --git a/src/ProvWebSocketClient.cpp b/src/ProvWebSocketClient.cpp index b24a190..14c7ed9 100644 --- a/src/ProvWebSocketClient.cpp +++ b/src/ProvWebSocketClient.cpp @@ -7,16 +7,17 @@ #include "StorageService.h" #include "SerialNumberCache.h" #include "sdks/SDK_sec.h" +#include "framework/UI_WebSocketClientServer.h" namespace OpenWifi { ProvWebSocketClient::ProvWebSocketClient(Poco::Logger &Logger) : Logger_(Logger){ - WebSocketClientServer()->SetProcessor(this); + UI_WebSocketClientServer()->SetProcessor(this); } ProvWebSocketClient::~ProvWebSocketClient() { - WebSocketClientServer()->SetProcessor(nullptr); + UI_WebSocketClientServer()->SetProcessor(nullptr); } void ProvWebSocketClient::ws_command_serial_number_search(const Poco::JSON::Object::Ptr &O, @@ -117,11 +118,11 @@ namespace OpenWifi { auto Command = O->get("command").toString(); if (Command == "serial_number_search" && O->has("serial_prefix")) { ws_command_serial_number_search(O,Done,Answer); - } else if (WebSocketClientServer()->GeoCodeEnabled() && Command == "address_completion" && O->has("address")) { + } else if (UI_WebSocketClientServer()->GeoCodeEnabled() && Command == "address_completion" && O->has("address")) { ws_command_address_completion(O,Done,Answer); - } else if (WebSocketClientServer()->GeoCodeEnabled() && Command == "subuser_search" && O->has("operatorId")) { + } else if (UI_WebSocketClientServer()->GeoCodeEnabled() && Command == "subuser_search" && O->has("operatorId")) { ws_command_subuser_search(O,Done,Answer); - } else if (WebSocketClientServer()->GeoCodeEnabled() && Command == "subdevice_search" && O->has("operatorId") && O->has("serial_prefix")) { + } else if (UI_WebSocketClientServer()->GeoCodeEnabled() && Command == "subdevice_search" && O->has("operatorId") && O->has("serial_prefix")) { ws_command_subdevice_search(O,Done,Answer); } else if (Command=="exit") { ws_command_exit(O,Done,Answer); @@ -142,7 +143,7 @@ namespace OpenWifi { Poco::URI uri(URI); uri.addQueryParameter("address",A); - uri.addQueryParameter("key", WebSocketClientServer()->GoogleApiKey()); + uri.addQueryParameter("key", UI_WebSocketClientServer()->GoogleApiKey()); Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort()); Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_GET, uri.getPathAndQuery(), Poco::Net::HTTPMessage::HTTP_1_1); diff --git a/src/ProvWebSocketClient.h b/src/ProvWebSocketClient.h index 112231c..f5fd8db 100644 --- a/src/ProvWebSocketClient.h +++ b/src/ProvWebSocketClient.h @@ -4,11 +4,11 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/UI_WebSocketClientServer.h" namespace OpenWifi { - class ProvWebSocketClient : public WebSocketClientProcessor { + class ProvWebSocketClient : public UI_WebSocketClientProcessor { public: explicit ProvWebSocketClient(Poco::Logger &Logger); virtual ~ProvWebSocketClient(); diff --git a/src/RESTAPI/RESTAPI_asset_server.h b/src/RESTAPI/RESTAPI_asset_server.h index 3d60b33..8cfb8df 100644 --- a/src/RESTAPI/RESTAPI_asset_server.h +++ b/src/RESTAPI/RESTAPI_asset_server.h @@ -3,13 +3,13 @@ // #pragma once - -#include "../framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" +#include "StorageService.h" namespace OpenWifi { class RESTAPI_asset_server : public RESTAPIHandler { public: - RESTAPI_asset_server(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_asset_server(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector { diff --git a/src/RESTAPI/RESTAPI_configurations_handler.cpp b/src/RESTAPI/RESTAPI_configurations_handler.cpp index 26d433e..ec136af 100644 --- a/src/RESTAPI/RESTAPI_configurations_handler.cpp +++ b/src/RESTAPI/RESTAPI_configurations_handler.cpp @@ -6,8 +6,6 @@ // Arilia Wireless Inc. // -#include "framework/MicroService.h" - #include "RESTAPI_configurations_handler.h" #include "RESTObjects/RESTAPI_ProvObjects.h" #include "StorageService.h" diff --git a/src/RESTAPI/RESTAPI_configurations_handler.h b/src/RESTAPI/RESTAPI_configurations_handler.h index f4638c9..51b93d4 100644 --- a/src/RESTAPI/RESTAPI_configurations_handler.h +++ b/src/RESTAPI/RESTAPI_configurations_handler.h @@ -5,16 +5,15 @@ // Created by Stephane Bourque on 2021-03-04. // Arilia Wireless Inc. // -#pragma once -#include "framework/MicroService.h" -#include "RESTObjects/RESTAPI_ProvObjects.h" +#pragma once +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_configurations_handler : public RESTAPIHandler { public: - RESTAPI_configurations_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_configurations_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_configurations_list_handler.h b/src/RESTAPI/RESTAPI_configurations_list_handler.h index 5fd40ec..02df495 100644 --- a/src/RESTAPI/RESTAPI_configurations_list_handler.h +++ b/src/RESTAPI/RESTAPI_configurations_list_handler.h @@ -3,15 +3,14 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_configurations_list_handler : public RESTAPIHandler { public: - RESTAPI_configurations_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_configurations_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_contact_handler.h b/src/RESTAPI/RESTAPI_contact_handler.h index d65dc58..652fdeb 100644 --- a/src/RESTAPI/RESTAPI_contact_handler.h +++ b/src/RESTAPI/RESTAPI_contact_handler.h @@ -7,14 +7,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_contact_handler : public RESTAPIHandler { public: - RESTAPI_contact_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_contact_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_contact_list_handler.h b/src/RESTAPI/RESTAPI_contact_list_handler.h index 13e9dd9..9747e00 100644 --- a/src/RESTAPI/RESTAPI_contact_list_handler.h +++ b/src/RESTAPI/RESTAPI_contact_list_handler.h @@ -3,16 +3,14 @@ // #pragma once - -#include "framework/MicroService.h" -#include "RESTObjects/RESTAPI_ProvObjects.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_contact_list_handler : public RESTAPIHandler { public: - RESTAPI_contact_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_contact_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_db_helpers.h b/src/RESTAPI/RESTAPI_db_helpers.h index d6e4e32..3197a7e 100644 --- a/src/RESTAPI/RESTAPI_db_helpers.h +++ b/src/RESTAPI/RESTAPI_db_helpers.h @@ -6,7 +6,6 @@ #include "RESTObjects/RESTAPI_ProvObjects.h" #include "StorageService.h" -#include "framework/MicroService.h" #include "framework/ConfigurationValidator.h" #include "sdks/SDK_sec.h" #include "Poco/StringTokenizer.h" diff --git a/src/RESTAPI/RESTAPI_entity_handler.cpp b/src/RESTAPI/RESTAPI_entity_handler.cpp index c8788e5..99c3673 100644 --- a/src/RESTAPI/RESTAPI_entity_handler.cpp +++ b/src/RESTAPI/RESTAPI_entity_handler.cpp @@ -14,6 +14,8 @@ #include "StorageService.h" #include "RESTAPI_db_helpers.h" +#include "framework/CIDR.h" + namespace OpenWifi{ void RESTAPI_entity_handler::DoGet() { @@ -78,7 +80,7 @@ namespace OpenWifi{ // When creating an entity, it cannot have any relations other that parent, notes, name, description. Everything else // must be conveyed through PUT. - NewEntity.info.id = (UUID==EntityDB::RootUUID()) ? UUID : MicroService::CreateUUID(); + NewEntity.info.id = (UUID==EntityDB::RootUUID()) ? UUID : MicroServiceCreateUUID(); if(UUID==EntityDB::RootUUID()) { NewEntity.parent=""; diff --git a/src/RESTAPI/RESTAPI_entity_handler.h b/src/RESTAPI/RESTAPI_entity_handler.h index b2cc324..512c7ee 100644 --- a/src/RESTAPI/RESTAPI_entity_handler.h +++ b/src/RESTAPI/RESTAPI_entity_handler.h @@ -7,14 +7,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_entity_handler : public RESTAPIHandler { public: - RESTAPI_entity_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_entity_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_entity_list_handler.cpp b/src/RESTAPI/RESTAPI_entity_list_handler.cpp index 40c3b12..7659190 100644 --- a/src/RESTAPI/RESTAPI_entity_list_handler.cpp +++ b/src/RESTAPI/RESTAPI_entity_list_handler.cpp @@ -6,7 +6,6 @@ // Arilia Wireless Inc. // -#include "framework/MicroService.h" #include "RESTAPI_entity_list_handler.h" #include "StorageService.h" #include "RESTAPI_db_helpers.h" diff --git a/src/RESTAPI/RESTAPI_entity_list_handler.h b/src/RESTAPI/RESTAPI_entity_list_handler.h index 0b9d7f6..eb7fcfe 100644 --- a/src/RESTAPI/RESTAPI_entity_list_handler.h +++ b/src/RESTAPI/RESTAPI_entity_list_handler.h @@ -8,14 +8,13 @@ #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_entity_list_handler : public RESTAPIHandler { public: - RESTAPI_entity_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_entity_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_inventory_handler.h b/src/RESTAPI/RESTAPI_inventory_handler.h index 1cfb6f8..ad8d44d 100644 --- a/src/RESTAPI/RESTAPI_inventory_handler.h +++ b/src/RESTAPI/RESTAPI_inventory_handler.h @@ -7,14 +7,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_inventory_handler : public RESTAPIHandler { public: - RESTAPI_inventory_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_inventory_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_inventory_list_handler.h b/src/RESTAPI/RESTAPI_inventory_list_handler.h index 8840943..af79791 100644 --- a/src/RESTAPI/RESTAPI_inventory_list_handler.h +++ b/src/RESTAPI/RESTAPI_inventory_list_handler.h @@ -7,15 +7,14 @@ // #pragma once - +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" -#include "framework/MicroService.h" namespace OpenWifi { class RESTAPI_inventory_list_handler : public RESTAPIHandler { public: - RESTAPI_inventory_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_inventory_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_iptocountry_handler.h b/src/RESTAPI/RESTAPI_iptocountry_handler.h index 06242f5..b09c8fb 100644 --- a/src/RESTAPI/RESTAPI_iptocountry_handler.h +++ b/src/RESTAPI/RESTAPI_iptocountry_handler.h @@ -3,13 +3,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" +#include "StorageService.h" namespace OpenWifi { class RESTAPI_iptocountry_handler : public RESTAPIHandler { public: - RESTAPI_iptocountry_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_iptocountry_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_OPTIONS}, diff --git a/src/RESTAPI/RESTAPI_location_handler.h b/src/RESTAPI/RESTAPI_location_handler.h index fd1d175..c52c704 100644 --- a/src/RESTAPI/RESTAPI_location_handler.h +++ b/src/RESTAPI/RESTAPI_location_handler.h @@ -7,14 +7,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_location_handler : public RESTAPIHandler { public: - RESTAPI_location_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_location_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_location_list_handler.h b/src/RESTAPI/RESTAPI_location_list_handler.h index a07b0c5..8fe03a9 100644 --- a/src/RESTAPI/RESTAPI_location_list_handler.h +++ b/src/RESTAPI/RESTAPI_location_list_handler.h @@ -3,15 +3,14 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_location_list_handler : public RESTAPIHandler { public: - RESTAPI_location_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_location_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_managementPolicy_handler.h b/src/RESTAPI/RESTAPI_managementPolicy_handler.h index 2e15430..2c3cb9c 100644 --- a/src/RESTAPI/RESTAPI_managementPolicy_handler.h +++ b/src/RESTAPI/RESTAPI_managementPolicy_handler.h @@ -7,13 +7,14 @@ // -#include "framework/MicroService.h" +#pragma once +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_managementPolicy_handler : public RESTAPIHandler { public: - RESTAPI_managementPolicy_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_managementPolicy_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_managementPolicy_list_handler.h b/src/RESTAPI/RESTAPI_managementPolicy_list_handler.h index 97229e8..cf9a8ab 100644 --- a/src/RESTAPI/RESTAPI_managementPolicy_list_handler.h +++ b/src/RESTAPI/RESTAPI_managementPolicy_list_handler.h @@ -2,14 +2,15 @@ // Created by stephane bourque on 2021-08-26. // -#include "framework/MicroService.h" +#pragma once +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_managementPolicy_list_handler : public RESTAPIHandler { public: - RESTAPI_managementPolicy_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_managementPolicy_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_managementRole_handler.h b/src/RESTAPI/RESTAPI_managementRole_handler.h index 41edb40..8555d16 100644 --- a/src/RESTAPI/RESTAPI_managementRole_handler.h +++ b/src/RESTAPI/RESTAPI_managementRole_handler.h @@ -2,13 +2,14 @@ // Created by stephane bourque on 2021-08-26. // -#include "framework/MicroService.h" +#pragma once +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_managementRole_handler : public RESTAPIHandler { public: - RESTAPI_managementRole_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_managementRole_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_managementRole_list_handler.cpp b/src/RESTAPI/RESTAPI_managementRole_list_handler.cpp index c6d7832..92d4759 100644 --- a/src/RESTAPI/RESTAPI_managementRole_list_handler.cpp +++ b/src/RESTAPI/RESTAPI_managementRole_list_handler.cpp @@ -2,10 +2,7 @@ // Created by stephane bourque on 2021-08-26. // -#include "framework/MicroService.h" - #include "RESTAPI_managementRole_list_handler.h" -#include "RESTObjects/RESTAPI_ProvObjects.h" #include "StorageService.h" #include "RESTAPI/RESTAPI_db_helpers.h" diff --git a/src/RESTAPI/RESTAPI_managementRole_list_handler.h b/src/RESTAPI/RESTAPI_managementRole_list_handler.h index 34e60ef..181b3c4 100644 --- a/src/RESTAPI/RESTAPI_managementRole_list_handler.h +++ b/src/RESTAPI/RESTAPI_managementRole_list_handler.h @@ -1,16 +1,16 @@ // // Created by stephane bourque on 2021-08-26. // -#pragma once -#include "framework/MicroService.h" +#pragma once +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_managementRole_list_handler : public RESTAPIHandler { public: - RESTAPI_managementRole_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_managementRole_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_map_handler.h b/src/RESTAPI/RESTAPI_map_handler.h index 87a043a..9f1abfe 100644 --- a/src/RESTAPI/RESTAPI_map_handler.h +++ b/src/RESTAPI/RESTAPI_map_handler.h @@ -2,14 +2,13 @@ // Created by stephane bourque on 2021-11-09. // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_map_handler : public RESTAPIHandler { public: - RESTAPI_map_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_map_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_map_list_handler.h b/src/RESTAPI/RESTAPI_map_list_handler.h index 705f642..1bd52db 100644 --- a/src/RESTAPI/RESTAPI_map_list_handler.h +++ b/src/RESTAPI/RESTAPI_map_list_handler.h @@ -2,15 +2,14 @@ // Created by stephane bourque on 2021-11-09. // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_map_list_handler : public RESTAPIHandler { public: - RESTAPI_map_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_map_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_op_contact_handler.h b/src/RESTAPI/RESTAPI_op_contact_handler.h index 22fba33..c3e9c64 100644 --- a/src/RESTAPI/RESTAPI_op_contact_handler.h +++ b/src/RESTAPI/RESTAPI_op_contact_handler.h @@ -3,14 +3,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_op_contact_handler : public RESTAPIHandler { public: - RESTAPI_op_contact_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_op_contact_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_op_contact_list_handler.h b/src/RESTAPI/RESTAPI_op_contact_list_handler.h index a1245f1..d74d21b 100644 --- a/src/RESTAPI/RESTAPI_op_contact_list_handler.h +++ b/src/RESTAPI/RESTAPI_op_contact_list_handler.h @@ -2,15 +2,14 @@ // Created by stephane bourque on 2022-04-07. // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_op_contact_list_handler : public RESTAPIHandler { public: - RESTAPI_op_contact_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_op_contact_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_op_location_handler.h b/src/RESTAPI/RESTAPI_op_location_handler.h index 89c8048..aac8954 100644 --- a/src/RESTAPI/RESTAPI_op_location_handler.h +++ b/src/RESTAPI/RESTAPI_op_location_handler.h @@ -3,14 +3,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_op_location_handler : public RESTAPIHandler { public: - RESTAPI_op_location_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_op_location_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_op_location_list_handler.h b/src/RESTAPI/RESTAPI_op_location_list_handler.h index 4520385..3bf7436 100644 --- a/src/RESTAPI/RESTAPI_op_location_list_handler.h +++ b/src/RESTAPI/RESTAPI_op_location_list_handler.h @@ -3,15 +3,14 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_op_location_list_handler : public RESTAPIHandler { public: - RESTAPI_op_location_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_op_location_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_operators_handler.cpp b/src/RESTAPI/RESTAPI_operators_handler.cpp index 2704670..a42e1f1 100644 --- a/src/RESTAPI/RESTAPI_operators_handler.cpp +++ b/src/RESTAPI/RESTAPI_operators_handler.cpp @@ -83,7 +83,7 @@ namespace OpenWifi { // Create the default service... ProvObjects::ServiceClass DefSer; - DefSer.info.id = MicroService::CreateUUID(); + DefSer.info.id = MicroServiceCreateUUID(); DefSer.info.name = "Default Service Class"; DefSer.defaultService = true; DefSer.info.created = DefSer.info.modified = OpenWifi::Now(); diff --git a/src/RESTAPI/RESTAPI_operators_handler.h b/src/RESTAPI/RESTAPI_operators_handler.h index 552f6ca..48b39e6 100644 --- a/src/RESTAPI/RESTAPI_operators_handler.h +++ b/src/RESTAPI/RESTAPI_operators_handler.h @@ -3,14 +3,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_operators_handler : public RESTAPIHandler { public: - RESTAPI_operators_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_operators_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_operators_list_handler.h b/src/RESTAPI/RESTAPI_operators_list_handler.h index 21b9cbe..23f3ece 100644 --- a/src/RESTAPI/RESTAPI_operators_list_handler.h +++ b/src/RESTAPI/RESTAPI_operators_list_handler.h @@ -2,15 +2,14 @@ // Created by stephane bourque on 2022-04-06. // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_operators_list_handler : public RESTAPIHandler { public: - RESTAPI_operators_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_operators_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_routers.cpp b/src/RESTAPI/RESTAPI_routers.cpp index 5b5d3e1..4c0a022 100644 --- a/src/RESTAPI/RESTAPI_routers.cpp +++ b/src/RESTAPI/RESTAPI_routers.cpp @@ -2,8 +2,6 @@ // Created by stephane bourque on 2021-10-23. // -#include "framework/MicroService.h" - #include "RESTAPI/RESTAPI_entity_handler.h" #include "RESTAPI/RESTAPI_contact_handler.h" #include "RESTAPI/RESTAPI_location_handler.h" @@ -36,12 +34,13 @@ #include "RESTAPI/RESTAPI_op_contact_list_handler.h" #include "RESTAPI/RESTAPI_op_location_handler.h" #include "RESTAPI/RESTAPI_op_location_list_handler.h" - +#include "framework/RESTAPI_SystemCommand.h" +#include "framework/RESTAPI_WebSocketServer.h" namespace OpenWifi { Poco::Net::HTTPRequestHandler * RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, - Poco::Logger & L, RESTAPI_GenericServer & S, uint64_t TransactionId) { + Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t TransactionId) { return RESTAPI_Router< RESTAPI_system_command, RESTAPI_entity_handler, @@ -81,7 +80,7 @@ namespace OpenWifi { } Poco::Net::HTTPRequestHandler * RESTAPI_IntRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, - Poco::Logger & L, RESTAPI_GenericServer & S, uint64_t TransactionId) { + Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t TransactionId) { return RESTAPI_Router_I< RESTAPI_system_command, RESTAPI_entity_handler, diff --git a/src/RESTAPI/RESTAPI_service_class_handler.h b/src/RESTAPI/RESTAPI_service_class_handler.h index 6d3e31b..ec67e91 100644 --- a/src/RESTAPI/RESTAPI_service_class_handler.h +++ b/src/RESTAPI/RESTAPI_service_class_handler.h @@ -3,14 +3,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_service_class_handler : public RESTAPIHandler { public: - RESTAPI_service_class_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_service_class_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_service_class_list_handler.h b/src/RESTAPI/RESTAPI_service_class_list_handler.h index c4eb085..0044455 100644 --- a/src/RESTAPI/RESTAPI_service_class_list_handler.h +++ b/src/RESTAPI/RESTAPI_service_class_list_handler.h @@ -3,15 +3,14 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_service_class_list_handler : public RESTAPIHandler { public: - RESTAPI_service_class_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_service_class_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_signup_handler.cpp b/src/RESTAPI/RESTAPI_signup_handler.cpp index a6472b1..cdf855c 100644 --- a/src/RESTAPI/RESTAPI_signup_handler.cpp +++ b/src/RESTAPI/RESTAPI_signup_handler.cpp @@ -5,6 +5,9 @@ #include "RESTAPI_signup_handler.h" #include "StorageService.h" #include "Signup.h" +#include "framework/OpenAPIRequests.h" +#include "framework/MicroServiceNames.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { @@ -93,7 +96,7 @@ namespace OpenWifi { // OK, we can claim this device, can we create a userid? // Let's create one // If sec.signup("email",uuid); - auto SignupUUID = MicroService::instance().CreateUUID(); + auto SignupUUID = MicroServiceCreateUUID(); Logger().information(fmt::format("SIGNUP: Creating signup entry for '{}', uuid='{}'",UserName, SignupUUID)); Poco::JSON::Object Body; diff --git a/src/RESTAPI/RESTAPI_signup_handler.h b/src/RESTAPI/RESTAPI_signup_handler.h index e5e8ead..95ea00b 100644 --- a/src/RESTAPI/RESTAPI_signup_handler.h +++ b/src/RESTAPI/RESTAPI_signup_handler.h @@ -3,13 +3,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" +#include "StorageService.h" namespace OpenWifi { class RESTAPI_signup_handler : public RESTAPIHandler { public: - RESTAPI_signup_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_signup_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_sub_devices_handler.h b/src/RESTAPI/RESTAPI_sub_devices_handler.h index f069855..a0861fb 100644 --- a/src/RESTAPI/RESTAPI_sub_devices_handler.h +++ b/src/RESTAPI/RESTAPI_sub_devices_handler.h @@ -3,14 +3,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_sub_devices_handler : public RESTAPIHandler { public: - RESTAPI_sub_devices_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_sub_devices_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_sub_devices_list_handler.h b/src/RESTAPI/RESTAPI_sub_devices_list_handler.h index de7968c..45683b5 100644 --- a/src/RESTAPI/RESTAPI_sub_devices_list_handler.h +++ b/src/RESTAPI/RESTAPI_sub_devices_list_handler.h @@ -3,15 +3,14 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_sub_devices_list_handler : public RESTAPIHandler { public: - RESTAPI_sub_devices_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_sub_devices_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_variables_handler.h b/src/RESTAPI/RESTAPI_variables_handler.h index d910f5e..5e997ed 100644 --- a/src/RESTAPI/RESTAPI_variables_handler.h +++ b/src/RESTAPI/RESTAPI_variables_handler.h @@ -1,15 +1,15 @@ // // Created by stephane bourque on 2022-02-23. // -#pragma once -#include "framework/MicroService.h" +#pragma once +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_variables_handler : public RESTAPIHandler { public: - RESTAPI_variables_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_variables_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_variables_list_handler.h b/src/RESTAPI/RESTAPI_variables_list_handler.h index 32d1dd2..677dd58 100644 --- a/src/RESTAPI/RESTAPI_variables_list_handler.h +++ b/src/RESTAPI/RESTAPI_variables_list_handler.h @@ -3,15 +3,14 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_variables_list_handler : public RESTAPIHandler { public: - RESTAPI_variables_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_variables_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_venue_handler.cpp b/src/RESTAPI/RESTAPI_venue_handler.cpp index 9956619..73c69fa 100644 --- a/src/RESTAPI/RESTAPI_venue_handler.cpp +++ b/src/RESTAPI/RESTAPI_venue_handler.cpp @@ -15,6 +15,8 @@ #include "Tasks/VenueConfigUpdater.h" #include "Tasks/VenueRebooter.h" #include "Tasks/VenueUpgrade.h" +#include "framework/CIDR.h" +#include "framework/MicroServiceFuncs.h" #include "Kafka_ProvUpdater.h" @@ -228,7 +230,7 @@ namespace OpenWifi{ Poco::JSON::Object Answer; SNL.serialNumbers = Existing.devices; - auto JobId = MicroService::instance().CreateUUID(); + auto JobId = MicroServiceCreateUUID(); Types::StringVec Parameters{UUID};; auto NewJob = new VenueConfigUpdater(JobId,"VenueConfigurationUpdater", Parameters, 0, UserInfo_.userinfo, Logger()); JobController()->AddJob(dynamic_cast(NewJob)); @@ -242,7 +244,7 @@ namespace OpenWifi{ Poco::JSON::Object Answer; SNL.serialNumbers = Existing.devices; - auto JobId = MicroService::instance().CreateUUID(); + auto JobId = MicroServiceCreateUUID(); Types::StringVec Parameters{UUID};; auto NewJob = new VenueUpgrade(JobId,"VenueFirmwareUpgrade", Parameters, 0, UserInfo_.userinfo, Logger()); JobController()->AddJob(dynamic_cast(NewJob)); @@ -256,7 +258,7 @@ namespace OpenWifi{ Poco::JSON::Object Answer; SNL.serialNumbers = Existing.devices; - auto JobId = MicroService::instance().CreateUUID(); + auto JobId = MicroServiceCreateUUID(); Types::StringVec Parameters{UUID};; auto NewJob = new VenueRebooter(JobId,"VenueRebooter", Parameters, 0, UserInfo_.userinfo, Logger()); JobController()->AddJob(dynamic_cast(NewJob)); diff --git a/src/RESTAPI/RESTAPI_venue_handler.h b/src/RESTAPI/RESTAPI_venue_handler.h index 3e1f063..cbea27a 100644 --- a/src/RESTAPI/RESTAPI_venue_handler.h +++ b/src/RESTAPI/RESTAPI_venue_handler.h @@ -7,14 +7,13 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_venue_handler : public RESTAPIHandler { public: - RESTAPI_venue_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_venue_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_venue_list_handler.h b/src/RESTAPI/RESTAPI_venue_list_handler.h index 7d4d2da..59810e6 100644 --- a/src/RESTAPI/RESTAPI_venue_list_handler.h +++ b/src/RESTAPI/RESTAPI_venue_list_handler.h @@ -3,15 +3,14 @@ // #pragma once - -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "StorageService.h" namespace OpenWifi { class RESTAPI_venue_list_handler : public RESTAPIHandler { public: - RESTAPI_venue_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) + RESTAPI_venue_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) : RESTAPIHandler(bindings, L, std::vector{ Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTObjects/RESTAPI_AnalyticsObjects.cpp b/src/RESTObjects/RESTAPI_AnalyticsObjects.cpp index e7738ba..6e095e3 100644 --- a/src/RESTObjects/RESTAPI_AnalyticsObjects.cpp +++ b/src/RESTObjects/RESTAPI_AnalyticsObjects.cpp @@ -4,7 +4,7 @@ #include "RESTAPI_AnalyticsObjects.h" #include "RESTAPI_ProvObjects.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; diff --git a/src/RESTObjects/RESTAPI_CertObjects.cpp b/src/RESTObjects/RESTAPI_CertObjects.cpp index ea55bd4..adad51a 100644 --- a/src/RESTObjects/RESTAPI_CertObjects.cpp +++ b/src/RESTObjects/RESTAPI_CertObjects.cpp @@ -3,7 +3,7 @@ // #include "RESTAPI_CertObjects.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; diff --git a/src/RESTObjects/RESTAPI_FMSObjects.cpp b/src/RESTObjects/RESTAPI_FMSObjects.cpp index ceb3619..6b0892a 100644 --- a/src/RESTObjects/RESTAPI_FMSObjects.cpp +++ b/src/RESTObjects/RESTAPI_FMSObjects.cpp @@ -3,7 +3,7 @@ // #include "RESTAPI_FMSObjects.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; diff --git a/src/RESTObjects/RESTAPI_GWobjects.cpp b/src/RESTObjects/RESTAPI_GWobjects.cpp index d6b3f1d..3b1ce84 100644 --- a/src/RESTObjects/RESTAPI_GWobjects.cpp +++ b/src/RESTObjects/RESTAPI_GWobjects.cpp @@ -11,12 +11,12 @@ #include "Daemon.h" #ifdef TIP_GATEWAY_SERVICE -#include "DeviceRegistry.h" +#include "AP_WS_Server.h" #include "CapabilitiesCache.h" #endif #include "RESTAPI_GWobjects.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; @@ -57,7 +57,7 @@ namespace OpenWifi::GWObjects { #ifdef TIP_GATEWAY_SERVICE ConnectionState ConState; - if (DeviceRegistry()->GetState(SerialNumber, ConState)) { + if (AP_WS_Server()->GetState(SerialNumber, ConState)) { ConState.to_json(Obj); } else { field_to_json(Obj,"ipAddress", ""); @@ -225,12 +225,14 @@ namespace OpenWifi::GWObjects { void DeviceConnectionStatistics::to_json(Poco::JSON::Object &Obj) const { field_to_json(Obj,"averageConnectionTime", averageConnectionTime); field_to_json(Obj,"connectedDevices", connectedDevices ); + field_to_json(Obj,"connectingDevices", connectingDevices ); } bool DeviceConnectionStatistics::from_json(const Poco::JSON::Object::Ptr &Obj) { try { field_from_json(Obj,"averageConnectionTime", averageConnectionTime); field_from_json(Obj,"connectedDevices", connectedDevices ); + field_from_json(Obj,"connectingDevices", connectingDevices ); return true; } catch (const Poco::Exception &E) { } diff --git a/src/RESTObjects/RESTAPI_GWobjects.h b/src/RESTObjects/RESTAPI_GWobjects.h index 2a2b2a9..899b3b2 100644 --- a/src/RESTObjects/RESTAPI_GWobjects.h +++ b/src/RESTObjects/RESTAPI_GWobjects.h @@ -78,6 +78,8 @@ namespace OpenWifi::GWObjects { struct DeviceConnectionStatistics { std::uint64_t connectedDevices = 0; std::uint64_t averageConnectionTime = 0; + std::uint64_t connectingDevices = 0; + void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; diff --git a/src/RESTObjects/RESTAPI_OWLSobjects.cpp b/src/RESTObjects/RESTAPI_OWLSobjects.cpp index 5a16165..15e5f0a 100644 --- a/src/RESTObjects/RESTAPI_OWLSobjects.cpp +++ b/src/RESTObjects/RESTAPI_OWLSobjects.cpp @@ -2,7 +2,7 @@ // Created by stephane bourque on 2021-08-31. // -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; diff --git a/src/RESTObjects/RESTAPI_ProvObjects.cpp b/src/RESTObjects/RESTAPI_ProvObjects.cpp index 927730a..636fd17 100644 --- a/src/RESTObjects/RESTAPI_ProvObjects.cpp +++ b/src/RESTObjects/RESTAPI_ProvObjects.cpp @@ -8,7 +8,8 @@ #include "RESTAPI_ProvObjects.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" +#include "framework/MicroServiceFuncs.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; @@ -1130,14 +1131,14 @@ namespace OpenWifi::ProvObjects { } I.notes = N; I.modified = I.created = Now; - I.id = MicroService::CreateUUID(); + I.id = MicroServiceCreateUUID(); return true; } bool CreateObjectInfo([[maybe_unused]] const SecurityObjects::UserInfo &U, ObjectInfo &I) { I.modified = I.created = OpenWifi::Now(); - I.id = MicroService::CreateUUID(); + I.id = MicroServiceCreateUUID(); return true; } diff --git a/src/RESTObjects/RESTAPI_ProvObjects.h b/src/RESTObjects/RESTAPI_ProvObjects.h index c417230..59f59ed 100644 --- a/src/RESTObjects/RESTAPI_ProvObjects.h +++ b/src/RESTObjects/RESTAPI_ProvObjects.h @@ -8,8 +8,7 @@ #pragma once -#include -#include "RESTAPI_SecurityObjects.h" +#include "RESTObjects/RESTAPI_SecurityObjects.h" namespace OpenWifi::ProvObjects { diff --git a/src/RESTObjects/RESTAPI_SecurityObjects.cpp b/src/RESTObjects/RESTAPI_SecurityObjects.cpp index a077e40..a8f6b4c 100644 --- a/src/RESTObjects/RESTAPI_SecurityObjects.cpp +++ b/src/RESTObjects/RESTAPI_SecurityObjects.cpp @@ -9,7 +9,7 @@ #include "Poco/JSON/Parser.h" #include "Poco/JSON/Stringifier.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "RESTAPI_SecurityObjects.h" using OpenWifi::RESTAPI_utils::field_to_json; diff --git a/src/RESTObjects/RESTAPI_SecurityObjects.h b/src/RESTObjects/RESTAPI_SecurityObjects.h index a3f06e8..ef74bc4 100644 --- a/src/RESTObjects/RESTAPI_SecurityObjects.h +++ b/src/RESTObjects/RESTAPI_SecurityObjects.h @@ -251,7 +251,8 @@ namespace OpenWifi { VERIFY_EMAIL, SUB_FORGOT_PASSWORD, SUB_VERIFY_EMAIL, - SUB_SIGNUP + SUB_SIGNUP, + EMAIL_INVITATION }; struct ActionLink { diff --git a/src/RESTObjects/RESTAPI_SubObjects.cpp b/src/RESTObjects/RESTAPI_SubObjects.cpp index c5b234c..a362b15 100644 --- a/src/RESTObjects/RESTAPI_SubObjects.cpp +++ b/src/RESTObjects/RESTAPI_SubObjects.cpp @@ -3,12 +3,11 @@ // #include "RESTAPI_SubObjects.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; - namespace OpenWifi::SubObjects { void HomeDeviceMode::to_json(Poco::JSON::Object &Obj) const { diff --git a/src/SerialNumberCache.cpp b/src/SerialNumberCache.cpp index 82f8b7d..c254946 100644 --- a/src/SerialNumberCache.cpp +++ b/src/SerialNumberCache.cpp @@ -2,11 +2,9 @@ // Created by stephane bourque on 2021-08-11. // -#include "SerialNumberCache.h" -#include -#include "StorageService.h" -#include "framework/MicroService.h" +#include "SerialNumberCache.h" +#include "framework/utils.h" namespace OpenWifi { diff --git a/src/SerialNumberCache.h b/src/SerialNumberCache.h index b843551..ae7d3eb 100644 --- a/src/SerialNumberCache.h +++ b/src/SerialNumberCache.h @@ -4,7 +4,8 @@ #pragma once -#include "framework/MicroService.h" +#include +#include "framework/SubSystemServer.h" namespace OpenWifi { class SerialNumberCache : public SubSystemServer { diff --git a/src/Signup.cpp b/src/Signup.cpp index d783ee4..99be630 100644 --- a/src/Signup.cpp +++ b/src/Signup.cpp @@ -5,12 +5,13 @@ #include "Signup.h" #include "StorageService.h" #include "sdks/SDK_gw.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { int Signup::Start() { - GracePeriod_ = MicroService::instance().ConfigGetInt("signup.graceperiod", 60*60); - LingerPeriod_ = MicroService::instance().ConfigGetInt("signup.lingerperiod", 24*60*60); + GracePeriod_ = MicroServiceConfigGetInt("signup.graceperiod", 60*60); + LingerPeriod_ = MicroServiceConfigGetInt("signup.lingerperiod", 24*60*60); SignupDB::RecordVec Signups_; StorageService()->SignupDB().GetIncompleteSignups(Signups_); @@ -86,7 +87,7 @@ namespace OpenWifi { // we need to move this device to the SubscriberDevice DB ProvObjects::SubscriberDevice SD; - SD.info.id = MicroService::CreateUUID(); + SD.info.id = MicroServiceCreateUUID(); SD.info.modified = SD.info.created = OpenWifi::Now(); SD.info.name = IT.realMacAddress; SD.operatorId = SE.operatorId; diff --git a/src/Signup.h b/src/Signup.h index 8e40afd..69adc64 100644 --- a/src/Signup.h +++ b/src/Signup.h @@ -4,7 +4,7 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/SubSystemServer.h" #include "RESTObjects/RESTAPI_ProvObjects.h" #include "Poco/Timer.h" diff --git a/src/StorageService.cpp b/src/StorageService.cpp index d0af97a..cdd932c 100644 --- a/src/StorageService.cpp +++ b/src/StorageService.cpp @@ -8,6 +8,8 @@ #include "StorageService.h" #include "RESTObjects/RESTAPI_ProvObjects.h" +#include "framework/utils.h" +#include "fmt/format.h" namespace OpenWifi { @@ -405,7 +407,7 @@ namespace OpenWifi { auto OperatorCount = OperatorDB().Count(); if(OperatorCount==0) { ProvObjects::Operator DefOp; - DefOp.info.id = MicroService::CreateUUID(); + DefOp.info.id = MicroServiceCreateUUID(); DefOp.info.name = "Default Operator"; DefOp.defaultOperator = true; DefOp.info.created = DefOp.info.modified = OpenWifi::Now(); @@ -413,7 +415,7 @@ namespace OpenWifi { OperatorDB_->CreateRecord(DefOp); ProvObjects::ServiceClass DefSer; - DefSer.info.id = MicroService::CreateUUID(); + DefSer.info.id = MicroServiceCreateUUID(); DefSer.info.name = "Default Service Class"; DefSer.defaultService = true; DefSer.info.created = DefSer.info.modified = OpenWifi::Now(); diff --git a/src/StorageService.h b/src/StorageService.h index 371d209..c9c5e6e 100644 --- a/src/StorageService.h +++ b/src/StorageService.h @@ -8,9 +8,8 @@ #pragma once -#include "framework/MicroService.h" #include "framework/StorageClass.h" - +#include "framework/MicroServiceFuncs.h" #include "storage/storage_entity.h" #include "storage/storage_policies.h" #include "storage/storage_venue.h" @@ -29,6 +28,9 @@ #include "storage/storage_service_class.h" #include "storage/storage_sub_devices.h" +#include "Poco/URI.h" +#include "framework/ow_constants.h" + namespace OpenWifi { class Storage : public StorageClass { @@ -87,11 +89,11 @@ namespace OpenWifi { static inline bool ApplyConfigRules(ProvObjects::DeviceRules & R_res) { if(R_res.firmwareUpgrade=="inherit") - R_res.firmwareUpgrade=MicroService::instance().ConfigGetString("firmware.updater.upgrade","yes"); + R_res.firmwareUpgrade=MicroServiceConfigGetString("firmware.updater.upgrade","yes"); if(R_res.rcOnly=="inherit") - R_res.rcOnly=MicroService::instance().ConfigGetString("firmware.updater.releaseonly","yes"); + R_res.rcOnly=MicroServiceConfigGetString("firmware.updater.releaseonly","yes"); if(R_res.rrm=="inherit") - R_res.rrm=MicroService::instance().ConfigGetString("rrm.default","no"); + R_res.rrm=MicroServiceConfigGetString("rrm.default","no"); return true; } diff --git a/src/TagServer.cpp b/src/TagServer.cpp index 1a90196..1bc5978 100644 --- a/src/TagServer.cpp +++ b/src/TagServer.cpp @@ -4,6 +4,7 @@ #include "TagServer.h" #include "StorageService.h" +#include "framework/utils.h" namespace OpenWifi { int TagServer::Start() { diff --git a/src/TagServer.h b/src/TagServer.h index 48ab170..48b1113 100644 --- a/src/TagServer.h +++ b/src/TagServer.h @@ -4,7 +4,7 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/SubSystemServer.h" namespace OpenWifi { diff --git a/src/Tasks/VenueConfigUpdater.h b/src/Tasks/VenueConfigUpdater.h index 45b4b56..f74314e 100644 --- a/src/Tasks/VenueConfigUpdater.h +++ b/src/Tasks/VenueConfigUpdater.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" #include "StorageService.h" #include "APConfig.h" #include "sdks/SDK_gw.h" #include "framework/WebSocketClientNotifications.h" #include "JobController.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { diff --git a/src/Tasks/VenueRebooter.h b/src/Tasks/VenueRebooter.h index 5d0d582..e8e517d 100644 --- a/src/Tasks/VenueRebooter.h +++ b/src/Tasks/VenueRebooter.h @@ -2,13 +2,13 @@ // Created by stephane bourque on 2022-05-04. // -#include "framework/MicroService.h" #include "framework/WebSocketClientNotifications.h" #include "StorageService.h" #include "APConfig.h" #include "sdks/SDK_gw.h" #include "JobController.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { diff --git a/src/Tasks/VenueUpgrade.h b/src/Tasks/VenueUpgrade.h index ed89a7f..34f0cbb 100644 --- a/src/Tasks/VenueUpgrade.h +++ b/src/Tasks/VenueUpgrade.h @@ -4,7 +4,6 @@ #pragma once -#include "framework/MicroService.h" #include "framework/WebSocketClientNotifications.h" #include "StorageService.h" @@ -12,6 +11,7 @@ #include "sdks/SDK_gw.h" #include "sdks/SDK_fms.h" #include "JobController.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { class VenueDeviceUpgrade : public Poco::Runnable { diff --git a/src/framework/ALBserver.cpp b/src/framework/ALBserver.cpp new file mode 100644 index 0000000..4b6af75 --- /dev/null +++ b/src/framework/ALBserver.cpp @@ -0,0 +1,76 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "ALBserver.h" + +#include "framework/utils.h" +#include "framework/MicroServiceFuncs.h" +#include "fmt/format.h" + +namespace OpenWifi { + + ALBRequestHandler::ALBRequestHandler(Poco::Logger & L, uint64_t id) + : Logger_(L), id_(id) + { + } + + void ALBRequestHandler::handleRequest([[maybe_unused]] Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) { + Utils::SetThreadName("alb-request"); + try { + if((id_ % 100) == 0) { + poco_debug(Logger_,fmt::format("ALB-REQUEST({}): ALB Request {}.", + Request.clientAddress().toString(), id_)); + } + Response.setChunkedTransferEncoding(true); + Response.setContentType("text/html"); + Response.setDate(Poco::Timestamp()); + Response.setStatus(Poco::Net::HTTPResponse::HTTP_OK); + Response.setKeepAlive(true); + Response.set("Connection", "keep-alive"); + Response.setVersion(Poco::Net::HTTPMessage::HTTP_1_1); + std::ostream &Answer = Response.send(); + Answer << "process Alive and kicking!"; + } catch (...) { + + } + } + + ALBRequestHandlerFactory::ALBRequestHandlerFactory(Poco::Logger & L): + Logger_(L) { + } + + ALBRequestHandler* ALBRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest& request) { + if (request.getURI() == "/") + return new ALBRequestHandler(Logger_, req_id_++); + else + return nullptr; + } + + ALBHealthCheckServer::ALBHealthCheckServer() : + SubSystemServer("ALBHealthCheckServer", "ALB-SVR", "alb") + { + } + + int ALBHealthCheckServer::Start() { + if(MicroServiceConfigGetBool("alb.enable",false)) { + Running_=true; + Port_ = (int)MicroServiceConfigGetInt("alb.port",15015); + Socket_ = std::make_unique(Port_); + auto Params = new Poco::Net::HTTPServerParams; + Params->setName("ws:alb"); + Server_ = std::make_unique(new ALBRequestHandlerFactory(Logger()), *Socket_, Params); + Server_->start(); + } + + return 0; + } + + void ALBHealthCheckServer::Stop() { + poco_information(Logger(),"Stopping..."); + if(Running_) + Server_->stopAll(true); + poco_information(Logger(),"Stopped..."); + } + +} // namespace OpenWifi \ No newline at end of file diff --git a/src/framework/ALBserver.h b/src/framework/ALBserver.h new file mode 100644 index 0000000..37cea16 --- /dev/null +++ b/src/framework/ALBserver.h @@ -0,0 +1,63 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "framework/SubSystemServer.h" + +#include "Poco/Net/HTTPRequestHandler.h" +#include "Poco/Net/HTTPServerRequest.h" +#include "Poco/Net/HTTPServerResponse.h" +#include "Poco/Net/HTTPRequestHandlerFactory.h" +#include "Poco/Net/HTTPServer.h" + +namespace OpenWifi { + + class ALBRequestHandler: public Poco::Net::HTTPRequestHandler + /// Return a HTML document with the current date and time. + { + public: + explicit ALBRequestHandler(Poco::Logger & L, uint64_t id); + void handleRequest([[maybe_unused]] Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) override; + + private: + Poco::Logger & Logger_; + uint64_t id_; + }; + + class ALBRequestHandlerFactory: public Poco::Net::HTTPRequestHandlerFactory + { + public: + explicit ALBRequestHandlerFactory(Poco::Logger & L); + ALBRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request) override; + + private: + Poco::Logger &Logger_; + inline static std::atomic_uint64_t req_id_=1; + }; + + class ALBHealthCheckServer : public SubSystemServer { + public: + ALBHealthCheckServer(); + + static auto instance() { + static auto instance = new ALBHealthCheckServer; + return instance; + } + + int Start() override; + + void Stop() override; + + private: + std::unique_ptr Server_; + std::unique_ptr Socket_; + int Port_ = 0; + mutable std::atomic_bool Running_=false; + }; + + inline auto ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); } + +} // namespace OpenWifi + diff --git a/src/framework/API_Proxy.h b/src/framework/API_Proxy.h index 82fd2e6..a6a402a 100644 --- a/src/framework/API_Proxy.h +++ b/src/framework/API_Proxy.h @@ -4,8 +4,14 @@ #pragma once -#include "framework/MicroService.h" +#include "Poco/Logger.h" #include "Poco/JSON/Parser.h" +#include "Poco/Net/HTTPServerRequest.h" +#include "Poco/Net/HTTPServerResponse.h" +#include "Poco/Net/HTTPSClientSession.h" +#include "Poco/URI.h" + +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { inline void API_Proxy( Poco::Logger &Logger, @@ -15,7 +21,7 @@ namespace OpenWifi { const char * PathRewrite, uint64_t msTimeout_ = 10000 ) { try { - auto Services = MicroService::instance().GetServices(ServiceType); + auto Services = MicroServiceGetServices(ServiceType); for(auto const &Svc:Services) { Poco::URI SourceURI(Request->getURI()); Poco::URI DestinationURI(Svc.PrivateEndPoint); @@ -35,7 +41,7 @@ namespace OpenWifi { ProxyRequest.add("Authorization", Request->get("Authorization")); } else { ProxyRequest.add("X-API-KEY", Svc.AccessKey); - ProxyRequest.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); + ProxyRequest.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); } if(Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) { diff --git a/src/framework/AppServiceRegistry.h b/src/framework/AppServiceRegistry.h new file mode 100644 index 0000000..0466df7 --- /dev/null +++ b/src/framework/AppServiceRegistry.h @@ -0,0 +1,102 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include +#include +#include +#include + +#include "Poco/StreamCopier.h" +#include "Poco/File.h" + +#include "framework/MicroServiceFuncs.h" + +#include "nlohmann/json.hpp" + +namespace OpenWifi { + + + class AppServiceRegistry { + public: + AppServiceRegistry() { + FileName = MicroServiceDataDirectory() + "/registry.json"; + Poco::File F(FileName); + + try { + if(F.exists()) { + std::ostringstream OS; + std::ifstream IF(FileName); + Poco::StreamCopier::copyStream(IF, OS); + Registry_ = nlohmann::json::parse(OS.str()); + } + } catch (...) { + Registry_ = nlohmann::json::parse("{}"); + } + } + + static AppServiceRegistry & instance() { + static auto instance_= new AppServiceRegistry; + return *instance_; + } + + inline ~AppServiceRegistry() { + Save(); + } + + inline void Save() { + std::istringstream IS( to_string(Registry_)); + std::ofstream OF; + OF.open(FileName,std::ios::binary | std::ios::trunc); + Poco::StreamCopier::copyStream(IS, OF); + } + + inline void Set(const char *Key, uint64_t Value ) { + Registry_[Key] = Value; + Save(); + } + + inline void Set(const char *Key, const std::string &Value ) { + Registry_[Key] = Value; + Save(); + } + + inline void Set(const char *Key, bool Value ) { + Registry_[Key] = Value; + Save(); + } + + inline bool Get(const char *Key, bool & Value ) { + if(Registry_[Key].is_boolean()) { + Value = Registry_[Key].get(); + return true; + } + return false; + } + + inline bool Get(const char *Key, uint64_t & Value ) { + if(Registry_[Key].is_number_unsigned()) { + Value = Registry_[Key].get(); + return true; + } + return false; + } + + inline bool Get(const char *Key, std::string & Value ) { + if(Registry_[Key].is_string()) { + Value = Registry_[Key].get(); + return true; + } + return false; + } + + private: + std::string FileName; + nlohmann::json Registry_; + }; + + inline auto AppServiceRegistry() { return AppServiceRegistry::instance(); } + +} \ No newline at end of file diff --git a/src/framework/AuthClient.cpp b/src/framework/AuthClient.cpp new file mode 100644 index 0000000..619358d --- /dev/null +++ b/src/framework/AuthClient.cpp @@ -0,0 +1,71 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "Poco/Net/HTTPServerResponse.h" + +#include "framework/AuthClient.h" +#include "framework/MicroServiceNames.h" +#include "framework/OpenAPIRequests.h" +#include "fmt/format.h" + +namespace OpenWifi { + + bool AuthClient::RetrieveTokenInformation(const std::string & SessionToken, + SecurityObjects::UserInfoAndPolicy & UInfo, + std::uint64_t TID, + bool & Expired, bool & Contacted, bool Sub) { + try { + Types::StringPairVec QueryData; + QueryData.push_back(std::make_pair("token",SessionToken)); + OpenAPIRequestGet Req( uSERVICE_SECURITY, + Sub ? "/api/v1/validateSubToken" : "/api/v1/validateToken", + QueryData, + 10000); + Poco::JSON::Object::Ptr Response; + + auto StatusCode = Req.Do(Response); + if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT) { + Contacted = false; + return false; + } + + Contacted = true; + if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_OK) { + if(Response->has("tokenInfo") && Response->has("userInfo")) { + UInfo.from_json(Response); + if(IsTokenExpired(UInfo.webtoken)) { + Expired = true; + return false; + } + Expired = false; + Cache_.update(SessionToken, UInfo); + return true; + } else { + return false; + } + } + } catch (...) { + poco_error(Logger(),fmt::format("Failed to retrieve token={} for TID={}", SessionToken, TID)); + } + Expired = false; + return false; + } + + bool AuthClient::IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, + std::uint64_t TID, + bool & Expired, bool & Contacted, bool Sub) { + auto User = Cache_.get(SessionToken); + if(!User.isNull()) { + if(IsTokenExpired(User->webtoken)) { + Expired = true; + return false; + } + Expired = false; + UInfo = *User; + return true; + } + return RetrieveTokenInformation(SessionToken, UInfo, TID, Expired, Contacted, Sub); + } + +} // namespace OpenWifi \ No newline at end of file diff --git a/src/framework/AuthClient.h b/src/framework/AuthClient.h new file mode 100644 index 0000000..cc1ab06 --- /dev/null +++ b/src/framework/AuthClient.h @@ -0,0 +1,59 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "framework/SubSystemServer.h" +#include "RESTObjects/RESTAPI_SecurityObjects.h" +#include "Poco/ExpireLRUCache.h" + +namespace OpenWifi { + + class AuthClient : public SubSystemServer { + public: + explicit AuthClient() noexcept: + SubSystemServer("Authentication", "AUTH-CLNT", "authentication") + { + } + + static auto instance() { + static auto instance_ = new AuthClient; + return instance_; + } + + inline int Start() override { + return 0; + } + + inline void Stop() override { + poco_information(Logger(),"Stopping..."); + std::lock_guard G(Mutex_); + Cache_.clear(); + poco_information(Logger(),"Stopped..."); + } + + inline void RemovedCachedToken(const std::string &Token) { + Cache_.remove(Token); + } + + inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) { + return ((T.expires_in_+T.created_) < OpenWifi::Now()); + } + + bool RetrieveTokenInformation(const std::string & SessionToken, + SecurityObjects::UserInfoAndPolicy & UInfo, + std::uint64_t TID, + bool & Expired, bool & Contacted, bool Sub=false); + bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, + std::uint64_t TID, + bool & Expired, bool & Contacted, bool Sub = false); + + private: + Poco::ExpireLRUCache Cache_{512,1200000 }; + }; + + inline auto AuthClient() { return AuthClient::instance(); } + +} // namespace OpenWifi + diff --git a/src/framework/CIDR.h b/src/framework/CIDR.h new file mode 100644 index 0000000..0d40f52 --- /dev/null +++ b/src/framework/CIDR.h @@ -0,0 +1,155 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include + +#include "Poco/Net/IPAddress.h" +#include "Poco/StringTokenizer.h" + +#include "framework/OpenWifiTypes.h" + +namespace OpenWifi::CIDR { + + static bool cidr_match(const in_addr &addr, const in_addr &net, uint8_t bits) { + if (bits == 0) { + return true; + } + return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits))); + } + + static bool cidr6_match(const in6_addr &address, const in6_addr &network, uint8_t bits) { + #ifdef __linux__ + const uint32_t *a = address.s6_addr32; + const uint32_t *n = network.s6_addr32; + #else + const uint32_t *a = address.__u6_addr.__u6_addr32; + const uint32_t *n = network.__u6_addr.__u6_addr32; + #endif + int bits_whole, bits_incomplete; + bits_whole = bits >> 5; // number of whole u32 + bits_incomplete = bits & 0x1F; // number of bits in incomplete u32 + if (bits_whole) { + if (memcmp(a, n, bits_whole << 2) != 0) { + return false; + } + } + if (bits_incomplete) { + uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete)); + if ((a[bits_whole] ^ n[bits_whole]) & mask) { + return false; + } + } + return true; + } + + static bool ConvertStringToLong(const char *S, unsigned long &L) { + char *end; + L = std::strtol(S, &end, 10); + return end != S; + } + + static bool CidrIPinRange(const Poco::Net::IPAddress &IP, const std::string &Range) { + Poco::StringTokenizer TimeTokens(Range, "/", Poco::StringTokenizer::TOK_TRIM); + + Poco::Net::IPAddress RangeIP; + if (Poco::Net::IPAddress::tryParse(TimeTokens[0], RangeIP)) { + if (TimeTokens.count() == 2) { + if (RangeIP.family() == Poco::Net::IPAddress::IPv4) { + unsigned long MaskLength; + if (ConvertStringToLong(TimeTokens[1].c_str(), MaskLength)) { + return cidr_match(*static_cast(RangeIP.addr()), + *static_cast(IP.addr()), MaskLength); + } + } else if (RangeIP.family() == Poco::Net::IPAddress::IPv6) { + unsigned long MaskLength; + if (ConvertStringToLong(TimeTokens[1].c_str(), MaskLength)) { + return cidr6_match(*static_cast(RangeIP.addr()), + *static_cast(IP.addr()), MaskLength); + } + } + } + return false; + } + return false; + } + + // + // Ranges can be a single IP, of IP1-IP2, of A set of IPs: IP1,IP2,IP3, or a cidr IP/24 + // These can work for IPv6 too... + // + static bool ValidateRange(const std::string &R) { + + auto Tokens = Poco::StringTokenizer(R, "-"); + if (Tokens.count() == 2) { + Poco::Net::IPAddress a, b; + if (!Poco::Net::IPAddress::tryParse(Tokens[0], a) && + Poco::Net::IPAddress::tryParse(Tokens[1], b)) + return false; + return a.family() == b.family(); + } + + Tokens = Poco::StringTokenizer(R, ","); + if (Tokens.count() > 1) { + return std::all_of(Tokens.begin(), Tokens.end(), [](const std::string &A) { + Poco::Net::IPAddress a; + return Poco::Net::IPAddress::tryParse(A, a); + }); + } + + Tokens = Poco::StringTokenizer(R, "/"); + if (Tokens.count() == 2) { + Poco::Net::IPAddress a; + if (!Poco::Net::IPAddress::tryParse(Tokens[0], a)) + return false; + if (std::atoi(Tokens[1].c_str()) == 0) + return false; + return true; + } + + Poco::Net::IPAddress a; + return Poco::Net::IPAddress::tryParse(R, a); + } + + static bool IpInRange(const Poco::Net::IPAddress &target, const std::string &R) { + + auto Tokens = Poco::StringTokenizer(R, "-"); + if (Tokens.count() == 2) { + auto a = Poco::Net::IPAddress::parse(Tokens[0]); + auto b = Poco::Net::IPAddress::parse(Tokens[1]); + if (target.family() != a.family()) + return false; + return (a <= target && b >= target); + } + + Tokens = Poco::StringTokenizer(R, ","); + if (Tokens.count() > 1) { + return std::any_of(Tokens.begin(), Tokens.end(), [target](const std::string &Element) { + return Poco::Net::IPAddress::parse(Element) == target; + }); + } + + Tokens = Poco::StringTokenizer(R, "/"); + if (Tokens.count() == 2) { + return CidrIPinRange(target, R); + } + + return Poco::Net::IPAddress::parse(R) == target; + } + + [[nodiscard]] inline bool IpInRanges(const std::string &IP, const Types::StringVec &R) { + Poco::Net::IPAddress Target; + + if (!Poco::Net::IPAddress::tryParse(IP, Target)) + return false; + + return std::any_of(cbegin(R), cend(R), + [Target](const std::string &i) { return IpInRange(Target, i); }); + } + + [[nodiscard]] inline bool ValidateIpRanges(const Types::StringVec &Ranges) { + return std::all_of(cbegin(Ranges), cend(Ranges), ValidateRange); + } +} \ No newline at end of file diff --git a/src/framework/ConfigurationValidator.cpp b/src/framework/ConfigurationValidator.cpp index 5170ef6..a2ce465 100644 --- a/src/framework/ConfigurationValidator.cpp +++ b/src/framework/ConfigurationValidator.cpp @@ -6,10 +6,15 @@ #include #include -#include "framework/MicroService.h" #include "ConfigurationValidator.h" #include "framework/CountryCodes.h" +#include "framework/MicroServiceFuncs.h" +#include "framework/utils.h" + #include "Poco/StringTokenizer.h" +#include "Poco/URI.h" + +#include "fmt/format.h" namespace OpenWifi { @@ -2625,7 +2630,7 @@ static json DefaultUCentralSchema = R"( return; std::string GitSchema; - if(MicroService::instance().ConfigGetBool("ucentral.datamodel.internal",true)) { + if(MicroServiceConfigGetBool("ucentral.datamodel.internal",true)) { RootSchema_ = DefaultUCentralSchema; Logger().information("Using uCentral validation from built-in default."); Initialized_ = Working_ = true; @@ -2633,12 +2638,12 @@ static json DefaultUCentralSchema = R"( } try { - auto GitURI = MicroService::instance().ConfigGetString("ucentral.datamodel.uri",GitUCentralJSONSchemaFile); + auto GitURI = MicroServiceConfigGetString("ucentral.datamodel.uri",GitUCentralJSONSchemaFile); if(Utils::wgets(GitURI, GitSchema)) { RootSchema_ = json::parse(GitSchema); Logger().information("Using uCentral validation schema from GIT."); } else { - std::string FileName{ MicroService::instance().DataDir() + "/ucentral.schema.json" }; + std::string FileName{ MicroServiceDataDirectory() + "/ucentral.schema.json" }; std::ifstream input(FileName); std::stringstream schema_file; schema_file << input.rdbuf(); diff --git a/src/framework/ConfigurationValidator.h b/src/framework/ConfigurationValidator.h index 6fe401e..0e1a75e 100644 --- a/src/framework/ConfigurationValidator.h +++ b/src/framework/ConfigurationValidator.h @@ -5,7 +5,7 @@ #pragma once #include -#include "framework/MicroService.h" +#include "framework/SubSystemServer.h" using nlohmann::json; using nlohmann::json_schema::json_validator; diff --git a/src/framework/EventBusManager.cpp b/src/framework/EventBusManager.cpp new file mode 100644 index 0000000..a32b4f6 --- /dev/null +++ b/src/framework/EventBusManager.cpp @@ -0,0 +1,48 @@ +// +// Created by stephane bourque on 2022-10-26. +// + +#include "framework/EventBusManager.h" +#include "framework/KafkaManager.h" +#include "framework/utils.h" +#include "framework/MicroServiceFuncs.h" + +namespace OpenWifi { + + EventBusManager::EventBusManager(Poco::Logger &L) : + Logger_(L) { + } + + void EventBusManager::run() { + Running_ = true; + Utils::SetThreadName("fmwk:EventMgr"); + auto Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN); + KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroServicePrivateEndPoint(),Msg, false); + while(Running_) { + Poco::Thread::trySleep((unsigned long)MicroServiceDaemonBusTimer()); + if(!Running_) + break; + Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE); + KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroServicePrivateEndPoint(),Msg, false); + } + Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_LEAVE); + KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroServicePrivateEndPoint(),Msg, false); + }; + + void EventBusManager::Start() { + if(KafkaManager()->Enabled()) { + Thread_.start(*this); + } + } + + void EventBusManager::Stop() { + if(KafkaManager()->Enabled()) { + poco_information(Logger(),"Stopping..."); + Running_ = false; + Thread_.wakeUp(); + Thread_.join(); + poco_information(Logger(),"Stopped..."); + } + } + +} // namespace OpenWifi \ No newline at end of file diff --git a/src/framework/EventBusManager.h b/src/framework/EventBusManager.h new file mode 100644 index 0000000..4b57d20 --- /dev/null +++ b/src/framework/EventBusManager.h @@ -0,0 +1,28 @@ +// +// Created by stephane bourque on 2022-10-26. +// + +#pragma once + +#include "Poco/Runnable.h" +#include "Poco/Logger.h" +#include "Poco/Thread.h" + +namespace OpenWifi { + + class EventBusManager : public Poco::Runnable { + public: + explicit EventBusManager(Poco::Logger &L); + void run() final; + void Start(); + void Stop(); + inline Poco::Logger & Logger() { return Logger_; } + + private: + mutable std::atomic_bool Running_ = false; + Poco::Thread Thread_; + Poco::Logger &Logger_; + }; + +} // namespace OpenWifi + diff --git a/src/framework/KafkaManager.cpp b/src/framework/KafkaManager.cpp new file mode 100644 index 0000000..a6c6b88 --- /dev/null +++ b/src/framework/KafkaManager.cpp @@ -0,0 +1,356 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "KafkaManager.h" + +#include "framework/MicroServiceFuncs.h" +#include "fmt/format.h" + +namespace OpenWifi { + + void KafkaLoggerFun([[maybe_unused]] cppkafka::KafkaHandleBase & handle, int level, const std::string & facility, const std::string &message) { + switch ((cppkafka::LogLevel) level) { + case cppkafka::LogLevel::LogNotice: { + poco_notice(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); + } + break; + case cppkafka::LogLevel::LogDebug: { + poco_debug(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); + } + break; + case cppkafka::LogLevel::LogInfo: { + poco_information(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); + } + break; + case cppkafka::LogLevel::LogWarning: { + poco_warning(KafkaManager()->Logger(), fmt::format("kafka-log: facility: {} message: {}",facility, message)); + } + break; + case cppkafka::LogLevel::LogAlert: + case cppkafka::LogLevel::LogCrit: { + poco_critical(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); + } + break; + case cppkafka::LogLevel::LogErr: + case cppkafka::LogLevel::LogEmerg: + default: { + poco_error(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); + } + break; + } + } + + inline void KafkaErrorFun([[maybe_unused]] cppkafka::KafkaHandleBase & handle, int error, const std::string &reason) { + poco_error(KafkaManager()->Logger(),fmt::format("kafka-error: {}, reason: {}", error, reason)); + } + + inline void AddKafkaSecurity(cppkafka::Configuration & Config) { + auto CA = MicroServiceConfigGetString("openwifi.kafka.ssl.ca.location",""); + auto Certificate = MicroServiceConfigGetString("openwifi.kafka.ssl.certificate.location",""); + auto Key = MicroServiceConfigGetString("openwifi.kafka.ssl.key.location",""); + auto Password = MicroServiceConfigGetString("openwifi.kafka.ssl.key.password",""); + + if(CA.empty() || Certificate.empty() || Key.empty()) + return; + + Config.set("ssl.ca.location", CA); + Config.set("ssl.certificate.location", Certificate); + Config.set("ssl.key.location", Key); + if(!Password.empty()) + Config.set("ssl.key.password", Password); + } + + + void KafkaManager::initialize(Poco::Util::Application & self) { + SubSystemServer::initialize(self); + KafkaEnabled_ = MicroServiceConfigGetBool("openwifi.kafka.enable",false); + } + + + + inline void KafkaProducer::run() { + + Utils::SetThreadName("Kafka:Prod"); + cppkafka::Configuration Config({ + { "client.id", MicroServiceConfigGetString("openwifi.kafka.client.id", "") }, + { "metadata.broker.list", MicroServiceConfigGetString("openwifi.kafka.brokerlist", "") } + }); + + AddKafkaSecurity(Config); + + Config.set_log_callback(KafkaLoggerFun); + Config.set_error_callback(KafkaErrorFun); + + KafkaManager()->SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" + + std::to_string(MicroServiceID()) + + R"lit( , "host" : ")lit" + MicroServicePrivateEndPoint() + + R"lit(" } , "payload" : )lit" ; + + cppkafka::Producer Producer(Config); + Running_ = true; + + Poco::AutoPtr Note(Queue_.waitDequeueNotification()); + while(Note && Running_) { + try { + auto Msg = dynamic_cast(Note.get()); + if (Msg != nullptr) { + Producer.produce( + cppkafka::MessageBuilder(Msg->Topic()).key(Msg->Key()).payload(Msg->Payload())); + } + } catch (const cppkafka::HandleException &E) { + poco_warning(KafkaManager()->Logger(),fmt::format("Caught a Kafka exception (producer): {}", E.what())); + } catch( const Poco::Exception &E) { + KafkaManager()->Logger().log(E); + } catch (...) { + poco_error(KafkaManager()->Logger(),"std::exception"); + } + Note = Queue_.waitDequeueNotification(); + } + } + + inline void KafkaConsumer::run() { + Utils::SetThreadName("Kafka:Cons"); + + cppkafka::Configuration Config({ + { "client.id", MicroServiceConfigGetString("openwifi.kafka.client.id","") }, + { "metadata.broker.list", MicroServiceConfigGetString("openwifi.kafka.brokerlist","") }, + { "group.id", MicroServiceConfigGetString("openwifi.kafka.group.id","") }, + { "enable.auto.commit", MicroServiceConfigGetBool("openwifi.kafka.auto.commit",false) }, + { "auto.offset.reset", "latest" } , + { "enable.partition.eof", false } + }); + + AddKafkaSecurity(Config); + + Config.set_log_callback(KafkaLoggerFun); + Config.set_error_callback(KafkaErrorFun); + + cppkafka::TopicConfiguration topic_config = { + { "auto.offset.reset", "smallest" } + }; + + // Now configure it to be the default topic config + Config.set_default_topic_configuration(topic_config); + + cppkafka::Consumer Consumer(Config); + Consumer.set_assignment_callback([](cppkafka::TopicPartitionList& partitions) { + if(!partitions.empty()) { + KafkaManager()->Logger().information(fmt::format("Partition assigned: {}...", + partitions.front().get_partition())); + } + }); + Consumer.set_revocation_callback([](const cppkafka::TopicPartitionList& partitions) { + if(!partitions.empty()) { + KafkaManager()->Logger().information(fmt::format("Partition revocation: {}...", + partitions.front().get_partition())); + } + }); + + bool AutoCommit = MicroServiceConfigGetBool("openwifi.kafka.auto.commit",false); + auto BatchSize = MicroServiceConfigGetInt("openwifi.kafka.consumer.batchsize",20); + + Types::StringVec Topics; + KafkaManager()->Topics(Topics); + Consumer.subscribe(Topics); + + Running_ = true; + while(Running_) { + try { + std::vector MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(100)); + for(auto const &Msg:MsgVec) { + if (!Msg) + continue; + if (Msg.get_error()) { + if (!Msg.is_eof()) { + poco_error(KafkaManager()->Logger(),fmt::format("Error: {}", Msg.get_error().to_string())); + } + if(!AutoCommit) + Consumer.async_commit(Msg); + continue; + } + KafkaManager()->Dispatch(Msg.get_topic(), Msg.get_key(),Msg.get_payload() ); + if (!AutoCommit) + Consumer.async_commit(Msg); + } + } catch (const cppkafka::HandleException &E) { + poco_warning(KafkaManager()->Logger(),fmt::format("Caught a Kafka exception (consumer): {}", E.what())); + } catch (const Poco::Exception &E) { + KafkaManager()->Logger().log(E); + } catch (...) { + poco_error(KafkaManager()->Logger(),"std::exception"); + } + } + Consumer.unsubscribe(); + } + + void KafkaProducer::Start() { + if(!Running_) { + Running_=true; + Worker_.start(*this); + } + } + + void KafkaProducer::Stop() { + if(Running_) { + Running_=false; + Queue_.wakeUpAll(); + Worker_.join(); + } + } + + void KafkaProducer::Produce(const std::string &Topic, const std::string &Key, const std::string &Payload) { + std::lock_guard G(Mutex_); + Queue_.enqueueNotification( new KafkaMessage(Topic,Key,Payload)); + } + + void KafkaConsumer::Start() { + if(!Running_) { + Running_=true; + Worker_.start(*this); + } + } + + void KafkaConsumer::Stop() { + if(Running_) { + Running_=false; + Worker_.wakeUp(); + Worker_.join(); + } + } + + void KafkaDispatcher::Start() { + if(!Running_) { + Running_=true; + Worker_.start(*this); + } + } + + void KafkaDispatcher::Stop() { + if(Running_) { + Running_=false; + Queue_.wakeUpAll(); + Worker_.join(); + } + } + + auto KafkaDispatcher::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { + std::lock_guard G(Mutex_); + auto It = Notifiers_.find(Topic); + if(It == Notifiers_.end()) { + Types::TopicNotifyFunctionList L; + L.emplace(L.end(),std::make_pair(F,FunctionId_)); + Notifiers_[Topic] = std::move(L); + } else { + It->second.emplace(It->second.end(),std::make_pair(F,FunctionId_)); + } + return FunctionId_++; + } + + void KafkaDispatcher::UnregisterTopicWatcher(const std::string &Topic, int Id) { + std::lock_guard G(Mutex_); + auto It = Notifiers_.find(Topic); + if(It != Notifiers_.end()) { + Types::TopicNotifyFunctionList & L = It->second; + for(auto it=L.begin(); it!=L.end(); it++) + if(it->second == Id) { + L.erase(it); + break; + } + } + } + + void KafkaDispatcher::Dispatch(const std::string &Topic, const std::string &Key, const std::string &Payload) { + std::lock_guard G(Mutex_); + auto It = Notifiers_.find(Topic); + if(It!=Notifiers_.end()) { + Queue_.enqueueNotification(new KafkaMessage(Topic, Key, Payload)); + } + } + + void KafkaDispatcher::run() { + Poco::AutoPtr Note(Queue_.waitDequeueNotification()); + Utils::SetThreadName("kafka:dispatch"); + while(Note && Running_) { + auto Msg = dynamic_cast(Note.get()); + if(Msg!= nullptr) { + auto It = Notifiers_.find(Msg->Topic()); + if (It != Notifiers_.end()) { + const auto & FL = It->second; + for(const auto &[CallbackFunc,_]:FL) { + CallbackFunc(Msg->Key(), Msg->Payload()); + } + } + } + Note = Queue_.waitDequeueNotification(); + } + } + + void KafkaDispatcher::Topics(std::vector &T) { + T.clear(); + for(const auto &[TopicName,_]:Notifiers_) + T.push_back(TopicName); + } + + + int KafkaManager::Start() { + if(!KafkaEnabled_) + return 0; + ConsumerThr_.Start(); + ProducerThr_.Start(); + Dispatcher_.Start(); + return 0; + } + + void KafkaManager::Stop() { + if(KafkaEnabled_) { + poco_information(Logger(),"Stopping..."); + Dispatcher_.Stop(); + ProducerThr_.Stop(); + ConsumerThr_.Stop(); + poco_information(Logger(),"Stopped..."); + return; + } + } + + void KafkaManager::PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage ) { + if(KafkaEnabled_) { + ProducerThr_.Produce(topic,key,WrapMessage ? WrapSystemId(PayLoad) : PayLoad); + } + } + + void KafkaManager::Dispatch(const std::string &Topic, const std::string & Key, const std::string &Payload) { + Dispatcher_.Dispatch(Topic, Key, Payload); + } + + [[nodiscard]] std::string KafkaManager::WrapSystemId(const std::string & PayLoad) { + return SystemInfoWrapper_ + PayLoad + "}"; + } + + uint64_t KafkaManager::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { + if(KafkaEnabled_) { + return Dispatcher_.RegisterTopicWatcher(Topic,F); + } else { + return 0; + } + } + + void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, uint64_t Id) { + if(KafkaEnabled_) { + Dispatcher_.UnregisterTopicWatcher(Topic, Id); + } + } + + void KafkaManager::Topics(std::vector &T) { + Dispatcher_.Topics(T); + } + + void KafkaManager::PartitionAssignment(const cppkafka::TopicPartitionList& partitions) { + Logger().information(fmt::format("Partition assigned: {}...", partitions.front().get_partition())); + } + + void KafkaManager::PartitionRevocation(const cppkafka::TopicPartitionList& partitions) { + Logger().information(fmt::format("Partition revocation: {}...",partitions.front().get_partition())); + } + +} // namespace OpenWifi \ No newline at end of file diff --git a/src/framework/KafkaManager.h b/src/framework/KafkaManager.h new file mode 100644 index 0000000..ebe75e2 --- /dev/null +++ b/src/framework/KafkaManager.h @@ -0,0 +1,124 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "Poco/Notification.h" +#include "Poco/NotificationQueue.h" + +#include "framework/SubSystemServer.h" +#include "framework/OpenWifiTypes.h" +#include "framework/utils.h" +#include "framework/KafkaTopics.h" + +#include "cppkafka/cppkafka.h" + +namespace OpenWifi { + + class KafkaMessage: public Poco::Notification { + public: + KafkaMessage( const std::string &Topic, const std::string &Key, const std::string & Payload) : + Topic_(Topic), Key_(Key), Payload_(Payload) { + } + + inline const std::string & Topic() { return Topic_; } + inline const std::string & Key() { return Key_; } + inline const std::string & Payload() { return Payload_; } + + private: + std::string Topic_; + std::string Key_; + std::string Payload_; + }; + + class KafkaProducer : public Poco::Runnable { + public: + + void run () override; + void Start(); + void Stop(); + void Produce(const std::string &Topic, const std::string &Key, const std::string &Payload); + + private: + std::recursive_mutex Mutex_; + Poco::Thread Worker_; + mutable std::atomic_bool Running_=false; + Poco::NotificationQueue Queue_; + }; + + class KafkaConsumer : public Poco::Runnable { + public: + void run() override; + void Start(); + void Stop(); + + private: + std::recursive_mutex Mutex_; + Poco::Thread Worker_; + mutable std::atomic_bool Running_=false; + }; + + class KafkaDispatcher : public Poco::Runnable { + public: + + void Start(); + void Stop(); + auto RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F); + void UnregisterTopicWatcher(const std::string &Topic, int Id); + void Dispatch(const std::string &Topic, const std::string &Key, const std::string &Payload); + void run() override; + void Topics(std::vector &T); + + private: + std::recursive_mutex Mutex_; + Types::NotifyTable Notifiers_; + Poco::Thread Worker_; + mutable std::atomic_bool Running_=false; + uint64_t FunctionId_=1; + Poco::NotificationQueue Queue_; + }; + + class KafkaManager : public SubSystemServer { + public: + + friend class KafkaConsumer; + friend class KafkaProducer; + + inline void initialize(Poco::Util::Application & self) override; + + static auto instance() { + static auto instance_ = new KafkaManager; + return instance_; + } + + int Start() override; + void Stop() override; + + void PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage = true ); + void Dispatch(const std::string &Topic, const std::string & Key, const std::string &Payload); + [[nodiscard]] std::string WrapSystemId(const std::string & PayLoad); + [[nodiscard]] inline bool Enabled() const { return KafkaEnabled_; } + uint64_t RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F); + void UnregisterTopicWatcher(const std::string &Topic, uint64_t Id); + void Topics(std::vector &T); + + private: + bool KafkaEnabled_ = false; + std::string SystemInfoWrapper_; + KafkaProducer ProducerThr_; + KafkaConsumer ConsumerThr_; + KafkaDispatcher Dispatcher_; + + void PartitionAssignment(const cppkafka::TopicPartitionList& partitions); + void PartitionRevocation(const cppkafka::TopicPartitionList& partitions); + + KafkaManager() noexcept: + SubSystemServer("KafkaManager", "KAFKA-SVR", "openwifi.kafka") { + } + }; + + inline auto KafkaManager() { return KafkaManager::instance(); } + +} // namespace OpenWifi + diff --git a/src/framework/KafkaTopics.h b/src/framework/KafkaTopics.h index 0fee341..c2b7392 100644 --- a/src/framework/KafkaTopics.h +++ b/src/framework/KafkaTopics.h @@ -8,6 +8,7 @@ #pragma once +#include namespace OpenWifi::KafkaTopics { static const std::string HEALTHCHECK{"healthcheck"}; static const std::string STATE{"state"}; diff --git a/src/framework/MicroService.cpp b/src/framework/MicroService.cpp new file mode 100644 index 0000000..288b143 --- /dev/null +++ b/src/framework/MicroService.cpp @@ -0,0 +1,635 @@ +// +// Created by stephane bourque on 2022-10-26. +// + +#include "Poco/FileChannel.h" +#include "Poco/ConsoleChannel.h" +#include "Poco/PatternFormatter.h" +#include "Poco/FormattingChannel.h" +#include "Poco/AsyncChannel.h" +#include "Poco/NullChannel.h" +#include "Poco/Net/HTTPStreamFactory.h" +#include "Poco/Net/HTTPSStreamFactory.h" +#include "Poco/Net/FTPSStreamFactory.h" +#include "Poco/Net/FTPStreamFactory.h" +#include "Poco/Net/SSLManager.h" +#include "Poco/JSON/JSONException.h" + +#include "framework/MicroService.h" +#include "framework/MicroServiceErrorHandler.h" +#include "framework/UI_WebSocketClientServer.h" +#include "framework/MicroServiceNames.h" +#include "framework/AuthClient.h" +#include "framework/ALBserver.h" +#include "framework/KafkaManager.h" +#include "framework/RESTAPI_RateLimiter.h" +#include "framework/WebSocketLogger.h" +#include "framework/RESTAPI_GenericServerAccounting.h" +#include "framework/RESTAPI_Handler.h" +#include "framework/RESTAPI_ExtServer.h" +#include "framework/RESTAPI_IntServer.h" + + +namespace OpenWifi { + + void MicroService::Exit(int Reason) { + std::exit(Reason); + } + + void MicroService::BusMessageReceived([[maybe_unused]] const std::string &Key, const std::string & Payload) { + std::lock_guard G(InfraMutex_); + try { + Poco::JSON::Parser P; + auto Object = P.parse(Payload).extract(); + + if (Object->has(KafkaTopics::ServiceEvents::Fields::ID) && + Object->has(KafkaTopics::ServiceEvents::Fields::EVENT)) { + uint64_t ID = Object->get(KafkaTopics::ServiceEvents::Fields::ID); + auto Event = Object->get(KafkaTopics::ServiceEvents::Fields::EVENT).toString(); + if (ID != ID_) { + if( Event==KafkaTopics::ServiceEvents::EVENT_JOIN || + Event==KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE || + Event==KafkaTopics::ServiceEvents::EVENT_LEAVE ) { + if( Object->has(KafkaTopics::ServiceEvents::Fields::TYPE) && + Object->has(KafkaTopics::ServiceEvents::Fields::PUBLIC) && + Object->has(KafkaTopics::ServiceEvents::Fields::PRIVATE) && + Object->has(KafkaTopics::ServiceEvents::Fields::VRSN) && + Object->has(KafkaTopics::ServiceEvents::Fields::KEY)) { + auto PrivateEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(); + if (Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE && Services_.find(PrivateEndPoint) != Services_.end()) { + Services_[PrivateEndPoint].LastUpdate = OpenWifi::Now(); + } else if (Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) { + Services_.erase(PrivateEndPoint); + poco_debug(logger(),fmt::format("Service {} ID={} leaving system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); + } else if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN || Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE) { + poco_debug(logger(),fmt::format("Service {} ID={} joining system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); + Services_[PrivateEndPoint] = Types::MicroServiceMeta{ + .Id = ID, + .Type = Poco::toLower(Object->get(KafkaTopics::ServiceEvents::Fields::TYPE).toString()), + .PrivateEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(), + .PublicEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PUBLIC).toString(), + .AccessKey = Object->get(KafkaTopics::ServiceEvents::Fields::KEY).toString(), + .Version = Object->get(KafkaTopics::ServiceEvents::Fields::VRSN).toString(), + .LastUpdate = OpenWifi::Now() }; + + std::string SvcList; + for (const auto &Svc: Services_) { + if(SvcList.empty()) + SvcList = Svc.second.Type; + else + SvcList += ", " + Svc.second.Type; + } + logger().information(fmt::format("Current list of microservices: {}", SvcList)); + } + } else { + poco_error(logger(),fmt::format("KAFKA-MSG: invalid event '{}', missing a field.",Event)); + } + } else if (Event==KafkaTopics::ServiceEvents::EVENT_REMOVE_TOKEN) { + if(Object->has(KafkaTopics::ServiceEvents::Fields::TOKEN)) { + #ifndef TIP_SECURITY_SERVICE + AuthClient()->RemovedCachedToken(Object->get(KafkaTopics::ServiceEvents::Fields::TOKEN).toString()); + #endif + } else { + poco_error(logger(),fmt::format("KAFKA-MSG: invalid event '{}', missing token",Event)); + } + } else { + poco_error(logger(),fmt::format("Unknown Event: {} Source: {}", Event, ID)); + } + } + } else { + poco_error(logger(),"Bad bus message."); + } + + auto i=Services_.begin(); + auto now = OpenWifi::Now(); + for(;i!=Services_.end();) { + if((now - i->second.LastUpdate)>60) { + i = Services_.erase(i); + } else + ++i; + } + + } catch (const Poco::Exception &E) { + logger().log(E); + } + } + + Types::MicroServiceMetaVec MicroService::GetServices(const std::string & Type) { + std::lock_guard G(InfraMutex_); + + auto T = Poco::toLower(Type); + Types::MicroServiceMetaVec Res; + for(const auto &[_,ServiceRec]:Services_) { + if(ServiceRec.Type==T) + Res.push_back(ServiceRec); + } + return Res; + } + + Types::MicroServiceMetaVec MicroService::GetServices() { + std::lock_guard G(InfraMutex_); + + Types::MicroServiceMetaVec Res; + for(const auto &[_,ServiceRec]:Services_) { + Res.push_back(ServiceRec); + } + return Res; + } + + void MicroService::LoadConfigurationFile() { + std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR,"."); + ConfigFileName_ = ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : ConfigFileName_; + Poco::Path ConfigFile(ConfigFileName_); + + if(!ConfigFile.isFile()) + { + std::cerr << DAEMON_APP_NAME << ": Configuration " + << ConfigFile.toString() << " does not seem to exist. Please set " + DAEMON_CONFIG_ENV_VAR + + " env variable the path of the " + DAEMON_PROPERTIES_FILENAME + " file." << std::endl; + std::exit(Poco::Util::Application::EXIT_CONFIG); + } + + // loadConfiguration(ConfigFile.toString()); + PropConfigurationFile_ = new Poco::Util::PropertyFileConfiguration(ConfigFile.toString()); + configPtr()->addWriteable(PropConfigurationFile_, PRIO_DEFAULT); + } + + void MicroService::Reload() { + LoadConfigurationFile(); + LoadMyConfig(); + } + + void MicroService::LoadMyConfig() { + NoAPISecurity_ = ConfigGetBool("openwifi.security.restapi.disable",false); + std::string KeyFile = ConfigPath("openwifi.service.key",""); + if(!KeyFile.empty()) { + std::string KeyFilePassword = ConfigPath("openwifi.service.key.password", ""); + AppKey_ = Poco::SharedPtr(new Poco::Crypto::RSAKey("", KeyFile, KeyFilePassword)); + Cipher_ = CipherFactory_.createCipher(*AppKey_); + Signer_.setRSAKey(AppKey_); + Signer_.addAllAlgorithms(); + NoBuiltInCrypto_ = false; + } else { + NoBuiltInCrypto_ = true; + } + + ID_ = Utils::GetSystemId(); + if(!DebugMode_) + DebugMode_ = ConfigGetBool("openwifi.system.debug",false); + MyPrivateEndPoint_ = ConfigGetString("openwifi.system.uri.private"); + MyPublicEndPoint_ = ConfigGetString("openwifi.system.uri.public"); + UIURI_ = ConfigGetString("openwifi.system.uri.ui"); + MyHash_ = Utils::ComputeHash(MyPublicEndPoint_); + } + + void MicroServicePostInitialization(); + + void MicroService::InitializeLoggingSystem() { + static auto initialized = false; + + if(!initialized) { + initialized = true; + LoadConfigurationFile(); + + auto LoggingDestination = MicroService::instance().ConfigGetString("logging.type", "file"); + auto LoggingFormat = MicroService::instance().ConfigGetString("logging.format", + "%Y-%m-%d %H:%M:%S.%i %s: [%p][thr:%I] %t"); + auto UseAsyncLogs_ = MicroService::instance().ConfigGetBool("logging.asynch",false); + if (LoggingDestination == "null") { + Poco::AutoPtr DevNull(new Poco::NullChannel); + Poco::Logger::root().setChannel(DevNull); + } else if (LoggingDestination == "console") { + Poco::AutoPtr Console(new Poco::ConsoleChannel); + if(UseAsyncLogs_) { + Poco::AutoPtr Async(new Poco::AsyncChannel(Console)); + Poco::AutoPtr Formatter(new Poco::PatternFormatter); + Formatter->setProperty("pattern", LoggingFormat); + Poco::AutoPtr FormattingChannel( + new Poco::FormattingChannel(Formatter, Async)); + Poco::Logger::root().setChannel(FormattingChannel); + } else { + Poco::AutoPtr Formatter(new Poco::PatternFormatter); + Formatter->setProperty("pattern", LoggingFormat); + Poco::AutoPtr FormattingChannel( + new Poco::FormattingChannel(Formatter, Console)); + Poco::Logger::root().setChannel(FormattingChannel); + } + } else if (LoggingDestination == "colorconsole") { + Poco::AutoPtr ColorConsole(new Poco::ColorConsoleChannel); + if(UseAsyncLogs_) { + Poco::AutoPtr Async(new Poco::AsyncChannel(ColorConsole)); + Poco::AutoPtr Formatter(new Poco::PatternFormatter); + Formatter->setProperty("pattern", LoggingFormat); + Poco::AutoPtr FormattingChannel( + new Poco::FormattingChannel(Formatter, Async)); + Poco::Logger::root().setChannel(FormattingChannel); + } else { + Poco::AutoPtr Formatter(new Poco::PatternFormatter); + Formatter->setProperty("pattern", LoggingFormat); + Poco::AutoPtr FormattingChannel( + new Poco::FormattingChannel(Formatter, ColorConsole)); + Poco::Logger::root().setChannel(FormattingChannel); + } + } else if (LoggingDestination == "sql") { + //"CREATE TABLE T_POCO_LOG (Source VARCHAR, Name VARCHAR, ProcessId INTEGER, Thread VARCHAR, ThreadId INTEGER, Priority INTEGER, Text VARCHAR, DateTime DATE)" + + } else if (LoggingDestination == "syslog") { + + } else { + auto LoggingLocation = + MicroService::instance().ConfigPath("logging.path", "$OWCERT_ROOT/logs") + "/log"; + + Poco::AutoPtr FileChannel(new Poco::FileChannel); + FileChannel->setProperty("rotation", "10 M"); + FileChannel->setProperty("archive", "timestamp"); + FileChannel->setProperty("path", LoggingLocation); + if(UseAsyncLogs_) { + Poco::AutoPtr Async_File( + new Poco::AsyncChannel(FileChannel)); + Poco::AutoPtr Formatter(new Poco::PatternFormatter); + Formatter->setProperty("pattern", LoggingFormat); + Poco::AutoPtr FormattingChannel( + new Poco::FormattingChannel(Formatter, Async_File)); + Poco::Logger::root().setChannel(FormattingChannel); + } else { + Poco::AutoPtr Formatter(new Poco::PatternFormatter); + Formatter->setProperty("pattern", LoggingFormat); + Poco::AutoPtr FormattingChannel( + new Poco::FormattingChannel(Formatter, FileChannel)); + Poco::Logger::root().setChannel(FormattingChannel); + } + } + auto Level = Poco::Logger::parseLevel(MicroService::instance().ConfigGetString("logging.level", "debug")); + Poco::Logger::root().setLevel(Level); + } + } + + void DaemonPostInitialization(Poco::Util::Application &self); + + void MicroService::initialize(Poco::Util::Application &self) { + // add the default services + LoadConfigurationFile(); + InitializeLoggingSystem(); + + SubSystems_.push_back(KafkaManager()); + SubSystems_.push_back(ALBHealthCheckServer()); + SubSystems_.push_back(RESTAPI_ExtServer()); + SubSystems_.push_back(RESTAPI_IntServer()); + #ifndef TIP_SECURITY_SERVICE + SubSystems_.push_back(AuthClient()); + #endif + Poco::Net::initializeSSL(); + Poco::Net::HTTPStreamFactory::registerFactory(); + Poco::Net::HTTPSStreamFactory::registerFactory(); + Poco::Net::FTPStreamFactory::registerFactory(); + Poco::Net::FTPSStreamFactory::registerFactory(); + + Poco::File DataDir(ConfigPath("openwifi.system.data")); + DataDir_ = DataDir.path(); + if(!DataDir.exists()) { + try { + DataDir.createDirectory(); + } catch (const Poco::Exception &E) { + logger().log(E); + } + } + WWWAssetsDir_ = ConfigPath("openwifi.restapi.wwwassets",""); + if(WWWAssetsDir_.empty()) + WWWAssetsDir_ = DataDir_; + + LoadMyConfig(); + + InitializeSubSystemServers(); + ServerApplication::initialize(self); + DaemonPostInitialization(self); + + Types::TopicNotifyFunction F = [this](const std::string &Key,const std::string &Payload) { this->BusMessageReceived(Key, Payload); }; + KafkaManager()->RegisterTopicWatcher(KafkaTopics::SERVICE_EVENTS, F); + } + + void MicroService::uninitialize() { + // add your own uninitialization code here + ServerApplication::uninitialize(); + } + + void MicroService::reinitialize(Poco::Util::Application &self) { + ServerApplication::reinitialize(self); + // add your own reinitialization code here + } + + void MicroService::defineOptions(Poco::Util::OptionSet &options) { + ServerApplication::defineOptions(options); + + options.addOption( + Poco::Util::Option("help", "", "display help information on command line arguments") + .required(false) + .repeatable(false) + .callback(Poco::Util::OptionCallback(this, &MicroService::handleHelp))); + + options.addOption( + Poco::Util::Option("file", "", "specify the configuration file") + .required(false) + .repeatable(false) + .argument("file") + .callback(Poco::Util::OptionCallback(this, &MicroService::handleConfig))); + + options.addOption( + Poco::Util::Option("debug", "", "to run in debug, set to true") + .required(false) + .repeatable(false) + .callback(Poco::Util::OptionCallback(this, &MicroService::handleDebug))); + + options.addOption( + Poco::Util::Option("logs", "", "specify the log directory and file (i.e. dir/file.log)") + .required(false) + .repeatable(false) + .argument("dir") + .callback(Poco::Util::OptionCallback(this, &MicroService::handleLogs))); + + options.addOption( + Poco::Util::Option("version", "", "get the version and quit.") + .required(false) + .repeatable(false) + .callback(Poco::Util::OptionCallback(this, &MicroService::handleVersion))); + + } + + void MicroService::handleHelp([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) { + HelpRequested_ = true; + displayHelp(); + stopOptionsProcessing(); + } + + void MicroService::handleVersion([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) { + HelpRequested_ = true; + std::cout << Version() << std::endl; + stopOptionsProcessing(); + } + + void MicroService::handleDebug([[maybe_unused]] const std::string &name, const std::string &value) { + if(value == "true") + DebugMode_ = true ; + } + + void MicroService::handleLogs([[maybe_unused]] const std::string &name, const std::string &value) { + LogDir_ = value; + } + + void MicroService::handleConfig([[maybe_unused]] const std::string &name, const std::string &value) { + ConfigFileName_ = value; + } + + void MicroService::displayHelp() { + Poco::Util::HelpFormatter helpFormatter(options()); + helpFormatter.setCommand(commandName()); + helpFormatter.setUsage("OPTIONS"); + helpFormatter.setHeader("A " + DAEMON_APP_NAME + " implementation for TIP."); + helpFormatter.format(std::cout); + } + + void MicroService::InitializeSubSystemServers() { + for(auto i:SubSystems_) { + addSubsystem(i); + } + } + + void MicroService::StartSubSystemServers() { + AddActivity("Starting"); + for(auto i:SubSystems_) { + i->Start(); + } + EventBusManager_ = std::make_unique(Poco::Logger::create("EventBusManager",Poco::Logger::root().getChannel(),Poco::Logger::root().getLevel())); + EventBusManager_->Start(); + } + + void MicroService::StopSubSystemServers() { + AddActivity("Stopping"); + EventBusManager_->Stop(); + for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) { + (*i)->Stop(); + } + } + + [[nodiscard]] std::string MicroService::CreateUUID() { + static std::random_device rd; + static std::mt19937_64 gen(rd()); + static std::uniform_int_distribution<> dis(0, 15); + static std::uniform_int_distribution<> dis2(8, 11); + + std::stringstream ss; + int i; + ss << std::hex; + for (i = 0; i < 8; i++) { + ss << dis(gen); + } + ss << "-"; + for (i = 0; i < 4; i++) { + ss << dis(gen); + } + ss << "-4"; + for (i = 0; i < 3; i++) { + ss << dis(gen); + } + ss << "-"; + ss << dis2(gen); + for (i = 0; i < 3; i++) { + ss << dis(gen); + } + ss << "-"; + for (i = 0; i < 12; i++) { + ss << dis(gen); + }; + return ss.str(); + } + + bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) { + try { + auto P = Poco::Logger::parseLevel(Level); + auto Sub = Poco::toLower(SubSystem); + + if (Sub == "all") { + for (auto i : SubSystems_) { + i->Logger().setLevel(P); + } + return true; + } else { + for (auto i : SubSystems_) { + if (Sub == Poco::toLower(i->Name())) { + i->Logger().setLevel(P); + return true; + } + } + } + } catch (const Poco::Exception & E) { + std::cerr << "Exception" << std::endl; + } + return false; + } + + void MicroService::Reload(const std::string &Sub) { + for (auto i : SubSystems_) { + if (Poco::toLower(Sub) == Poco::toLower(i->Name())) { + i->reinitialize(Poco::Util::Application::instance()); + return; + } + } + } + + Types::StringVec MicroService::GetSubSystems() const { + Types::StringVec Result; + for(auto i:SubSystems_) + Result.push_back(Poco::toLower(i->Name())); + return Result; + } + + Types::StringPairVec MicroService::GetLogLevels() { + Types::StringPairVec Result; + + for(auto &i:SubSystems_) { + auto P = std::make_pair( i->Name(), Utils::LogLevelToString(i->GetLoggingLevel())); + Result.push_back(P); + } + return Result; + } + + const Types::StringVec & MicroService::GetLogLevelNames() { + static Types::StringVec LevelNames{"none", "fatal", "critical", "error", "warning", "notice", "information", "debug", "trace" }; + return LevelNames; + } + + uint64_t MicroService::ConfigGetInt(const std::string &Key,uint64_t Default) { + return (uint64_t) config().getInt64(Key,Default); + } + + uint64_t MicroService::ConfigGetInt(const std::string &Key) { + return config().getInt(Key); + } + + uint64_t MicroService::ConfigGetBool(const std::string &Key,bool Default) { + return config().getBool(Key,Default); + } + + uint64_t MicroService::ConfigGetBool(const std::string &Key) { + return config().getBool(Key); + } + + std::string MicroService::ConfigGetString(const std::string &Key,const std::string & Default) { + return config().getString(Key, Default); + } + + std::string MicroService::ConfigGetString(const std::string &Key) { + return config().getString(Key); + } + + std::string MicroService::ConfigPath(const std::string &Key,const std::string & Default) { + std::string R = config().getString(Key, Default); + return Poco::Path::expand(R); + } + + std::string MicroService::ConfigPath(const std::string &Key) { + std::string R = config().getString(Key); + return Poco::Path::expand(R); + } + + std::string MicroService::Encrypt(const std::string &S) { + if(NoBuiltInCrypto_) { + return S; + } + return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; + } + + std::string MicroService::Decrypt(const std::string &S) { + if(NoBuiltInCrypto_) { + return S; + } + return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; + } + + std::string MicroService::MakeSystemEventMessage( const std::string & Type ) const { + Poco::JSON::Object Obj; + Obj.set(KafkaTopics::ServiceEvents::Fields::EVENT,Type); + Obj.set(KafkaTopics::ServiceEvents::Fields::ID,ID_); + Obj.set(KafkaTopics::ServiceEvents::Fields::TYPE,Poco::toLower(DAEMON_APP_NAME)); + Obj.set(KafkaTopics::ServiceEvents::Fields::PUBLIC,MyPublicEndPoint_); + Obj.set(KafkaTopics::ServiceEvents::Fields::PRIVATE,MyPrivateEndPoint_); + Obj.set(KafkaTopics::ServiceEvents::Fields::KEY,MyHash_); + Obj.set(KafkaTopics::ServiceEvents::Fields::VRSN,Version_); + std::stringstream ResultText; + Poco::JSON::Stringifier::stringify(Obj, ResultText); + return ResultText.str(); + } + + [[nodiscard]] bool MicroService::IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request) { + try { + auto APIKEY = Request.get("X-API-KEY"); + return APIKEY == MyHash_; + } catch (const Poco::Exception &E) { + logger().log(E); + } + return false; + } + + void MicroService::SavePID() { + try { + std::ofstream O; + O.open(MicroService::instance().DataDir() + "/pidfile",std::ios::binary | std::ios::trunc); + O << Poco::Process::id(); + O.close(); + } catch (...) + { + std::cout << "Could not save system ID" << std::endl; + } + } + + int MicroService::main([[maybe_unused]] const ArgVec &args) { + MicroServiceErrorHandler ErrorHandler(*this); + Poco::ErrorHandler::set(&ErrorHandler); + + if (!HelpRequested_) { + SavePID(); + + Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME); + logger.notice(fmt::format("Starting {} version {}.",DAEMON_APP_NAME, Version())); + + if(Poco::Net::Socket::supportsIPv6()) + logger.information("System supports IPv6."); + else + logger.information("System does NOT support IPv6."); + + if (config().getBool("application.runAsDaemon", false)) { + logger.information("Starting as a daemon."); + } + + logger.information(fmt::format("System ID set to {}",ID_)); + StartSubSystemServers(); + waitForTerminationRequest(); + StopSubSystemServers(); + logger.notice(fmt::format("Stopped {}...",DAEMON_APP_NAME)); + } + + return Application::EXIT_OK; + } + + void MicroService::AddActivity(const std::string &Activity) { + if(!DataDir_.empty()) { + std::string ActivityFile{ DataDir_ + "/activity.log"}; + try { + std::ofstream of(ActivityFile,std::ios_base::app | std::ios_base::out ); + auto t = std::chrono::system_clock::now(); + std::time_t now = std::chrono::system_clock::to_time_t(t); + of << Activity << " at " << std::ctime(&now) ; + } catch (...) { + + } + } + } + + [[nodiscard]] std::string MicroService::Sign(Poco::JWT::Token &T, const std::string &Algo) { + if(NoBuiltInCrypto_) { + return T.toString(); + } else { + return Signer_.sign(T,Algo); + } + } + +} \ No newline at end of file diff --git a/src/framework/MicroService.h b/src/framework/MicroService.h index 1bba1e6..a50d97c 100644 --- a/src/framework/MicroService.h +++ b/src/framework/MicroService.h @@ -8,20 +8,12 @@ #pragma once - #include #include -#include #include -#include -#include -#include #include -#include #include -#include -#include -#include +#include // This must be defined for poco_debug and poco_trace macros to function. @@ -34,3220 +26,41 @@ namespace OpenWifi { inline uint64_t Now() { return std::time(nullptr); }; } -namespace OpenWifi::Utils { - std::vector base64decode(const std::string& input); - std::string base64encode(const unsigned char *input, uint32_t size); -} - -using namespace std::chrono_literals; - #include "Poco/Util/Application.h" #include "Poco/Util/ServerApplication.h" #include "Poco/Util/Option.h" #include "Poco/Util/OptionSet.h" -#include "Poco/UUIDGenerator.h" -#include "Poco/ErrorHandler.h" #include "Poco/Crypto/RSAKey.h" #include "Poco/Crypto/CipherFactory.h" #include "Poco/Crypto/Cipher.h" -#include "Poco/SHA2Engine.h" #include "Poco/Net/HTTPServerRequest.h" #include "Poco/Process.h" -#include "Poco/Net/Context.h" -#include "Poco/Net/SecureServerSocket.h" -#include "Poco/Net/Socket.h" -#include "Poco/Net/SSLManager.h" -#include "Poco/Net/PrivateKeyPassphraseHandler.h" -#include "Poco/Net/HTTPServerResponse.h" -#include "Poco/Net/HTTPServer.h" -#include "Poco/Net/FTPStreamFactory.h" -#include "Poco/Net/FTPSStreamFactory.h" -#include "Poco/Net/HTTPSStreamFactory.h" -#include "Poco/Net/HTTPStreamFactory.h" -#include "Poco/File.h" -#include "Poco/Net/HTTPRequestHandler.h" -#include "Poco/Net/OAuth20Credentials.h" #include "Poco/Util/HelpFormatter.h" -#include "Poco/Net/PartHandler.h" -#include "Poco/TemporaryFile.h" -#include "Poco/NullStream.h" -#include "Poco/CountingStream.h" -#include "Poco/URI.h" -#include "Poco/Net/HTTPSClientSession.h" -#include "Poco/Net/NetworkInterface.h" -#include "Poco/ExpireLRUCache.h" #include "Poco/JSON/Object.h" -#include "Poco/JSON/Parser.h" -#include "Poco/StringTokenizer.h" -#include "Poco/AsyncChannel.h" -#include "Poco/ConsoleChannel.h" #include "Poco/AutoPtr.h" -#include "Poco/FormattingChannel.h" -#include "Poco/PatternFormatter.h" -#include "Poco/FileChannel.h" -#include "Poco/SimpleFileChannel.h" #include "Poco/Util/PropertyFileConfiguration.h" -#include "Poco/SplitterChannel.h" #include "Poco/JWT/Signer.h" -#include "Poco/DeflatingStream.h" -#include "Poco/Net/SocketReactor.h" -#include "Poco/Net/WebSocket.h" #include "Poco/Environment.h" -#include "Poco/NObserver.h" -#include "Poco/Net/SocketNotification.h" -#include "Poco/Base64Decoder.h" -#include "Poco/ThreadLocal.h" + +#include "framework/OpenWifiTypes.h" + +#include "framework/ow_constants.h" +#include "framework/utils.h" +#include "framework/SubSystemServer.h" +#include "framework/EventBusManager.h" + +#include "RESTObjects/RESTAPI_SecurityObjects.h" #include "cppkafka/cppkafka.h" -#include "framework/MicroServiceErrorHandler.h" -#include "framework/OpenWifiTypes.h" -#include "framework/KafkaTopics.h" -#include "framework/ow_constants.h" -#include "RESTObjects/RESTAPI_SecurityObjects.h" #include "nlohmann/json.hpp" #include "ow_version.h" #include "fmt/core.h" #define _OWDEBUG_ std::cout<< __FILE__ <<":" << __LINE__ << std::endl; // #define _OWDEBUG_ Logger().debug(Poco::format("%s: %lu",__FILE__,__LINE__)); -namespace OpenWifi { - - enum UNAUTHORIZED_REASON { - SUCCESS=0, - PASSWORD_CHANGE_REQUIRED, - INVALID_CREDENTIALS, - PASSWORD_ALREADY_USED, - USERNAME_PENDING_VERIFICATION, - PASSWORD_INVALID, - INTERNAL_ERROR, - ACCESS_DENIED, - INVALID_TOKEN, - EXPIRED_TOKEN, - RATE_LIMIT_EXCEEDED, - BAD_MFA_TRANSACTION, - MFA_FAILURE, - SECURITY_SERVICE_UNREACHABLE, - CANNOT_REFRESH_TOKEN - }; - - class AppServiceRegistry { - public: - inline AppServiceRegistry(); - - static AppServiceRegistry & instance() { - static auto instance_= new AppServiceRegistry; - return *instance_; - } - - inline ~AppServiceRegistry() { - Save(); - } - - inline void Save() { - std::istringstream IS( to_string(Registry_)); - std::ofstream OF; - OF.open(FileName,std::ios::binary | std::ios::trunc); - Poco::StreamCopier::copyStream(IS, OF); - } - - inline void Set(const char *Key, uint64_t Value ) { - Registry_[Key] = Value; - Save(); - } - - inline void Set(const char *Key, const std::string &Value ) { - Registry_[Key] = Value; - Save(); - } - - inline void Set(const char *Key, bool Value ) { - Registry_[Key] = Value; - Save(); - } - - inline bool Get(const char *Key, bool & Value ) { - if(Registry_[Key].is_boolean()) { - Value = Registry_[Key].get(); - return true; - } - return false; - } - - inline bool Get(const char *Key, uint64_t & Value ) { - if(Registry_[Key].is_number_unsigned()) { - Value = Registry_[Key].get(); - return true; - } - return false; - } - - inline bool Get(const char *Key, std::string & Value ) { - if(Registry_[Key].is_string()) { - Value = Registry_[Key].get(); - return true; - } - return false; - } - - private: - std::string FileName; - nlohmann::json Registry_; - }; - - inline auto AppServiceRegistry() { return AppServiceRegistry::instance(); } - -} - -namespace OpenWifi::RESTAPI_utils { - - inline void EmbedDocument(const std::string & ObjName, Poco::JSON::Object & Obj, const std::string &ObjStr) { - std::string D = ObjStr.empty() ? "{}" : ObjStr; - Poco::JSON::Parser P; - Poco::Dynamic::Var result = P.parse(D); - const auto &DetailsObj = result.extract(); - Obj.set(ObjName, DetailsObj); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, bool V) { - Obj.set(Field,V); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, double V) { - Obj.set(Field,V); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, float V) { - Obj.set(Field,V); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::string & S) { - Obj.set(Field,S); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const char * S) { - Obj.set(Field,S); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, int16_t Value) { - Obj.set(Field, Value); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, int32_t Value) { - Obj.set(Field, Value); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, int64_t Value) { - Obj.set(Field, Value); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint16_t Value) { - Obj.set(Field, Value); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint32_t Value) { - Obj.set(Field, Value); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint64_t Value) { - Obj.set(Field,Value); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Poco::Data::BLOB &Value) { - auto Result = Utils::base64encode((const unsigned char *)Value.rawContent(),Value.size()); - Obj.set(Field,Result); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringPairVec & S) { - Poco::JSON::Array Array; - for(const auto &i:S) { - Poco::JSON::Object O; - O.set("tag",i.first); - O.set("value", i.second); - Array.add(O); - } - Obj.set(Field,Array); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringVec &V) { - Poco::JSON::Array A; - for(const auto &i:V) - A.add(i); - Obj.set(Field,A); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::TagList &V) { - Poco::JSON::Array A; - for(const auto &i:V) - A.add(i); - Obj.set(Field,A); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::CountedMap &M) { - Poco::JSON::Array A; - for(const auto &[Key,Value]:M) { - Poco::JSON::Object O; - O.set("tag",Key); - O.set("value", Value); - A.add(O); - } - Obj.set(Field,A); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::Counted3DMapSII &M) { - Poco::JSON::Array A; - for(const auto &[OrgName,MonthlyNumberMap]:M) { - Poco::JSON::Object OrgObject; - OrgObject.set("tag",OrgName); - Poco::JSON::Array MonthlyArray; - for(const auto &[Month,Counter]:MonthlyNumberMap) { - Poco::JSON::Object Inner; - Inner.set("value", Month); - Inner.set("counter", Counter); - MonthlyArray.add(Inner); - } - OrgObject.set("index",MonthlyArray); - A.add(OrgObject); - } - Obj.set(Field, A); - } - - template void field_to_json(Poco::JSON::Object &Obj, - const char *Field, - const T &V, - std::function F) { - Obj.set(Field, F(V)); - } - - template void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::vector &Value) { - Poco::JSON::Array Arr; - for(const auto &i:Value) { - Poco::JSON::Object AO; - i.to_json(AO); - Arr.add(AO); - } - Obj.set(Field, Arr); - } - - template void field_to_json(Poco::JSON::Object &Obj, const char *Field, const T &Value) { - Poco::JSON::Object Answer; - Value.to_json(Answer); - Obj.set(Field, Answer); - } - - /////////////////////////// - /////////////////////////// - /////////////////////////// - /////////////////////////// - - template bool field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, T & V, - std::function F) { - if(Obj->has(Field) && !Obj->isNull(Field)) - V = F(Obj->get(Field).toString()); - return true; - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::string &S) { - if(Obj->has(Field) && !Obj->isNull(Field)) - S = Obj->get(Field).toString(); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, double & Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) - Value = (double)Obj->get(Field); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, float & Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) - Value = (float)Obj->get(Field); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, bool &Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) - Value = (Obj->get(Field).toString() == "true"); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, int16_t &Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) - Value = (int16_t)Obj->get(Field); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, int32_t &Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) - Value = (int32_t) Obj->get(Field); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, int64_t &Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) - Value = (int64_t)Obj->get(Field); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint16_t &Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) - Value = (uint16_t)Obj->get(Field); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint32_t &Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) - Value = (uint32_t)Obj->get(Field); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint64_t &Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) - Value = (uint64_t)Obj->get(Field); - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Poco::Data::BLOB &Value) { - if(Obj->has(Field) && !Obj->isNull(Field)) { - auto Result = Utils::base64decode(Obj->get(Field).toString()); - Value.assignRaw((const unsigned char *)&Result[0],Result.size()); - } - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::StringPairVec &Vec) { - if(Obj->isArray(Field) && !Obj->isNull(Field)) { - auto O = Obj->getArray(Field); - for(const auto &i:*O) { - std::string S1,S2; - auto Inner = i.extract(); - if(Inner->has("tag")) - S1 = Inner->get("tag").toString(); - if(Inner->has("value")) - S2 = Inner->get("value").toString(); - auto P = std::make_pair(S1,S2); - Vec.push_back(P); - } - } - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::StringVec &Value) { - if(Obj->isArray(Field) && !Obj->isNull(Field)) { - Value.clear(); - Poco::JSON::Array::Ptr A = Obj->getArray(Field); - for(const auto &i:*A) { - Value.push_back(i.toString()); - } - } - } - - inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::TagList &Value) { - if(Obj->isArray(Field) && !Obj->isNull(Field)) { - Value.clear(); - Poco::JSON::Array::Ptr A = Obj->getArray(Field); - for(const auto &i:*A) { - Value.push_back(i); - } - } - } - - template void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::vector &Value) { - if(Obj->isArray(Field) && !Obj->isNull(Field)) { - Poco::JSON::Array::Ptr Arr = Obj->getArray(Field); - for(auto &i:*Arr) { - auto InnerObj = i.extract(); - T NewItem; - NewItem.from_json(InnerObj); - Value.push_back(NewItem); - } - } - } - - template void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, T &Value) { - if(Obj->isObject(Field) && !Obj->isNull(Field)) { - Poco::JSON::Object::Ptr A = Obj->getObject(Field); - Value.from_json(A); - } - } - - inline std::string to_string(const Types::TagList & ObjectArray) { - Poco::JSON::Array OutputArr; - if(ObjectArray.empty()) - return "[]"; - for(auto const &i:ObjectArray) { - OutputArr.add(i); - } - std::ostringstream OS; - Poco::JSON::Stringifier::stringify(OutputArr,OS, 0,0, Poco::JSON_PRESERVE_KEY_ORDER ); - return OS.str(); - } - - inline std::string to_string(const Types::StringVec & ObjectArray) { - Poco::JSON::Array OutputArr; - if(ObjectArray.empty()) - return "[]"; - for(auto const &i:ObjectArray) { - OutputArr.add(i); - } - std::ostringstream OS; - Poco::JSON::Stringifier::condense(OutputArr,OS); - return OS.str(); - } - - inline std::string to_string(const Types::StringPairVec & ObjectArray) { - Poco::JSON::Array OutputArr; - if(ObjectArray.empty()) - return "[]"; - for(auto const &i:ObjectArray) { - Poco::JSON::Array InnerArray; - InnerArray.add(i.first); - InnerArray.add(i.second); - OutputArr.add(InnerArray); - } - std::ostringstream OS; - Poco::JSON::Stringifier::condense(OutputArr,OS); - return OS.str(); - } - - template std::string to_string(const std::vector & ObjectArray) { - Poco::JSON::Array OutputArr; - if(ObjectArray.empty()) - return "[]"; - for(auto const &i:ObjectArray) { - Poco::JSON::Object O; - i.to_json(O); - OutputArr.add(O); - } - std::ostringstream OS; - Poco::JSON::Stringifier::condense(OutputArr,OS); - return OS.str(); - } - - template std::string to_string(const std::vector> & ObjectArray) { - Poco::JSON::Array OutputArr; - if(ObjectArray.empty()) - return "[]"; - for(auto const &i:ObjectArray) { - Poco::JSON::Array InnerArr; - for(auto const &j:i) { - if constexpr(std::is_integral::value) { - InnerArr.add(j); - } if constexpr(std::is_same_v) { - InnerArr.add(j); - } else { - InnerArr.add(j); - Poco::JSON::Object O; - j.to_json(O); - InnerArr.add(O); - } - } - OutputArr.add(InnerArr); - } - std::ostringstream OS; - Poco::JSON::Stringifier::condense(OutputArr,OS); - return OS.str(); - } - - template std::string to_string(const T & Object) { - Poco::JSON::Object OutputObj; - Object.to_json(OutputObj); - std::ostringstream OS; - Poco::JSON::Stringifier::condense(OutputObj,OS); - return OS.str(); - } - - inline Types::StringVec to_object_array(const std::string & ObjectString) { - - Types::StringVec Result; - if(ObjectString.empty()) - return Result; - - try { - Poco::JSON::Parser P; - auto Object = P.parse(ObjectString).template extract(); - for (auto const &i : *Object) { - Result.push_back(i.toString()); - } - } catch (...) { - - } - return Result; - } - - inline OpenWifi::Types::TagList to_taglist(const std::string & ObjectString) { - Types::TagList Result; - if(ObjectString.empty()) - return Result; - - try { - Poco::JSON::Parser P; - auto Object = P.parse(ObjectString).template extract(); - for (auto const &i : *Object) { - Result.push_back(i); - } - } catch (...) { - - } - return Result; - } - - inline Types::StringPairVec to_stringpair_array(const std::string &S) { - Types::StringPairVec R; - if(S.empty()) - return R; - try { - Poco::JSON::Parser P; - auto Object = P.parse(S).template extract(); - for (const auto &i : *Object) { - auto InnerObject = i.template extract(); - if(InnerObject->size()==2) { - auto S1 = InnerObject->getElement(0); - auto S2 = InnerObject->getElement(1); - R.push_back(std::make_pair(S1,S2)); - } - } - } catch (...) { - - } - - return R; - } - - template std::vector to_object_array(const std::string & ObjectString) { - std::vector Result; - if(ObjectString.empty()) - return Result; - - try { - Poco::JSON::Parser P; - auto Object = P.parse(ObjectString).template extract(); - for (auto const &i : *Object) { - auto InnerObject = i.template extract(); - T Obj; - Obj.from_json(InnerObject); - Result.push_back(Obj); - } - } catch (...) { - - } - return Result; - } - - template std::vector> to_array_of_array_of_object(const std::string & ObjectString) { - std::vector> Result; - if(ObjectString.empty()) - return Result; - try { - Poco::JSON::Parser P1; - auto OutterArray = P1.parse(ObjectString).template extract(); - for (auto const &i : *OutterArray) { - Poco::JSON::Parser P2; - auto InnerArray = P2.parse(i).template extract(); - std::vector InnerVector; - for(auto const &j: *InnerArray) { - auto Object = j.template extract(); - T Obj; - Obj.from_json(Object); - InnerVector.push_back(Obj); - } - Result.push_back(InnerVector); - } - } catch (...) { - - } - return Result; - } - - template T to_object(const std::string & ObjectString) { - T Result; - - if(ObjectString.empty()) - return Result; - - Poco::JSON::Parser P; - auto Object = P.parse(ObjectString).template extract(); - Result.from_json(Object); - - return Result; - } - - template bool from_request(T & Obj, Poco::Net::HTTPServerRequest &Request) { - Poco::JSON::Parser IncomingParser; - auto RawObject = IncomingParser.parse(Request.stream()).extract(); - Obj.from_json(RawObject); - return true; - } -} - -namespace OpenWifi::Utils { - - inline bool NormalizeMac(std::string & Mac) { - Poco::replaceInPlace(Mac,":",""); - Poco::replaceInPlace(Mac,"-",""); - if(Mac.size()!=12) - return false; - for(const auto &i:Mac) { - if(!std::isxdigit(i)) - return false; - } - Poco::toLowerInPlace(Mac); - return true; - } - - inline void SetThreadName(const char *name) { -#ifdef __linux__ - Poco::Thread::current()->setName(name); - pthread_setname_np(pthread_self(), name); -#endif -#ifdef __APPLE__ - Poco::Thread::current()->setName(name); - pthread_setname_np(name); -#endif - } - - inline void SetThreadName(Poco::Thread &thr, const char *name) { -#ifdef __linux__ - thr.setName(name); - pthread_setname_np(thr.tid(), name); -#endif -#ifdef __APPLE__ - thr.setName(name); -#endif - } - - enum MediaTypeEncodings { - PLAIN, - BINARY, - BASE64 - }; - struct MediaTypeEncoding { - MediaTypeEncodings Encoding=PLAIN; - std::string ContentType; - }; - - [[nodiscard]] inline bool ValidSerialNumber(const std::string &Serial) { - return ((Serial.size() < uCentralProtocol::SERIAL_NUMBER_LENGTH) && - std::all_of(Serial.begin(),Serial.end(),[](auto i){return std::isxdigit(i);})); - } - - [[nodiscard]] inline bool ValidUUID(const std::string &UUID) { - if(UUID.size()>36) - return false; - uint dashes=0; - return (std::all_of(UUID.begin(),UUID.end(),[&](auto i){ if(i=='-') dashes++; return i=='-' || std::isxdigit(i);})) && (dashes>0); - } - - template std::string ComputeHash(Args&&... args) { - Poco::SHA2Engine E; - auto as_string = [](auto p) { - if constexpr(std::is_arithmetic_v) { - return std::to_string(p); - } else { - return p; - } - }; - (E.update(as_string(args)),...); - return Poco::SHA2Engine::digestToHex(E.digest()); - } - - [[nodiscard]] inline std::vector Split(const std::string &List, char Delimiter=',' ) { - std::vector ReturnList; - - unsigned long P=0; - - while(P12) - R = R.substr(0,12); - - char buf[18]; - - buf[0] = R[0]; buf[1] = R[1] ; buf[2] = ':' ; - buf[3] = R[2] ; buf[4] = R[3]; buf[5] = ':' ; - buf[6] = R[4]; buf[7] = R[5] ; buf[8] = ':' ; - buf[9] = R[6] ; buf[10]= R[7]; buf[11] = ':'; - buf[12] = R[8] ; buf[13]= R[9]; buf[14] = ':'; - buf[15] = R[10] ; buf[16]= R[11];buf[17] = 0; - - return buf; - } - - inline uint64_t MACToInt(const std::string &MAC) { - uint64_t Result = 0 ; - for(const auto &c:MAC) { - if(c==':') - continue; - Result <<= 4; - if(c>='0' && c<='9') { - Result += (c - '0'); - } else if (c>='a' && c<='f') { - Result += (c-'a'+10); - } else if (c>='A' && c<='F') { - Result += (c-'A'+10); - } - } - return Result; - } - - [[nodiscard]] inline std::string ToHex(const std::vector & B) { - std::string R; - R.reserve(B.size()*2); - - static const char hex[] = "0123456789abcdef"; - - for(const auto &i:B) - { - R += (hex[ (i & 0xf0) >> 4]); - R += (hex[ (i & 0x0f) ]); - } - - return R; - } - - inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - inline static const char kPadCharacter = '='; - - using byte = std::uint8_t; - - [[nodiscard]] inline std::string base64encode(const byte *input, uint32_t size) { - std::string encoded; - encoded.reserve(((size / 3) + (size % 3 > 0)) * 4); - - std::uint32_t temp,i,ee; - ee = (size/3); - - for (i = 0; i < 3*ee; ++i) { - temp = input[i++] << 16; - temp += input[i++] << 8; - temp += input[i]; - encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); - encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); - encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]); - encoded.append(1, kEncodeLookup[(temp & 0x0000003F)]); - } - - switch (size % 3) { - case 1: - temp = input[i] << 16; - encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); - encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); - encoded.append(2, kPadCharacter); - break; - case 2: - temp = input[i++] << 16; - temp += input[i] << 8; - encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); - encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); - encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]); - encoded.append(1, kPadCharacter); - break; - } - - return encoded; - } - - [[nodiscard]] inline std::vector base64decode(const std::string& input) - { - if(input.length() % 4) - throw std::runtime_error("Invalid base64 length!"); - - std::size_t padding=0; - - if(input.length()) - { - if(input[input.length() - 1] == kPadCharacter) padding++; - if(input[input.length() - 2] == kPadCharacter) padding++; - } - - std::vector decoded; - decoded.reserve(((input.length() / 4) * 3) - padding); - - std::uint32_t temp=0; - auto it = input.begin(); - - while(it < input.end()) - { - for(std::size_t i = 0; i < 4; ++i) - { - temp <<= 6; - if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41; - else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47; - else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04; - else if(*it == 0x2B) temp |= 0x3E; - else if(*it == 0x2F) temp |= 0x3F; - else if(*it == kPadCharacter) - { - switch(input.end() - it) - { - case 1: - decoded.push_back((temp >> 16) & 0x000000FF); - decoded.push_back((temp >> 8 ) & 0x000000FF); - return decoded; - case 2: - decoded.push_back((temp >> 10) & 0x000000FF); - return decoded; - default: - throw std::runtime_error("Invalid padding in base64!"); - } - } - else throw std::runtime_error("Invalid character in base64!"); - - ++it; - } - - decoded.push_back((temp >> 16) & 0x000000FF); - decoded.push_back((temp >> 8 ) & 0x000000FF); - decoded.push_back((temp ) & 0x000000FF); - } - - return decoded; - } - - inline bool ParseTime(const std::string &Time, int & Hours, int & Minutes, int & Seconds) { - Poco::StringTokenizer TimeTokens(Time,":",Poco::StringTokenizer::TOK_TRIM); - - Hours = Minutes = Seconds = 0 ; - if(TimeTokens.count()==1) { - Hours = std::atoi(TimeTokens[0].c_str()); - } else if(TimeTokens.count()==2) { - Hours = std::atoi(TimeTokens[0].c_str()); - Minutes = std::atoi(TimeTokens[1].c_str()); - } else if(TimeTokens.count()==3) { - Hours = std::atoi(TimeTokens[0].c_str()); - Minutes = std::atoi(TimeTokens[1].c_str()); - Seconds = std::atoi(TimeTokens[2].c_str()); - } else - return false; - return true; - } - - - inline bool ParseDate(const std::string &Time, int & Year, int & Month, int & Day) { - Poco::StringTokenizer DateTokens(Time,"-",Poco::StringTokenizer::TOK_TRIM); - - Year = Month = Day = 0 ; - if(DateTokens.count()==3) { - Year = std::atoi(DateTokens[0].c_str()); - Month = std::atoi(DateTokens[1].c_str()); - Day = std::atoi(DateTokens[2].c_str()); - } else - return false; - return true; - } - - inline bool CompareTime( int H1, int H2, int M1, int M2, int S1, int S2) { - if(H1H2) - return false; - if(M1M1) - return false; - if(S1<=S2) - return true; - return false; - } - - [[nodiscard]] inline std::string LogLevelToString(int Level) { - switch(Level) { - case Poco::Message::PRIO_DEBUG: return "debug"; - case Poco::Message::PRIO_INFORMATION: return "information"; - case Poco::Message::PRIO_FATAL: return "fatal"; - case Poco::Message::PRIO_WARNING: return "warning"; - case Poco::Message::PRIO_NOTICE: return "notice"; - case Poco::Message::PRIO_CRITICAL: return "critical"; - case Poco::Message::PRIO_ERROR: return "error"; - case Poco::Message::PRIO_TRACE: return "trace"; - default: return "none"; - } - } - - [[nodiscard]] inline uint64_t SerialNumberToInt(const std::string & S) { - return std::stoull(S,nullptr,16); - } - - [[nodiscard]] inline std::string IntToSerialNumber(uint64_t S) { - char b[16]; - for(int i=0;i<12;++i) { - int B = (S & 0x0f); - if(B<10) - b[11-i] = B+'0'; - else - b[11-i] = B - 10 + 'a'; - S >>= 4 ; - } - b[12]=0; - return b; - } - - - [[nodiscard]] inline bool SerialNumberMatch(const std::string &S1, const std::string &S2, int Bits=2) { - auto S1_i = SerialNumberToInt(S1); - auto S2_i = SerialNumberToInt(S2); - return ((S1_i>>Bits)==(S2_i>>Bits)); - } - - [[nodiscard]] inline uint64_t SerialNumberToOUI(const std::string & S) { - uint64_t Result = 0 ; - int Digits=0; - - for(const auto &i:S) { - if(std::isxdigit(i)) { - if(i>='0' && i<='9') { - Result <<=4; - Result += i-'0'; - } else if(i>='A' && i<='F') { - Result <<=4; - Result += i-'A'+10; - } else if(i>='a' && i<='f') { - Result <<=4; - Result += i-'a'+10; - } - Digits++; - if(Digits==6) - break; - } - } - return Result; - } - - [[nodiscard]] inline uint64_t GetDefaultMacAsInt64() { - uint64_t Result=0; - auto IFaceList = Poco::Net::NetworkInterface::list(); - - for(const auto &iface:IFaceList) { - if(iface.isRunning() && !iface.isLoopback()) { - auto MAC = iface.macAddress(); - for (auto const &i : MAC) { - Result <<= 8; - Result += (uint8_t)i; - } - if (Result != 0) - break; - } - } - return Result; - } - - [[nodiscard]] inline uint64_t InitializeSystemId() { - std::random_device RDev; - std::srand(RDev()); - std::chrono::high_resolution_clock Clock; - auto Now = Clock.now().time_since_epoch().count(); - auto S = (GetDefaultMacAsInt64() + std::rand() + Now) ; - OpenWifi::AppServiceRegistry().Set("systemid",S); - return S; - } - - [[nodiscard]] inline uint64_t GetSystemId(); - - [[nodiscard]] inline bool ValidEMailAddress(const std::string &email) { - // define a regular expression - static const std::regex pattern - ("[_a-z0-9-]+(\\.[_a-z0-9-]+)*(\\+[a-z0-9-]+)?@[a-z0-9-]+(\\.[a-z0-9-]+)*"); - // try to match the string with the regular expression - return std::regex_match(email, pattern); - } - - [[nodiscard]] inline std::string LoadFile( const Poco::File & F) { - std::string Result; - try { - std::ostringstream OS; - std::ifstream IF(F.path()); - Poco::StreamCopier::copyStream(IF, OS); - Result = OS.str(); - } catch (...) { - - } - return Result; - } - - inline void ReplaceVariables( std::string & Content , const Types::StringPairVec & P) { - for(const auto &[Variable,Value]:P) { - Poco::replaceInPlace(Content,"${" + Variable + "}", Value); - } - } - - [[nodiscard]] inline MediaTypeEncoding FindMediaType(const Poco::File &F) { - const auto E = Poco::Path(F.path()).getExtension(); - if(E=="png") - return MediaTypeEncoding{ .Encoding = BINARY, - .ContentType = "image/png" }; - if(E=="gif") - return MediaTypeEncoding{ .Encoding = BINARY, - .ContentType = "image/gif" }; - if(E=="jpeg" || E=="jpg") - return MediaTypeEncoding{ .Encoding = BINARY, - .ContentType = "image/jpeg" }; - if(E=="svg" || E=="svgz") - return MediaTypeEncoding{ .Encoding = PLAIN, - .ContentType = "image/svg+xml" }; - if(E=="html") - return MediaTypeEncoding{ .Encoding = PLAIN, - .ContentType = "text/html" }; - if(E=="css") - return MediaTypeEncoding{ .Encoding = PLAIN, - .ContentType = "text/css" }; - if(E=="js") - return MediaTypeEncoding{ .Encoding = PLAIN, - .ContentType = "application/javascript" }; - return MediaTypeEncoding{ .Encoding = BINARY, - .ContentType = "application/octet-stream" }; - } - - [[nodiscard]] inline std::string BinaryFileToHexString(const Poco::File &F) { - static const char hex[] = "0123456789abcdef"; - std::string Result; - try { - std::ifstream IF(F.path()); - - int Count = 0; - while (IF.good()) { - if (Count) - Result += ", "; - if ((Count % 32) == 0) - Result += "\r\n"; - Count++; - unsigned char C = IF.get(); - Result += "0x"; - Result += (char) (hex[(C & 0xf0) >> 4]); - Result += (char) (hex[(C & 0x0f)]); - } - } catch(...) { - - } - return Result; - } - - [[nodiscard]] inline std::string SecondsToNiceText(uint64_t Seconds) { - std::string Result; - int Days = Seconds / (24*60*60); - Seconds -= Days * (24*60*60); - int Hours= Seconds / (60*60); - Seconds -= Hours * (60*60); - int Minutes = Seconds / 60; - Seconds -= Minutes * 60; - Result = std::to_string(Days) +" days, " + std::to_string(Hours) + ":" + std::to_string(Minutes) + ":" + std::to_string(Seconds); - return Result; - } - - [[nodiscard]] inline bool wgets(const std::string &URL, std::string &Response) { - try { - Poco::URI uri(URL); - Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort()); - - // prepare path - std::string path(uri.getPathAndQuery()); - if (path.empty()) { - path = "/"; - } - - // send request - Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1); - session.sendRequest(req); - - Poco::Net::HTTPResponse res; - std::istream &is = session.receiveResponse(res); - std::ostringstream os; - - Poco::StreamCopier::copyStream(is,os); - Response = os.str(); - - return true; - } catch (...) { - - } - return false; - } - - template< typename T > - std::string int_to_hex( T i ) - { - std::stringstream stream; - stream << std::setfill ('0') << std::setw(12) - << std::hex << i; - return stream.str(); - } - - inline bool ExtractBase64CompressedData(const std::string &CompressedData, - std::string &UnCompressedData, uint64_t compress_sz ) { - std::istringstream ifs(CompressedData); - Poco::Base64Decoder b64in(ifs); - std::ostringstream ofs; - Poco::StreamCopier::copyStream(b64in, ofs); - - int factor = 20; - unsigned long MaxSize = compress_sz ? (unsigned long) (compress_sz + 5000) : (unsigned long) (ofs.str().size() * factor); - while(true) { - std::vector UncompressedBuffer(MaxSize); - unsigned long FinalSize = MaxSize; - auto status = uncompress((uint8_t *)&UncompressedBuffer[0], &FinalSize, - (uint8_t *)ofs.str().c_str(), ofs.str().size()); - if(status==Z_OK) { - UncompressedBuffer[FinalSize] = 0; - UnCompressedData = (char *)&UncompressedBuffer[0]; - return true; - } - if(status==Z_BUF_ERROR) { - if(factor<300) { - factor+=10; - MaxSize = ofs.str().size() * factor; - continue; - } else { - return false; - } - } - return false; - } - return false; - } - -} namespace OpenWifi { - static const std::string uSERVICE_SECURITY{"owsec"}; - static const std::string uSERVICE_GATEWAY{"owgw"}; - static const std::string uSERVICE_FIRMWARE{ "owfms"}; - static const std::string uSERVICE_TOPOLOGY{ "owtopo"}; - static const std::string uSERVICE_PROVISIONING{ "owprov"}; - static const std::string uSERVICE_OWLS{ "owls"}; - static const std::string uSERVICE_SUBCRIBER{ "owsub"}; - static const std::string uSERVICE_INSTALLER{ "owinst"}; - static const std::string uSERVICE_ANALYTICS{ "owanalytics"}; - static const std::string uSERVICE_OWRRM{ "owrrm"}; - - class ConfigurationEntry { - public: - template explicit ConfigurationEntry(T def) : - Default_(def), - Current_(def){ - } - - template explicit ConfigurationEntry(T def, T cur, const std::string &Hint="") : - Default_(def), - Current_(cur), - Hint_(Hint){ - } - - inline ConfigurationEntry()=default; - inline ~ConfigurationEntry()=default; - - template explicit operator T () const { return std::get(Current_); } - inline ConfigurationEntry & operator=(const char *v) { Current_ = std::string(v); return *this;} - template ConfigurationEntry & operator=(T v) { Current_ = (T) v; return *this;} - - void reset() { - Current_ = Default_; - } - - private: - std::variant Default_, Current_; - std::string Hint_; - }; - inline std::string to_string(const ConfigurationEntry &v) { return (std::string) v; } - - typedef std::map ConfigurationMap_t; - - template class FIFO { - public: - explicit FIFO(uint32_t Size) : - Size_(Size) { - Buffer_ = new T [Size_]; - } - - ~FIFO() { - delete [] Buffer_; - } - - mutable Poco::BasicEvent Writable_; - mutable Poco::BasicEvent Readable_; - - inline bool Read(T &t) { - { - std::lock_guard M(Mutex_); - if (Write_ == Read_) { - return false; - } - - t = Buffer_[Read_++]; - if (Read_ == Size_) { - Read_ = 0; - } - Used_--; - } - bool flag = true; - Writable_.notify(this, flag); - return true; - } - - inline bool Write(const T &t) { - { - std::lock_guard M(Mutex_); - - Buffer_[Write_++] = t; - if (Write_ == Size_) { - Write_ = 0; - } - Used_++; - MaxEverUsed_ = std::max(Used_,MaxEverUsed_); - } - bool flag = true; - Readable_.notify(this, flag); - return false; - } - - inline bool isFull() { - std::lock_guard M(Mutex_); - return Used_==Buffer_->capacity(); - } - - inline auto MaxEverUser() const { return MaxEverUsed_; } - - private: - std::recursive_mutex Mutex_; - uint32_t Size_=0; - uint32_t Read_=0; - uint32_t Write_=0; - uint32_t Used_=0; - uint32_t MaxEverUsed_=0; - T * Buffer_ = nullptr; - }; - - template class RecordCache { - public: - explicit RecordCache( KeyType Record::* Q) : - MemberOffset(Q){ - }; - inline auto update(const Record &R) { - return Cache_.update(R.*MemberOffset, R); - } - inline auto get(const KeyType &K) { - return Cache_.get(K); - } - inline auto remove(const KeyType &K) { - return Cache_.remove(K); - } - inline auto remove(const Record &R) { - return Cache_.remove(R.*MemberOffset); - } - private: - KeyType Record::* MemberOffset; - Poco::ExpireLRUCache Cache_{Size,Expiry}; - }; - - class BusEventManager : public Poco::Runnable { - public: - explicit BusEventManager(Poco::Logger &L) : Logger_(L) { - - } - inline void run() final; - inline void Start(); - inline void Stop(); - inline Poco::Logger & Logger() { return Logger_; } - private: - mutable std::atomic_bool Running_ = false; - Poco::Thread Thread_; - Poco::Logger &Logger_; - }; - - class MyPrivateKeyPassphraseHandler : public Poco::Net::PrivateKeyPassphraseHandler { - public: - explicit MyPrivateKeyPassphraseHandler(const std::string &Password, Poco::Logger & Logger): - PrivateKeyPassphraseHandler(true), - Password_(Password), - Logger_(Logger) - {} - void onPrivateKeyRequested([[maybe_unused]] const void * pSender,std::string & privateKey) { - Logger_.information("Returning key passphrase."); - privateKey = Password_; - }; - inline Poco::Logger & Logger() { return Logger_; } - private: - std::string Password_; - Poco::Logger & Logger_; - }; - - class PropertiesFileServerEntry { - public: - PropertiesFileServerEntry(std::string Address, uint32_t port, std::string Key_file, - std::string Cert_file, std::string RootCa, std::string Issuer, - std::string ClientCas, std::string Cas, - std::string Key_file_password = "", std::string Name = "", - Poco::Net::Context::VerificationMode M = - Poco::Net::Context::VerificationMode::VERIFY_RELAXED, - int backlog = 64) - : address_(std::move(Address)), - port_(port), - cert_file_(std::move(Cert_file)), - key_file_(std::move(Key_file)), - root_ca_(std::move(RootCa)), - key_file_password_(std::move(Key_file_password)), - issuer_cert_file_(std::move(Issuer)), - client_cas_(std::move(ClientCas)), - cas_(std::move(Cas)), - name_(std::move(Name)), - backlog_(backlog), - level_(M) - {}; - - [[nodiscard]] inline const std::string &Address() const { return address_; }; - [[nodiscard]] inline uint32_t Port() const { return port_; }; - [[nodiscard]] inline auto KeyFile() const { return key_file_; }; - [[nodiscard]] inline auto CertFile() const { return cert_file_; }; - [[nodiscard]] inline auto RootCA() const { return root_ca_; }; - [[nodiscard]] inline auto KeyFilePassword() const { return key_file_password_; }; - [[nodiscard]] inline auto IssuerCertFile() const { return issuer_cert_file_; }; - [[nodiscard]] inline auto Name() const { return name_; }; - [[nodiscard]] inline int Backlog() const { return backlog_; } - [[nodiscard]] inline auto Cas() const { return cas_; } - - [[nodiscard]] inline Poco::Net::SecureServerSocket CreateSecureSocket(Poco::Logger &L) const { - Poco::Net::Context::Params P; - - P.verificationMode = level_; - P.verificationDepth = 9; - P.loadDefaultCAs = root_ca_.empty(); - P.cipherList = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"; - P.dhUse2048Bits = true; - P.caLocation = cas_; - - auto Context = Poco::AutoPtr(new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P)); - - if(!key_file_password_.empty()) { - auto PassphraseHandler = Poco::SharedPtr( new MyPrivateKeyPassphraseHandler(key_file_password_,L)); - Poco::Net::SSLManager::instance().initializeServer(PassphraseHandler, nullptr,Context); - } - - if (!cert_file_.empty() && !key_file_.empty()) { - Poco::Crypto::X509Certificate Cert(cert_file_); - Poco::Crypto::X509Certificate Root(root_ca_); - - Context->useCertificate(Cert); - Context->addChainCertificate(Root); - - Context->addCertificateAuthority(Root); - - if (level_ == Poco::Net::Context::VERIFY_STRICT) { - if (issuer_cert_file_.empty()) { - L.fatal("In strict mode, you must supply ans issuer certificate"); - } - if (client_cas_.empty()) { - L.fatal("In strict mode, client cas must be supplied"); - } - Poco::Crypto::X509Certificate Issuing(issuer_cert_file_); - Context->addChainCertificate(Issuing); - Context->addCertificateAuthority(Issuing); - } - - Poco::Crypto::RSAKey Key("", key_file_, key_file_password_); - Context->usePrivateKey(Key); - - SSL_CTX *SSLCtx = Context->sslContext(); - if (!SSL_CTX_check_private_key(SSLCtx)) { - L.fatal(fmt::format("Wrong Certificate({}) for Key({})", cert_file_, key_file_)); - } - - SSL_CTX_set_verify(SSLCtx, SSL_VERIFY_PEER, nullptr); - - if (level_ == Poco::Net::Context::VERIFY_STRICT) { - SSL_CTX_set_client_CA_list(SSLCtx, SSL_load_client_CA_file(client_cas_.c_str())); - } - SSL_CTX_enable_ct(SSLCtx, SSL_CT_VALIDATION_STRICT); - SSL_CTX_dane_enable(SSLCtx); - - Context->enableSessionCache(); - Context->setSessionCacheSize(0); - Context->setSessionTimeout(60); - Context->enableExtendedCertificateVerification(true); - Context->disableStatelessSessionResumption(); - } - - if (address_ == "*") { - Poco::Net::IPAddress Addr(Poco::Net::IPAddress::wildcard( - Poco::Net::Socket::supportsIPv6() ? Poco::Net::AddressFamily::IPv6 - : Poco::Net::AddressFamily::IPv4)); - Poco::Net::SocketAddress SockAddr(Addr, port_); - - return Poco::Net::SecureServerSocket(SockAddr, backlog_, Context); - } else { - Poco::Net::IPAddress Addr(address_); - Poco::Net::SocketAddress SockAddr(Addr, port_); - - return Poco::Net::SecureServerSocket(SockAddr, backlog_, Context); - } - } - - [[nodiscard]] inline Poco::Net::ServerSocket CreateSocket([[maybe_unused]] Poco::Logger &L) const { - Poco::Net::Context::Params P; - - if (address_ == "*") { - Poco::Net::IPAddress Addr(Poco::Net::IPAddress::wildcard( - Poco::Net::Socket::supportsIPv6() ? Poco::Net::AddressFamily::IPv6 - : Poco::Net::AddressFamily::IPv4)); - Poco::Net::SocketAddress SockAddr(Addr, port_); - return Poco::Net::ServerSocket(SockAddr, backlog_); - } else { - Poco::Net::IPAddress Addr(address_); - Poco::Net::SocketAddress SockAddr(Addr, port_); - return Poco::Net::ServerSocket(SockAddr, backlog_); - } - } - - inline void LogCertInfo(Poco::Logger &L, const Poco::Crypto::X509Certificate &C) const { - L.information("============================================================================================="); - L.information(fmt::format("> Issuer: {}", C.issuerName())); - L.information("---------------------------------------------------------------------------------------------"); - L.information(fmt::format("> Common Name: {}", - C.issuerName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); - L.information(fmt::format("> Country: {}", - C.issuerName(Poco::Crypto::X509Certificate::NID_COUNTRY))); - L.information(fmt::format("> Locality: {}", - C.issuerName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); - L.information(fmt::format("> State/Prov: {}", - C.issuerName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); - L.information(fmt::format("> Org name: {}", - C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); - L.information( - fmt::format("> Org unit: {}", - C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); - L.information( - fmt::format("> Email: {}", - C.issuerName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); - L.information(fmt::format("> Serial#: {}", - C.issuerName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); - L.information("---------------------------------------------------------------------------------------------"); - L.information(fmt::format("> Subject: {}", C.subjectName())); - L.information("---------------------------------------------------------------------------------------------"); - L.information(fmt::format("> Common Name: {}", - C.subjectName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); - L.information(fmt::format("> Country: {}", - C.subjectName(Poco::Crypto::X509Certificate::NID_COUNTRY))); - L.information(fmt::format("> Locality: {}", - C.subjectName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); - L.information( - fmt::format("> State/Prov: {}", - C.subjectName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); - L.information( - fmt::format("> Org name: {}", - C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); - L.information( - fmt::format("> Org unit: {}", - C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); - L.information( - fmt::format("> Email: {}", - C.subjectName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); - L.information(fmt::format("> Serial#: {}", - C.subjectName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); - L.information("---------------------------------------------------------------------------------------------"); - L.information(fmt::format("> Signature Algo: {}", C.signatureAlgorithm())); - auto From = Poco::DateTimeFormatter::format(C.validFrom(), Poco::DateTimeFormat::HTTP_FORMAT); - L.information(fmt::format("> Valid from: {}", From)); - auto Expires = - Poco::DateTimeFormatter::format(C.expiresOn(), Poco::DateTimeFormat::HTTP_FORMAT); - L.information(fmt::format("> Expires on: {}", Expires)); - L.information(fmt::format("> Version: {}", (int)C.version())); - L.information(fmt::format("> Serial #: {}", C.serialNumber())); - L.information("============================================================================================="); - } - - inline void LogCert(Poco::Logger &L) const { - try { - Poco::Crypto::X509Certificate C(cert_file_); - L.information("============================================================================================="); - L.information("============================================================================================="); - L.information(fmt::format("Certificate Filename: {}", cert_file_)); - LogCertInfo(L, C); - L.information("============================================================================================="); - - if (!issuer_cert_file_.empty()) { - Poco::Crypto::X509Certificate C1(issuer_cert_file_); - L.information("============================================================================================="); - L.information("============================================================================================="); - L.information(fmt::format("Issues Certificate Filename: {}", issuer_cert_file_)); - LogCertInfo(L, C1); - L.information("============================================================================================="); - } - - if (!client_cas_.empty()) { - std::vector Certs = - Poco::Net::X509Certificate::readPEM(client_cas_); - - L.information("============================================================================================="); - L.information("============================================================================================="); - L.information(fmt::format("Client CAs Filename: {}", client_cas_)); - L.information("============================================================================================="); - auto i = 1; - for (const auto &C3 : Certs) { - L.information(fmt::format(" Index: {}", i)); - L.information("============================================================================================="); - LogCertInfo(L, C3); - i++; - } - L.information("============================================================================================="); - } - - } catch (const Poco::Exception &E) { - L.log(E); - } - } - - inline void LogCas(Poco::Logger &L) const { - try { - std::vector Certs = - Poco::Net::X509Certificate::readPEM(root_ca_); - - L.information("============================================================================================="); - L.information("============================================================================================="); - L.information(fmt::format("CA Filename: {}", root_ca_)); - L.information("============================================================================================="); - auto i = 1; - for (const auto &C : Certs) { - L.information(fmt::format(" Index: {}", i)); - L.information("============================================================================================="); - LogCertInfo(L, C); - i++; - } - L.information("============================================================================================="); - } catch (const Poco::Exception &E) { - L.log(E); - } - } - - private: - std::string address_; - uint32_t port_; - std::string cert_file_; - std::string key_file_; - std::string root_ca_; - std::string key_file_password_; - std::string issuer_cert_file_; - std::string client_cas_; - std::string cas_; - std::string name_; - int backlog_; - Poco::Net::Context::VerificationMode level_; - }; - - class SubSystemServer : public Poco::Util::Application::Subsystem { - public: - SubSystemServer(const std::string & Name, const std::string &LoggingPrefix, - const std::string & SubSystemConfigPrefix); - - inline void initialize(Poco::Util::Application &self) override; - inline void uninitialize() override { - } - inline void reinitialize([[maybe_unused]] Poco::Util::Application &self) override { - Logger_->L_.information("Reloading of this subsystem is not supported."); - } - inline void defineOptions([[maybe_unused]] Poco::Util::OptionSet &options) override { - } - inline const std::string & Name() const { return Name_; }; - inline const char * name() const override { return Name_.c_str(); } - - inline const PropertiesFileServerEntry & Host(uint64_t index) { return ConfigServersList_[index]; }; - inline uint64_t HostSize() const { return ConfigServersList_.size(); } - inline Poco::Logger & Logger() const { return Logger_->L_; } - inline void SetLoggingLevel(const std::string & levelName) { - Logger_->L_.setLevel(Poco::Logger::parseLevel(levelName)); - } - inline int GetLoggingLevel() { return Logger_->L_.getLevel(); } - - virtual int Start() = 0; - virtual void Stop() = 0; - - struct LoggerWrapper { - Poco::Logger & L_; - LoggerWrapper(Poco::Logger &L) : - L_(L) {} - }; - - protected: - std::recursive_mutex Mutex_; - std::vector ConfigServersList_; - - private: - std::unique_ptr Logger_; - std::string Name_; - std::string LoggerPrefix_; - std::string SubSystemConfigPrefix_; - }; - - class RESTAPI_GenericServer { - public: - - enum { - LOG_GET=0, - LOG_DELETE, - LOG_PUT, - LOG_POST - }; - - void inline SetFlags(bool External, const std::string &Methods) { - Poco::StringTokenizer Tokens(Methods,","); - auto Offset = (External ? 0 : 4); - for(const auto &i:Tokens) { - if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_DELETE)==0) - LogFlags_[Offset+LOG_DELETE]=true; - else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_PUT)==0) - LogFlags_[Offset+LOG_PUT]=true; - else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_POST)==0) - LogFlags_[Offset+LOG_POST]=true; - else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_GET)==0) - LogFlags_[Offset+LOG_GET]=true; - } - } - - inline void InitLogging(); - - [[nodiscard]] inline bool LogIt(const std::string &Method, bool External) const { - auto Offset = (External ? 0 : 4); - if(Method == Poco::Net::HTTPRequest::HTTP_GET) - return LogFlags_[Offset+LOG_GET]; - if(Method == Poco::Net::HTTPRequest::HTTP_POST) - return LogFlags_[Offset+LOG_POST]; - if(Method == Poco::Net::HTTPRequest::HTTP_PUT) - return LogFlags_[Offset+LOG_PUT]; - if(Method == Poco::Net::HTTPRequest::HTTP_DELETE) - return LogFlags_[Offset+LOG_DELETE]; - return false; - }; - - [[nodiscard]] inline bool LogBadTokens(bool External) const { - return LogBadTokens_[ (External ? 0 : 1) ]; - }; - - private: - std::array LogFlags_{false}; - std::array LogBadTokens_{false}; - }; - - class RESTAPI_PartHandler: public Poco::Net::PartHandler - { - public: - RESTAPI_PartHandler(): - _length(0) - { - } - - inline void handlePart(const Poco::Net::MessageHeader& header, std::istream& stream) override - { - _type = header.get("Content-Type", "(unspecified)"); - if (header.has("Content-Disposition")) - { - std::string disp; - Poco::Net::NameValueCollection params; - Poco::Net::MessageHeader::splitParameters(header["Content-Disposition"], disp, params); - _name = params.get("name", "(unnamed)"); - _fileName = params.get("filename", "(unnamed)"); - } - - Poco::CountingInputStream istr(stream); - Poco::NullOutputStream ostr; - Poco::StreamCopier::copyStream(istr, ostr); - _length = (int)istr.chars(); - } - - [[nodiscard]] inline int length() const - { - return _length; - } - - [[nodiscard]] inline const std::string& name() const - { - return _name; - } - - [[nodiscard]] inline const std::string& fileName() const - { - return _fileName; - } - - [[nodiscard]] inline const std::string& contentType() const - { - return _type; - } - - private: - int _length; - std::string _type; - std::string _name; - std::string _fileName; - }; - - class RESTAPI_RateLimiter : public SubSystemServer { - public: - - struct ClientCacheEntry { - int64_t Start=0; - int Count=0; - }; - - static auto instance() { - static auto instance_ = new RESTAPI_RateLimiter; - return instance_; - } - - inline int Start() final { return 0;}; - inline void Stop() final { }; - - inline bool IsRateLimited(const Poco::Net::HTTPServerRequest &R, int64_t Period, int64_t MaxCalls) { - Poco::URI uri(R.getURI()); - auto H = str_hash(uri.getPath() + R.clientAddress().host().toString()); - auto E = Cache_.get(H); - auto Now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - if(E.isNull()) { - Cache_.add(H,ClientCacheEntry{.Start=Now, .Count=1}); - return false; - } - if((Now-E->Start)Count++; - Cache_.update(H,E); - if(E->Count > MaxCalls) { - poco_warning(Logger(),fmt::format("RATE-LIMIT-EXCEEDED: from '{}'", R.clientAddress().toString())); - return true; - } - return false; - } - E->Start = Now; - E->Count = 1; - Cache_.update(H,E); - return false; - } - - inline void Clear() { - Cache_.clear(); - } - - private: - Poco::ExpireLRUCache Cache_{2048}; - std::hash str_hash; - - RESTAPI_RateLimiter() noexcept: - SubSystemServer("RateLimiter", "RATE-LIMITER", "rate.limiter") - { - } - - }; - - inline auto RESTAPI_RateLimiter() { return RESTAPI_RateLimiter::instance(); } - - class RESTAPIHandler : public Poco::Net::HTTPRequestHandler { - public: - struct QueryBlock { - uint64_t StartDate = 0 , EndDate = 0 , Offset = 0 , Limit = 0, LogType = 0 ; - std::string SerialNumber, Filter; - std::vector Select; - bool Lifetime=false, LastOnly=false, Newest=false, CountOnly=false, AdditionalInfo=false; - }; - typedef std::map BindingMap; - - struct RateLimit { - int64_t Interval=1000; - int64_t MaxCalls=10; - }; - - RESTAPIHandler( BindingMap map, - Poco::Logger &l, - std::vector Methods, - RESTAPI_GenericServer & Server, - uint64_t TransactionId, - bool Internal, - bool AlwaysAuthorize=true, - bool RateLimited=false, - const RateLimit & Profile = RateLimit{.Interval=1000,.MaxCalls=100}, - bool SubscriberOnly=false) - : Bindings_(std::move(map)), - Logger_(l), - Methods_(std::move(Methods)), - Internal_(Internal), - RateLimited_(RateLimited), - SubOnlyService_(SubscriberOnly), - AlwaysAuthorize_(AlwaysAuthorize), - Server_(Server), - MyRates_(Profile), - TransactionId_(TransactionId) - { - } - - inline bool RoleIsAuthorized([[maybe_unused]] const std::string & Path, [[maybe_unused]] const std::string & Method, [[maybe_unused]] std::string & Reason) { - return true; - } - - inline void handleRequest(Poco::Net::HTTPServerRequest &RequestIn, - Poco::Net::HTTPServerResponse &ResponseIn) final { - try { - Request = &RequestIn; - Response = &ResponseIn; - -// std::string th_name = "restsvr_" + std::to_string(TransactionId_); -// Utils::SetThreadName(th_name.c_str()); - - if(Request->getContentLength()>0) { - if(Request->getContentType().find("application/json")!=std::string::npos) { - ParsedBody_ = IncomingParser_.parse(Request->stream()).extract(); - } - } - - if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls)) { - return UnAuthorized(RESTAPI::Errors::RATE_LIMIT_EXCEEDED); - } - - if (!ContinueProcessing()) - return; - - bool Expired=false, Contacted=false; - if (AlwaysAuthorize_ && !IsAuthorized(Expired, Contacted, SubOnlyService_)) { - if(Expired) - return UnAuthorized(RESTAPI::Errors::EXPIRED_TOKEN); - if(Contacted) - return UnAuthorized(RESTAPI::Errors::INVALID_TOKEN); - else - return UnAuthorized(RESTAPI::Errors::SECURITY_SERVICE_UNREACHABLE); - } - - std::string Reason; - if(!RoleIsAuthorized(RequestIn.getURI(), Request->getMethod(), Reason)) { - return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); - } - - ParseParameters(); - if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_GET) - return DoGet(); - else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_POST) - return DoPost(); - else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) - return DoDelete(); - else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_PUT) - return DoPut(); - return BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod); - } catch (const Poco::Exception &E) { - Logger_.log(E); - return BadRequest(RESTAPI::Errors::InternalError); - } - } - - [[nodiscard]] inline bool NeedAdditionalInfo() const { return QB_.AdditionalInfo; } - [[nodiscard]] inline const std::vector & SelectedRecords() const { return QB_.Select; } - - inline static bool ParseBindings(const std::string & Request, const std::list & EndPoints, BindingMap &bindings) { - bindings.clear(); - auto PathItems = Poco::StringTokenizer(Request, "/"); - - for(const auto &EndPoint:EndPoints) { - auto ParamItems = Poco::StringTokenizer(EndPoint, "/"); - if (PathItems.count() != ParamItems.count()) - continue; - - bool Matched = true; - for (size_t i = 0; i < PathItems.count(); i++) { - if (PathItems[i] != ParamItems[i]) { - if (ParamItems[i][0] == '{') { - auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2); - bindings[Poco::toLower(ParamName)] = PathItems[i]; - } else { - Matched = false; - break; - } - } - } - if(Matched) - return true; - } - return false; - } - - inline void PrintBindings() { - for (const auto &[key, value] : Bindings_) - std::cout << "Key = " << key << " Value= " << value << std::endl; - } - - inline void ParseParameters() { - Poco::URI uri(Request->getURI()); - Parameters_ = uri.getQueryParameters(); - InitQueryBlock(); - } - - inline static bool is_number(const std::string &s) { - return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); - } - - inline static bool is_bool(const std::string &s) { - if (s == "true" || s == "false") - return true; - return false; - } - - [[nodiscard]] inline uint64_t GetParameter(const std::string &Name, const uint64_t Default) { - auto Hint = std::find_if(Parameters_.begin(),Parameters_.end(),[&](const std::pair &S){ return S.first==Name; }); - if(Hint==Parameters_.end() || !is_number(Hint->second)) - return Default; - return std::stoull(Hint->second); - } - - [[nodiscard]] inline bool GetBoolParameter(const std::string &Name, bool Default=false) { - auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair &S){ return S.first==Name; }); - if(Hint==end(Parameters_) || !is_bool(Hint->second)) - return Default; - return Hint->second=="true"; - } - - [[nodiscard]] inline std::string GetParameter(const std::string &Name, const std::string &Default="") { - auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair &S){ return S.first==Name; }); - if(Hint==end(Parameters_)) - return Default; - return Hint->second; - } - - [[nodiscard]] inline bool HasParameter(const std::string &Name, std::string &Value) { - auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair &S){ return S.first==Name; }); - if(Hint==end(Parameters_)) - return false; - Value = Hint->second; - return true; - } - - [[nodiscard]] inline bool HasParameter(const std::string &Name, uint64_t & Value) { - auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair &S){ return S.first==Name; }); - if(Hint==end(Parameters_)) - return false; - Value = std::stoull(Hint->second); - return true; - } - - [[nodiscard]] inline const std::string & GetBinding(const std::string &Name, const std::string &Default="") { - auto E = Bindings_.find(Poco::toLower(Name)); - if (E == Bindings_.end()) - return Default; - - return E->second; - } - - [[nodiscard]] inline static std::string MakeList(const std::vector &L) { - std::string Return; - for (const auto &i : L) { - if (Return.empty()) - Return = i; - else - Return += ", " + i; - } - return Return; - } - - static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Types::UUIDvec_t & Value) { - if(O->has(Field) && O->isArray(Field)) { - auto Arr = O->getArray(Field); - for(const auto &i:*Arr) - Value.emplace_back(i.toString()); - return true; - } - return false; - } - - static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, std::string &Value) { - if(O->has(Field)) { - Value = O->get(Field).toString(); - return true; - } - return false; - } - - static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, uint64_t &Value) { - if(O->has(Field)) { - Value = O->get(Field); - return true; - } - return false; - } - - static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, bool &Value) { - if(O->has(Field)) { - Value = O->get(Field).toString()=="true"; - return true; - } - return false; - } - - static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, double &Value) { - if(O->has(Field)) { - Value = (double) O->get(Field); - return true; - } - return false; - } - - static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Poco::Data::BLOB &Value) { - if(O->has(Field)) { - std::string Content = O->get(Field).toString(); - auto DecodedBlob = Utils::base64decode(Content); - Value.assignRaw((const unsigned char *)&DecodedBlob[0],DecodedBlob.size()); - return true; - } - return false; - } - - - template bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, const T &value, T & assignee) { - if(O->has(Field)) { - assignee = value; - return true; - } - return false; - } - - inline void SetCommonHeaders(bool CloseConnection=false) { - Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1); - Response->setChunkedTransferEncoding(true); - Response->setContentType("application/json"); - auto Origin = Request->find("Origin"); - if (Origin != Request->end()) { - Response->set("Access-Control-Allow-Origin", Origin->second); - } else { - Response->set("Access-Control-Allow-Origin", "*"); - } - Response->set("Vary", "Origin, Accept-Encoding"); - if(CloseConnection) { - Response->set("Connection", "close"); - Response->setKeepAlive(false); - } else { - Response->setKeepAlive(true); - Response->set("Connection", "Keep-Alive"); - Response->set("Keep-Alive", "timeout=30, max=1000"); - } - } - - inline void ProcessOptions() { - Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1); - Response->setChunkedTransferEncoding(true); - auto Origin = Request->find("Origin"); - if (Origin != Request->end()) { - Response->set("Access-Control-Allow-Origin", Origin->second); - } else { - Response->set("Access-Control-Allow-Origin", "*"); - } - Response->set("Access-Control-Allow-Methods", MakeList(Methods_)); - auto RequestHeaders = Request->find("Access-Control-Request-Headers"); - if(RequestHeaders!=Request->end()) - Response->set("Access-Control-Allow-Headers", RequestHeaders->second); - Response->set("Vary", "Origin, Accept-Encoding"); - Response->set("Access-Control-Allow-Credentials", "true"); - Response->set("Access-Control-Max-Age", "86400"); - Response->set("Connection", "Keep-Alive"); - Response->set("Keep-Alive", "timeout=30, max=1000"); - - Response->setContentLength(0); - Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK); - Response->send(); - } - - inline void PrepareResponse(Poco::Net::HTTPResponse::HTTPStatus Status = Poco::Net::HTTPResponse::HTTP_OK, - bool CloseConnection = false) { - Response->setStatus(Status); - SetCommonHeaders(CloseConnection); - } - - inline void BadRequest(const OpenWifi::RESTAPI::Errors::msg &E, const std::string & Extra="") { - PrepareResponse(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST); - Poco::JSON::Object ErrorObject; - ErrorObject.set("ErrorCode",400); - ErrorObject.set("ErrorDetails",Request->getMethod()); - if(Extra.empty()) - ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; - else - ErrorObject.set("ErrorDescription",fmt::format("{}: {} ({})",E.err_num,E.err_txt, Extra)) ; - - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - } - -/* inline void BadRequest(uint64_t ErrorCode, const std::string & ErrorText) { - PrepareResponse(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST); - Poco::JSON::Object ErrorObject; - ErrorObject.set("ErrorCode", ErrorCode); - ErrorObject.set("ErrorDetails",Request->getMethod()); - ErrorObject.set("ErrorDescription", ErrorText) ; - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - } -*/ - inline void InternalError(const OpenWifi::RESTAPI::Errors::msg &E) { - PrepareResponse(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); - Poco::JSON::Object ErrorObject; - ErrorObject.set("ErrorCode",500); - ErrorObject.set("ErrorDetails",Request->getMethod()); - ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - } - - inline void UnAuthorized(const OpenWifi::RESTAPI::Errors::msg &E) { - PrepareResponse(Poco::Net::HTTPResponse::HTTP_FORBIDDEN); - Poco::JSON::Object ErrorObject; - ErrorObject.set("ErrorCode",E.err_num); - ErrorObject.set("ErrorDetails",Request->getMethod()); - ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - } - - inline void NotFound() { - PrepareResponse(Poco::Net::HTTPResponse::HTTP_NOT_FOUND); - Poco::JSON::Object ErrorObject; - ErrorObject.set("ErrorCode",404); - ErrorObject.set("ErrorDetails",Request->getMethod()); - const auto & E = OpenWifi::RESTAPI::Errors::Error404; - ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - poco_debug(Logger_,fmt::format("RES-NOTFOUND: User='{}@{}' Method='{}' Path='{}", - UserInfo_.userinfo.email, - Utils::FormatIPv6(Request->clientAddress().toString()), - Request->getMethod(), - Request->getURI())); - } - - inline void OK() { - PrepareResponse(); - if( Request->getMethod()==Poco::Net::HTTPRequest::HTTP_DELETE || - Request->getMethod()==Poco::Net::HTTPRequest::HTTP_OPTIONS) { - Response->send(); - } else { - Poco::JSON::Object ErrorObject; - ErrorObject.set("Code", 0); - ErrorObject.set("Operation", Request->getMethod()); - ErrorObject.set("Details", "Command completed."); - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - } - } - - inline void SendCompressedTarFile(const std::string & FileName, const std::string & Content) { - Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); - SetCommonHeaders(); - Response->set("Content-Type","application/gzip"); - Response->set("Content-Disposition", "attachment; filename=" + FileName ); - Response->set("Content-Transfer-Encoding","binary"); - Response->set("Accept-Ranges", "bytes"); - Response->set("Cache-Control", "no-store"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK); - Response->setContentLength(Content.size()); - Response->setChunkedTransferEncoding(true); - std::ostream& OutputStream = Response->send(); - OutputStream << Content; - } - - inline void SendFile(Poco::File & File, const std::string & UUID) { - Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); - SetCommonHeaders(); - Response->set("Content-Type","application/octet-stream"); - Response->set("Content-Disposition", "attachment; filename=" + UUID ); - Response->set("Content-Transfer-Encoding","binary"); - Response->set("Accept-Ranges", "bytes"); - Response->set("Cache-Control", "no-store"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - Response->setContentLength(File.getSize()); - Response->sendFile(File.path(),"application/octet-stream"); - } - - inline void SendFile(Poco::File & File) { - Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); - SetCommonHeaders(); - Poco::Path P(File.path()); - auto MT = Utils::FindMediaType(File); - if(MT.Encoding==Utils::BINARY) { - Response->set("Content-Transfer-Encoding","binary"); - Response->set("Accept-Ranges", "bytes"); - } - Response->set("Cache-Control", "no-store"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - Response->sendFile(File.path(),MT.ContentType); - } - - inline void SendFile(Poco::TemporaryFile &TempAvatar, [[maybe_unused]] const std::string &Type, const std::string & Name) { - Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); - SetCommonHeaders(); - auto MT = Utils::FindMediaType(Name); - if(MT.Encoding==Utils::BINARY) { - Response->set("Content-Transfer-Encoding","binary"); - Response->set("Accept-Ranges", "bytes"); - } - Response->set("Content-Disposition", "attachment; filename=" + Name ); - Response->set("Accept-Ranges", "bytes"); - Response->set("Cache-Control", "no-store"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - Response->setContentLength(TempAvatar.getSize()); - Response->sendFile(TempAvatar.path(),MT.ContentType); - } - - inline void SendFileContent(const std::string &Content, const std::string &Type, const std::string & Name) { - Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); - SetCommonHeaders(); - auto MT = Utils::FindMediaType(Name); - if(MT.Encoding==Utils::BINARY) { - Response->set("Content-Transfer-Encoding","binary"); - Response->set("Accept-Ranges", "bytes"); - } - Response->set("Content-Disposition", "attachment; filename=" + Name ); - Response->set("Accept-Ranges", "bytes"); - Response->set("Cache-Control", "no-store"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - Response->setContentLength(Content.size()); - Response->setContentType(Type ); - auto & OutputStream = Response->send(); - OutputStream << Content ; - } - - inline void SendHTMLFileBack(Poco::File & File, - const Types::StringPairVec & FormVars) { - Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); - SetCommonHeaders(); - Response->set("Pragma", "private"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - std::string FormContent = Utils::LoadFile(File.path()); - Utils::ReplaceVariables(FormContent, FormVars); - Response->setContentLength(FormContent.size()); - Response->setChunkedTransferEncoding(true); - Response->setContentType("text/html"); - std::ostream& ostr = Response->send(); - ostr << FormContent; - } - - inline void ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status, bool CloseConnection=false) { - PrepareResponse(Status, CloseConnection); - if(Status == Poco::Net::HTTPResponse::HTTP_NO_CONTENT) { - Response->setContentLength(0); - Response->erase("Content-Type"); - Response->setChunkedTransferEncoding(false); - } - Response->send(); - } - - inline bool ContinueProcessing() { - if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) { - ProcessOptions(); - return false; - } else if (std::find(Methods_.begin(), Methods_.end(), Request->getMethod()) == Methods_.end()) { - BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod); - return false; - } - - return true; - } - - inline bool IsAuthorized(bool & Expired, bool & Contacted, bool SubOnly = false ); - - inline void ReturnObject(Poco::JSON::Object &Object) { - PrepareResponse(); - if(Request!= nullptr) { - // can we compress ??? - auto AcceptedEncoding = Request->find("Accept-Encoding"); - if(AcceptedEncoding!=Request->end()) { - if( AcceptedEncoding->second.find("gzip")!=std::string::npos || - AcceptedEncoding->second.find("compress")!=std::string::npos) { - Response->set("Content-Encoding", "gzip"); - std::ostream &Answer = Response->send(); - Poco::DeflatingOutputStream deflater(Answer, Poco::DeflatingStreamBuf::STREAM_GZIP); - Poco::JSON::Stringifier::stringify(Object, deflater); - deflater.close(); - return; - } - } - } - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(Object, Answer); - } - - inline void ReturnCountOnly(uint64_t Count) { - Poco::JSON::Object Answer; - Answer.set("count", Count); - ReturnObject(Answer); - } - - inline bool InitQueryBlock() { - if(QueryBlockInitialized_) - return true; - QueryBlockInitialized_=true; - QB_.SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, ""); - QB_.StartDate = GetParameter(RESTAPI::Protocol::STARTDATE, 0); - QB_.EndDate = GetParameter(RESTAPI::Protocol::ENDDATE, 0); - QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 0); - QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100); - QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, ""); - QB_.Lifetime = GetBoolParameter(RESTAPI::Protocol::LIFETIME,false); - QB_.LogType = GetParameter(RESTAPI::Protocol::LOGTYPE,0); - QB_.LastOnly = GetBoolParameter(RESTAPI::Protocol::LASTONLY,false); - QB_.Newest = GetBoolParameter(RESTAPI::Protocol::NEWEST,false); - QB_.CountOnly = GetBoolParameter(RESTAPI::Protocol::COUNTONLY,false); - QB_.AdditionalInfo = GetBoolParameter(RESTAPI::Protocol::WITHEXTENDEDINFO,false); - - auto RawSelect = GetParameter(RESTAPI::Protocol::SELECT, ""); - - auto Entries = Poco::StringTokenizer(RawSelect,","); - for(const auto &i:Entries) { - QB_.Select.emplace_back(i); - } - if(QB_.Offset<1) - QB_.Offset=0; - return true; - } - - [[nodiscard]] inline uint64_t Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default=0){ - if(Obj->has(Parameter)) - return Obj->get(Parameter); - return Default; - } - - [[nodiscard]] inline std::string GetS(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, const std::string & Default=""){ - if(Obj->has(Parameter)) - return Obj->get(Parameter).toString(); - return Default; - } - - [[nodiscard]] inline bool GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default=false){ - if(Obj->has(Parameter)) - return Obj->get(Parameter).toString()=="true"; - return Default; - } - - [[nodiscard]] inline uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj) { - return RESTAPIHandler::Get(RESTAPI::Protocol::WHEN, Obj); - } - - template void ReturnObject(const char *Name, const std::vector & Objects) { - Poco::JSON::Object Answer; - RESTAPI_utils::field_to_json(Answer,Name,Objects); - ReturnObject(Answer); - } - - Poco::Logger & Logger() { return Logger_; } - - virtual void DoGet() = 0 ; - virtual void DoDelete() = 0 ; - virtual void DoPost() = 0 ; - virtual void DoPut() = 0 ; - - Poco::Net::HTTPServerRequest *Request= nullptr; - Poco::Net::HTTPServerResponse *Response= nullptr; - SecurityObjects::UserInfoAndPolicy UserInfo_; - QueryBlock QB_; - const std::string & Requester() const { return REST_Requester_; } - protected: - BindingMap Bindings_; - Poco::URI::QueryParameters Parameters_; - Poco::Logger &Logger_; - std::string SessionToken_; - std::vector Methods_; - bool Internal_=false; - bool RateLimited_=false; - bool QueryBlockInitialized_=false; - bool SubOnlyService_=false; - bool AlwaysAuthorize_=true; - Poco::JSON::Parser IncomingParser_; - RESTAPI_GenericServer & Server_; - RateLimit MyRates_; - uint64_t TransactionId_; - Poco::JSON::Object::Ptr ParsedBody_; - std::string REST_Requester_; - }; - - class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { - public: - RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) - : RESTAPIHandler(bindings, L, std::vector{}, Server, TransactionId, Internal) {} - inline void DoGet() override {}; - inline void DoPost() override {}; - inline void DoPut() override {}; - inline void DoDelete() override {}; - }; - - template - constexpr auto test_has_PathName_method(T*) - -> decltype( T::PathName() , std::true_type{} ) - { - return std::true_type{}; - } - constexpr auto test_has_PathName_method(...) -> std::false_type - { - return std::false_type{}; - } - - template - RESTAPIHandler * RESTAPI_Router(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, - Poco::Logger & Logger, RESTAPI_GenericServer & Server, uint64_t TransactionId) { - static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method."); - if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) { - return new T(Bindings, Logger, Server, TransactionId, false); - } - - if constexpr (sizeof...(Args) == 0) { - return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server, TransactionId, false); - } else { - return RESTAPI_Router(RequestedPath, Bindings, Logger, Server, TransactionId); - } - } - - template - RESTAPIHandler * RESTAPI_Router_I(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, - Poco::Logger & Logger, RESTAPI_GenericServer & Server, uint64_t TransactionId) { - static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method."); - if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) { - return new T(Bindings, Logger, Server, TransactionId, true ); - } - - if constexpr (sizeof...(Args) == 0) { - return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server, TransactionId, true); - } else { - return RESTAPI_Router_I(RequestedPath, Bindings, Logger, Server, TransactionId); - } - } - - class OpenAPIRequestGet { - public: - explicit OpenAPIRequestGet( const std::string & Type, - const std::string & EndPoint, - const Types::StringPairVec & QueryData, - uint64_t msTimeout): - Type_(Type), - EndPoint_(EndPoint), - QueryData_(QueryData), - msTimeout_(msTimeout) {}; - inline Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = ""); - private: - std::string Type_; - std::string EndPoint_; - Types::StringPairVec QueryData_; - uint64_t msTimeout_; - }; - - class OpenAPIRequestPut { - public: - explicit OpenAPIRequestPut( const std::string & Type, - const std::string & EndPoint, - const Types::StringPairVec & QueryData, - const Poco::JSON::Object & Body, - uint64_t msTimeout): - Type_(Type), - EndPoint_(EndPoint), - QueryData_(QueryData), - msTimeout_(msTimeout), - Body_(Body){}; - - inline Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = ""); - - private: - std::string Type_; - std::string EndPoint_; - Types::StringPairVec QueryData_; - uint64_t msTimeout_; - Poco::JSON::Object Body_; - }; - - class OpenAPIRequestPost { - public: - explicit OpenAPIRequestPost( const std::string & Type, - const std::string & EndPoint, - const Types::StringPairVec & QueryData, - const Poco::JSON::Object & Body, - uint64_t msTimeout): - Type_(Type), - EndPoint_(EndPoint), - QueryData_(QueryData), - msTimeout_(msTimeout), - Body_(Body){}; - inline Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = ""); - private: - std::string Type_; - std::string EndPoint_; - Types::StringPairVec QueryData_; - uint64_t msTimeout_; - Poco::JSON::Object Body_; - }; - - class OpenAPIRequestDelete { - public: - explicit OpenAPIRequestDelete( const std::string & Type, - const std::string & EndPoint, - const Types::StringPairVec & QueryData, - uint64_t msTimeout): - Type_(Type), - EndPoint_(EndPoint), - QueryData_(QueryData), - msTimeout_(msTimeout){}; - inline Poco::Net::HTTPServerResponse::HTTPStatus Do(const std::string & BearerToken = ""); - - private: - std::string Type_; - std::string EndPoint_; - Types::StringPairVec QueryData_; - uint64_t msTimeout_; - Poco::JSON::Object Body_; - }; - - class KafkaMessage: public Poco::Notification { - public: - KafkaMessage( const std::string &Topic, const std::string &Key, const std::string & Payload) : - Topic_(Topic), Key_(Key), Payload_(Payload) - { - - } - - inline const std::string & Topic() { return Topic_; } - inline const std::string & Key() { return Key_; } - inline const std::string & Payload() { return Payload_; } - - private: - std::string Topic_; - std::string Key_; - std::string Payload_; - - }; - - class KafkaProducer : public Poco::Runnable { - public: - - inline void run () override; - inline void Start() { - if(!Running_) { - Running_=true; - Worker_.start(*this); - } - } - - inline void Stop() { - if(Running_) { - Running_=false; - Queue_.wakeUpAll(); - Worker_.join(); - } - } - - inline void Produce(const std::string &Topic, const std::string &Key, const std::string &Payload) { - std::lock_guard G(Mutex_); - Queue_.enqueueNotification( new KafkaMessage(Topic,Key,Payload)); - } - - private: - std::recursive_mutex Mutex_; - Poco::Thread Worker_; - mutable std::atomic_bool Running_=false; - Poco::NotificationQueue Queue_; - }; - - class KafkaConsumer : public Poco::Runnable { - public: - inline void run() override; - - void Start() { - if(!Running_) { - Running_=true; - Worker_.start(*this); - } - } - - void Stop() { - if(Running_) { - Running_=false; - Worker_.wakeUp(); - Worker_.join(); - } - } - - private: - std::recursive_mutex Mutex_; - Poco::Thread Worker_; - mutable std::atomic_bool Running_=false; - }; - - class KafkaDispatcher : public Poco::Runnable { - public: - - inline void Start() { - if(!Running_) { - Running_=true; - Worker_.start(*this); - } - } - - inline void Stop() { - if(Running_) { - Running_=false; - Queue_.wakeUpAll(); - Worker_.join(); - } - } - - inline auto RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { - std::lock_guard G(Mutex_); - auto It = Notifiers_.find(Topic); - if(It == Notifiers_.end()) { - Types::TopicNotifyFunctionList L; - L.emplace(L.end(),std::make_pair(F,FunctionId_)); - Notifiers_[Topic] = std::move(L); - } else { - It->second.emplace(It->second.end(),std::make_pair(F,FunctionId_)); - } - return FunctionId_++; - } - - inline void UnregisterTopicWatcher(const std::string &Topic, int Id) { - std::lock_guard G(Mutex_); - auto It = Notifiers_.find(Topic); - if(It != Notifiers_.end()) { - Types::TopicNotifyFunctionList & L = It->second; - for(auto it=L.begin(); it!=L.end(); it++) - if(it->second == Id) { - L.erase(it); - break; - } - } - } - - void Dispatch(const std::string &Topic, const std::string &Key, const std::string &Payload) { - std::lock_guard G(Mutex_); - auto It = Notifiers_.find(Topic); - if(It!=Notifiers_.end()) { - Queue_.enqueueNotification(new KafkaMessage(Topic, Key, Payload)); - } - } - - inline void run() override { - Poco::AutoPtr Note(Queue_.waitDequeueNotification()); - Utils::SetThreadName("kafka:dispatch"); - while(Note && Running_) { - auto Msg = dynamic_cast(Note.get()); - if(Msg!= nullptr) { - auto It = Notifiers_.find(Msg->Topic()); - if (It != Notifiers_.end()) { - const auto & FL = It->second; - for(const auto &[CallbackFunc,_]:FL) { - CallbackFunc(Msg->Key(), Msg->Payload()); - } - } - } - Note = Queue_.waitDequeueNotification(); - } - } - - inline void Topics(std::vector &T) { - T.clear(); - for(const auto &[TopicName,_]:Notifiers_) - T.push_back(TopicName); - } - - private: - std::recursive_mutex Mutex_; - Types::NotifyTable Notifiers_; - Poco::Thread Worker_; - mutable std::atomic_bool Running_=false; - uint64_t FunctionId_=1; - Poco::NotificationQueue Queue_; - }; - - class KafkaManager : public SubSystemServer { - public: - - friend class KafkaConsumer; - friend class KafkaProducer; - - inline void initialize(Poco::Util::Application & self) override; - - static auto instance() { - static auto instance_ = new KafkaManager; - return instance_; - } - - inline int Start() override { - if(!KafkaEnabled_) - return 0; - ConsumerThr_.Start(); - ProducerThr_.Start(); - Dispatcher_.Start(); - return 0; - } - - inline void Stop() override { - if(KafkaEnabled_) { - poco_information(Logger(),"Stopping..."); - Dispatcher_.Stop(); - ProducerThr_.Stop(); - ConsumerThr_.Stop(); - poco_information(Logger(),"Stopped..."); - return; - } - } - - inline void PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage = true ) { - if(KafkaEnabled_) { - ProducerThr_.Produce(topic,key,WrapMessage ? WrapSystemId(PayLoad) : PayLoad); - } - } - - inline void Dispatch(const std::string &Topic, const std::string & Key, const std::string &Payload) { - Dispatcher_.Dispatch(Topic, Key, Payload); - } - - [[nodiscard]] inline std::string WrapSystemId(const std::string & PayLoad) { - return SystemInfoWrapper_ + PayLoad + "}"; - } - - [[nodiscard]] inline bool Enabled() const { return KafkaEnabled_; } - - inline uint64_t RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { - if(KafkaEnabled_) { - return Dispatcher_.RegisterTopicWatcher(Topic,F); - } else { - return 0; - } - } - - inline void UnregisterTopicWatcher(const std::string &Topic, uint64_t Id) { - if(KafkaEnabled_) { - Dispatcher_.UnregisterTopicWatcher(Topic, Id); - } - } - - inline void Topics(std::vector &T) { - Dispatcher_.Topics(T); - } - - private: - bool KafkaEnabled_ = false; - std::string SystemInfoWrapper_; - KafkaProducer ProducerThr_; - KafkaConsumer ConsumerThr_; - KafkaDispatcher Dispatcher_; - - inline void PartitionAssignment(const cppkafka::TopicPartitionList& partitions) { - Logger().information(fmt::format("Partition assigned: {}...", partitions.front().get_partition())); - } - inline void PartitionRevocation(const cppkafka::TopicPartitionList& partitions) { - Logger().information(fmt::format("Partition revocation: {}...",partitions.front().get_partition())); - } - - KafkaManager() noexcept: - SubSystemServer("KafkaManager", "KAFKA-SVR", "openwifi.kafka") - { - } - }; - - inline auto KafkaManager() { return KafkaManager::instance(); } - - class AuthClient : public SubSystemServer { - public: - explicit AuthClient() noexcept: - SubSystemServer("Authentication", "AUTH-CLNT", "authentication") - { - } - - static auto instance() { - static auto instance_ = new AuthClient; - return instance_; - } - - inline int Start() override { - return 0; - } - - inline void Stop() override { - poco_information(Logger(),"Stopping..."); - std::lock_guard G(Mutex_); - Cache_.clear(); - poco_information(Logger(),"Stopped..."); - } - - inline void RemovedCachedToken(const std::string &Token) { - Cache_.remove(Token); - } - - inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) { - return ((T.expires_in_+T.created_) < OpenWifi::Now()); - } - - inline bool RetrieveTokenInformation(const std::string & SessionToken, - SecurityObjects::UserInfoAndPolicy & UInfo, - std::uint64_t TID, - bool & Expired, bool & Contacted, bool Sub=false) { - try { - Types::StringPairVec QueryData; - QueryData.push_back(std::make_pair("token",SessionToken)); - OpenAPIRequestGet Req( uSERVICE_SECURITY, - Sub ? "/api/v1/validateSubToken" : "/api/v1/validateToken", - QueryData, - 10000); - Poco::JSON::Object::Ptr Response; - - auto StatusCode = Req.Do(Response); - if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT) { - Contacted = false; - return false; - } - - Contacted = true; - if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_OK) { - if(Response->has("tokenInfo") && Response->has("userInfo")) { - UInfo.from_json(Response); - if(IsTokenExpired(UInfo.webtoken)) { - Expired = true; - return false; - } - Expired = false; - Cache_.update(SessionToken, UInfo); - return true; - } else { - return false; - } - } - } catch (...) { - poco_error(Logger(),fmt::format("Failed to retrieve token={} for TID={}", SessionToken, TID)); - } - Expired = false; - return false; - } - - inline bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, - std::uint64_t TID, - bool & Expired, bool & Contacted, bool Sub = false) { - auto User = Cache_.get(SessionToken); - if(!User.isNull()) { - if(IsTokenExpired(User->webtoken)) { - Expired = true; - return false; - } - Expired = false; - UInfo = *User; - return true; - } - return RetrieveTokenInformation(SessionToken, UInfo, TID, Expired, Contacted, Sub); - } - - private: - Poco::ExpireLRUCache Cache_{512,1200000 }; - }; - - inline auto AuthClient() { return AuthClient::instance(); } - - class ALBRequestHandler: public Poco::Net::HTTPRequestHandler - /// Return a HTML document with the current date and time. - { - public: - explicit ALBRequestHandler(Poco::Logger & L, uint64_t id) - : Logger_(L), id_(id) - { - } - - void handleRequest([[maybe_unused]] Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) override - { - Utils::SetThreadName("alb-request"); - try { - if((id_ % 100) == 0) { - poco_debug(Logger_,fmt::format("ALB-REQUEST({}): ALB Request {}.", - Request.clientAddress().toString(), id_)); - } - Response.setChunkedTransferEncoding(true); - Response.setContentType("text/html"); - Response.setDate(Poco::Timestamp()); - Response.setStatus(Poco::Net::HTTPResponse::HTTP_OK); - Response.setKeepAlive(true); - Response.set("Connection", "keep-alive"); - Response.setVersion(Poco::Net::HTTPMessage::HTTP_1_1); - std::ostream &Answer = Response.send(); - Answer << "process Alive and kicking!"; - } catch (...) { - - } - } - - private: - Poco::Logger & Logger_; - uint64_t id_; - }; - - class ALBRequestHandlerFactory: public Poco::Net::HTTPRequestHandlerFactory - { - public: - explicit ALBRequestHandlerFactory(Poco::Logger & L): - Logger_(L) - { - } - - ALBRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request) override - { - if (request.getURI() == "/") - return new ALBRequestHandler(Logger_, req_id_++); - else - return nullptr; - } - - private: - Poco::Logger &Logger_; - inline static std::atomic_uint64_t req_id_=1; - }; - - class ALBHealthCheckServer : public SubSystemServer { - public: - ALBHealthCheckServer() noexcept: - SubSystemServer("ALBHealthCheckServer", "ALB-SVR", "alb") - { - } - - static auto instance() { - static auto instance = new ALBHealthCheckServer; - return instance; - } - - inline int Start() override; - - inline void Stop() override { - poco_information(Logger(),"Stopping..."); - if(Running_) - Server_->stopAll(true); - poco_information(Logger(),"Stopped..."); - } - - private: - std::unique_ptr Server_; - std::unique_ptr Socket_; - int Port_ = 0; - mutable std::atomic_bool Running_=false; - }; - - inline auto ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); } - - Poco::Net::HTTPRequestHandler * RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, - Poco::Logger & L, RESTAPI_GenericServer & S, uint64_t Id); - - Poco::Net::HTTPRequestHandler * RESTAPI_IntRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, - Poco::Logger & L, RESTAPI_GenericServer & S, uint64_t Id); - - - class RESTAPI_ExtServer : public SubSystemServer { - public: - static auto instance() { - static auto instance_ = new RESTAPI_ExtServer; - return instance_; - } - int Start() override; - inline void Stop() override { - Logger().information("Stopping..."); - for( const auto & svr : RESTServers_ ) - svr->stopAll(true); - Pool_.stopAll(); - Pool_.joinAll(); - RESTServers_.clear(); - Logger().information("Stopped..."); - } - - - inline void reinitialize(Poco::Util::Application &self) override; - - inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) { - RESTAPIHandler::BindingMap Bindings; - Utils::SetThreadName(fmt::format("x-rest:{}",Id).c_str()); - return RESTAPI_ExtRouter(Path, Bindings, Logger(), Server_, Id); - } - const Poco::ThreadPool & Pool() { return Pool_; } - - private: - std::vector> RESTServers_; - Poco::ThreadPool Pool_{"x-rest",8,128}; - RESTAPI_GenericServer Server_; - - RESTAPI_ExtServer() noexcept: - SubSystemServer("RESTAPI_ExtServer", "REST-XSRV", "openwifi.restapi") - { - } - }; - - inline auto RESTAPI_ExtServer() { return RESTAPI_ExtServer::instance(); }; - - class ExtRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { - public: - ExtRequestHandlerFactory() = default; - inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { - try { - Poco::URI uri(Request.getURI()); - auto TID = NextTransactionId_++; - Utils::SetThreadName(fmt::format("x-rest:{}",TID).c_str()); - return RESTAPI_ExtServer()->CallServer(uri.getPath(), TID); - } catch (...) { - - } - return nullptr; - } - private: - static inline std::atomic_uint64_t NextTransactionId_ = 1; - }; - - class LogMuxer : public Poco::Channel { - public: - - inline std::string getProperty( [[maybe_unused]] const std::string &p ) const final { - return ""; - } - - inline void close() final { - } - - inline void open() final { - } - - inline static std::string to_string(Poco::Message::Priority p) { - switch(p) { - case Poco::Message::PRIO_INFORMATION: return "information"; - case Poco::Message::PRIO_CRITICAL: return "critical"; - case Poco::Message::PRIO_DEBUG: return "debug"; - case Poco::Message::PRIO_ERROR: return "error"; - case Poco::Message::PRIO_FATAL: return "level"; - case Poco::Message::PRIO_NOTICE: return "notice"; - case Poco::Message::PRIO_TRACE: return "trace"; - case Poco::Message::PRIO_WARNING: return "warning"; - default: return "none"; - } - } - - inline void log(const Poco::Message &m) final { - if(Enabled_) { - /* - nlohmann::json log_msg; - log_msg["msg"] = m.getText(); - log_msg["level"] = to_string(m.getPriority()); - log_msg["timestamp"] = Poco::DateTimeFormatter::format(m.getTime(), Poco::DateTimeFormat::ISO8601_FORMAT); - log_msg["source"] = m.getSource(); - log_msg["thread_name"] = m.getThread(); - log_msg["thread_id"] = m.getTid(); - - std::cout << log_msg << std::endl; - */ - std::lock_guard G(Mutex_); - std::vector Remove; - for(const auto &[Id,CallBack]:CallBacks_) { - try { - CallBack(m); - } catch (...) { - Remove.push_back(Id); - } - } - for(const auto &i:Remove) - CallBacks_.erase(i); - } - } - - inline void setProperty([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) final { - - } - - inline static auto instance() { - static auto instance_ = new LogMuxer; - return instance_; - } - inline void Enable(bool enable) { Enabled_ = enable; } - typedef std::function logmuxer_callback_func_t; - inline void RegisterCallback(const logmuxer_callback_func_t & R, uint64_t &Id) { - std::lock_guard G(Mutex_); - Id = CallBackId_++; - CallBacks_[Id] = R; - } - private: - std::recursive_mutex Mutex_; - std::map CallBacks_; - inline static uint64_t CallBackId_=1; - bool Enabled_ = false; - }; - inline auto LogMuxer() { return LogMuxer::instance(); } - - - class RESTAPI_IntServer : public SubSystemServer { - public: - static auto instance() { - static auto instance_ = new RESTAPI_IntServer; - return instance_; - } - - inline int Start() override; - inline void Stop() override { - Logger().information("Stopping..."); - for( const auto & svr : RESTServers_ ) - svr->stopAll(true); - Pool_.stopAll(); - Pool_.joinAll(); - Logger().information("Stopped..."); - } - - inline void reinitialize(Poco::Util::Application &self) override; - - inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) { - RESTAPIHandler::BindingMap Bindings; - Utils::SetThreadName(fmt::format("i-rest:{}",Id).c_str()); - return RESTAPI_IntRouter(Path, Bindings, Logger(), Server_, Id); - } - const Poco::ThreadPool & Pool() { return Pool_; } - private: - std::vector> RESTServers_; - Poco::ThreadPool Pool_{"i-rest",4,64}; - RESTAPI_GenericServer Server_; - - RESTAPI_IntServer() noexcept: - SubSystemServer("RESTAPI_IntServer", "REST-ISRV", "openwifi.internal.restapi") - { - } - }; - - inline auto RESTAPI_IntServer() { return RESTAPI_IntServer::instance(); }; - - class IntRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { - public: - inline IntRequestHandlerFactory() = default; - inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { - auto TID=NextTransactionId_++; - Utils::SetThreadName(fmt::format("i-rest:{}",TID).c_str()); - Poco::URI uri(Request.getURI()); - return RESTAPI_IntServer()->CallServer(uri.getPath(), TID); - } - private: - static inline std::atomic_uint64_t NextTransactionId_ = 1; - }; - - struct MicroServiceMeta { - uint64_t Id=0; - std::string Type; - std::string PrivateEndPoint; - std::string PublicEndPoint; - std::string AccessKey; - std::string Version; - uint64_t LastUpdate=0; - }; - - class SubSystemServer; - typedef std::map MicroServiceMetaMap; - typedef std::vector MicroServiceMetaVec; - typedef std::vector SubSystemVec; - class MicroService : public Poco::Util::ServerApplication { public: explicit MicroService( std::string PropFile, @@ -3268,6 +81,10 @@ namespace OpenWifi { // Logger_ = Poco::Logger::root().get("BASE-SVC"); } + inline void SaveConfig() { PropConfigurationFile_->save(ConfigFileName_); } + inline auto UpdateConfig() { return PropConfigurationFile_; } + inline bool NoAPISecurity() const { return NoAPISecurity_; } + inline Poco::ThreadPool & TimerPool() { return TimerPool_; } [[nodiscard]] std::string Version() { return Version_; } [[nodiscard]] inline const std::string & DataDir() { return DataDir_; } [[nodiscard]] inline const std::string & WWWAssetsDir() { return WWWAssetsDir_; } @@ -3283,103 +100,65 @@ namespace OpenWifi { static inline uint64_t GetPID() { return Poco::Process::id(); }; [[nodiscard]] inline const std::string GetPublicAPIEndPoint() { return MyPublicEndPoint_ + "/api/v1"; }; [[nodiscard]] inline const std::string & GetUIURI() const { return UIURI_;}; - [[nodiscard]] inline uint64_t Random(uint64_t ceiling) { - return (RandomEngine_() % ceiling); - } - + [[nodiscard]] inline uint64_t Random(uint64_t ceiling) { return (RandomEngine_() % ceiling); } [[nodiscard]] inline uint64_t Random(uint64_t min, uint64_t max) { return ((RandomEngine_() % (max-min)) + min); } - -/* inline Poco::Logger & GetLogger(const std::string &Name) { - static auto initialized = false; - - if(!initialized) { - initialized = true; - InitializeLoggingSystem(); - } - return Poco::Logger::get(Name); - } -*/ virtual void GetExtraConfiguration(Poco::JSON::Object & Cfg) { Cfg.set("additionalConfiguration",false); } - - - static inline void Exit(int Reason); - inline void BusMessageReceived(const std::string &Key, const std::string & Payload); - inline MicroServiceMetaVec GetServices(const std::string & Type); - inline MicroServiceMetaVec GetServices(); - inline void LoadConfigurationFile(); - inline void Reload(); - inline void LoadMyConfig(); - inline void initialize(Poco::Util::Application &self) override; - inline void uninitialize() override; - inline void reinitialize(Poco::Util::Application &self) override; - inline void defineOptions(Poco::Util::OptionSet &options) override; - inline void handleHelp(const std::string &name, const std::string &value); - inline void handleVersion(const std::string &name, const std::string &value); - inline void handleDebug(const std::string &name, const std::string &value); - inline void handleLogs(const std::string &name, const std::string &value); - inline void handleConfig(const std::string &name, const std::string &value); - inline void displayHelp(); - inline void InitializeSubSystemServers(); - inline void StartSubSystemServers(); - inline void StopSubSystemServers(); - [[nodiscard]] static inline std::string CreateUUID(); - inline bool SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level); - inline void Reload(const std::string &Sub); - inline Types::StringVec GetSubSystems() const; - inline Types::StringPairVec GetLogLevels(); - inline const Types::StringVec & GetLogLevelNames(); - inline uint64_t ConfigGetInt(const std::string &Key,uint64_t Default); - inline uint64_t ConfigGetInt(const std::string &Key); - inline uint64_t ConfigGetBool(const std::string &Key,bool Default); - inline uint64_t ConfigGetBool(const std::string &Key); - inline std::string ConfigGetString(const std::string &Key,const std::string & Default); - inline std::string ConfigGetString(const std::string &Key); - inline std::string ConfigPath(const std::string &Key,const std::string & Default); - inline std::string ConfigPath(const std::string &Key); - inline std::string Encrypt(const std::string &S); - inline std::string Decrypt(const std::string &S); - inline std::string MakeSystemEventMessage( const std::string & Type ) const; - [[nodiscard]] inline bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); - inline static void SavePID(); - inline int main(const ArgVec &args) override; static MicroService & instance() { return *instance_; } - inline void InitializeLoggingSystem(); - inline void SaveConfig() { PropConfigurationFile_->save(ConfigFileName_); } - inline auto UpdateConfig() { return PropConfigurationFile_; } - inline void AddActivity(const std::string &Activity) { - if(!DataDir_.empty()) { - std::string ActivityFile{ DataDir_ + "/activity.log"}; - try { - std::ofstream of(ActivityFile,std::ios_base::app | std::ios_base::out ); - auto t = std::chrono::system_clock::now(); - std::time_t now = std::chrono::system_clock::to_time_t(t); - of << Activity << " at " << std::ctime(&now) ; - } catch (...) { - } - } - } - inline bool NoAPISecurity() const { return NoAPISecurity_; } - [[nodiscard]] inline std::string Sign(Poco::JWT::Token &T, const std::string &Algo) { - if(NoBuiltInCrypto_) { - return T.toString(); - } else { - return Signer_.sign(T,Algo); - } - } + inline void Exit(int Reason); + void BusMessageReceived(const std::string &Key, const std::string & Payload); + Types::MicroServiceMetaVec GetServices(const std::string & Type); + Types::MicroServiceMetaVec GetServices(); + void LoadConfigurationFile(); + void Reload(); + void LoadMyConfig(); + void initialize(Poco::Util::Application &self) override; + void uninitialize() override; + void reinitialize(Poco::Util::Application &self) override; + void defineOptions(Poco::Util::OptionSet &options) override; + void handleHelp(const std::string &name, const std::string &value); + void handleVersion(const std::string &name, const std::string &value); + void handleDebug(const std::string &name, const std::string &value); + void handleLogs(const std::string &name, const std::string &value); + void handleConfig(const std::string &name, const std::string &value); + void displayHelp(); + void InitializeSubSystemServers(); + void StartSubSystemServers(); + void StopSubSystemServers(); + [[nodiscard]] static std::string CreateUUID(); + bool SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level); + void Reload(const std::string &Sub); + Types::StringVec GetSubSystems() const; + Types::StringPairVec GetLogLevels(); + const Types::StringVec & GetLogLevelNames(); + uint64_t ConfigGetInt(const std::string &Key,uint64_t Default); + uint64_t ConfigGetInt(const std::string &Key); + uint64_t ConfigGetBool(const std::string &Key,bool Default); + uint64_t ConfigGetBool(const std::string &Key); + std::string ConfigGetString(const std::string &Key,const std::string & Default); + std::string ConfigGetString(const std::string &Key); + std::string ConfigPath(const std::string &Key,const std::string & Default); + std::string ConfigPath(const std::string &Key); + std::string Encrypt(const std::string &S); + std::string Decrypt(const std::string &S); + std::string MakeSystemEventMessage( const std::string & Type ) const; + [[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); + static void SavePID(); + int main(const ArgVec &args) override; + void InitializeLoggingSystem(); - inline Poco::ThreadPool & TimerPool() { return TimerPool_; } + [[nodiscard]] std::string Sign(Poco::JWT::Token &T, const std::string &Algo); + void AddActivity(const std::string &Activity); private: static MicroService * instance_; bool HelpRequested_ = false; std::string LogDir_; std::string ConfigFileName_; - Poco::UUIDGenerator UUIDGenerator_; uint64_t ID_ = 1; Poco::SharedPtr AppKey_; bool DebugMode_ = false; @@ -3387,7 +166,7 @@ namespace OpenWifi { std::string WWWAssetsDir_; Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory(); Poco::Crypto::Cipher * Cipher_ = nullptr; - MicroServiceMetaMap Services_; + Types::MicroServiceMetaMap Services_; std::string MyHash_; std::string MyPrivateEndPoint_; std::string MyPublicEndPoint_; @@ -3407,2008 +186,9 @@ namespace OpenWifi { Poco::JWT::Signer Signer_; Poco::Logger &Logger_; Poco::ThreadPool TimerPool_{"timer:pool",2,32}; - std::unique_ptr BusEventManager_; + std::unique_ptr EventBusManager_; }; - inline void MicroService::Exit(int Reason) { - std::exit(Reason); - } - - inline void MicroService::BusMessageReceived([[maybe_unused]] const std::string &Key, const std::string & Payload) { - std::lock_guard G(InfraMutex_); - try { - Poco::JSON::Parser P; - auto Object = P.parse(Payload).extract(); - - if (Object->has(KafkaTopics::ServiceEvents::Fields::ID) && - Object->has(KafkaTopics::ServiceEvents::Fields::EVENT)) { - uint64_t ID = Object->get(KafkaTopics::ServiceEvents::Fields::ID); - auto Event = Object->get(KafkaTopics::ServiceEvents::Fields::EVENT).toString(); - if (ID != ID_) { - if( Event==KafkaTopics::ServiceEvents::EVENT_JOIN || - Event==KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE || - Event==KafkaTopics::ServiceEvents::EVENT_LEAVE ) { - if( Object->has(KafkaTopics::ServiceEvents::Fields::TYPE) && - Object->has(KafkaTopics::ServiceEvents::Fields::PUBLIC) && - Object->has(KafkaTopics::ServiceEvents::Fields::PRIVATE) && - Object->has(KafkaTopics::ServiceEvents::Fields::VRSN) && - Object->has(KafkaTopics::ServiceEvents::Fields::KEY)) { - auto PrivateEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(); - if (Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE && Services_.find(PrivateEndPoint) != Services_.end()) { - Services_[PrivateEndPoint].LastUpdate = OpenWifi::Now(); - } else if (Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) { - Services_.erase(PrivateEndPoint); - poco_debug(logger(),fmt::format("Service {} ID={} leaving system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); - } else if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN || Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE) { - poco_debug(logger(),fmt::format("Service {} ID={} joining system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); - Services_[PrivateEndPoint] = MicroServiceMeta{ - .Id = ID, - .Type = Poco::toLower(Object->get(KafkaTopics::ServiceEvents::Fields::TYPE).toString()), - .PrivateEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(), - .PublicEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PUBLIC).toString(), - .AccessKey = Object->get(KafkaTopics::ServiceEvents::Fields::KEY).toString(), - .Version = Object->get(KafkaTopics::ServiceEvents::Fields::VRSN).toString(), - .LastUpdate = OpenWifi::Now() }; - - std::string SvcList; - for (const auto &Svc: Services_) { - if(SvcList.empty()) - SvcList = Svc.second.Type; - else - SvcList += ", " + Svc.second.Type; - } - logger().information(fmt::format("Current list of microservices: {}", SvcList)); - } - } else { - poco_error(logger(),fmt::format("KAFKA-MSG: invalid event '{}', missing a field.",Event)); - } - } else if (Event==KafkaTopics::ServiceEvents::EVENT_REMOVE_TOKEN) { - if(Object->has(KafkaTopics::ServiceEvents::Fields::TOKEN)) { -#ifndef TIP_SECURITY_SERVICE - AuthClient()->RemovedCachedToken(Object->get(KafkaTopics::ServiceEvents::Fields::TOKEN).toString()); -#endif - } else { - poco_error(logger(),fmt::format("KAFKA-MSG: invalid event '{}', missing token",Event)); - } - } else { - poco_error(logger(),fmt::format("Unknown Event: {} Source: {}", Event, ID)); - } - } - } else { - poco_error(logger(),"Bad bus message."); - } - - auto i=Services_.begin(); - auto now = OpenWifi::Now(); - for(;i!=Services_.end();) { - if((now - i->second.LastUpdate)>60) { - i = Services_.erase(i); - } else - ++i; - } - - } catch (const Poco::Exception &E) { - logger().log(E); - } - } - - inline MicroServiceMetaVec MicroService::GetServices(const std::string & Type) { - std::lock_guard G(InfraMutex_); - - auto T = Poco::toLower(Type); - MicroServiceMetaVec Res; - for(const auto &[_,ServiceRec]:Services_) { - if(ServiceRec.Type==T) - Res.push_back(ServiceRec); - } - return Res; - } - - inline MicroServiceMetaVec MicroService::GetServices() { - std::lock_guard G(InfraMutex_); - - MicroServiceMetaVec Res; - for(const auto &[_,ServiceRec]:Services_) { - Res.push_back(ServiceRec); - } - return Res; - } - - inline void MicroService::LoadConfigurationFile() { - std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR,"."); - ConfigFileName_ = ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : ConfigFileName_; - Poco::Path ConfigFile(ConfigFileName_); - - if(!ConfigFile.isFile()) - { - std::cerr << DAEMON_APP_NAME << ": Configuration " - << ConfigFile.toString() << " does not seem to exist. Please set " + DAEMON_CONFIG_ENV_VAR - + " env variable the path of the " + DAEMON_PROPERTIES_FILENAME + " file." << std::endl; - std::exit(Poco::Util::Application::EXIT_CONFIG); - } - - // loadConfiguration(ConfigFile.toString()); - PropConfigurationFile_ = new Poco::Util::PropertyFileConfiguration(ConfigFile.toString()); - configPtr()->addWriteable(PropConfigurationFile_, PRIO_DEFAULT); - } - - inline void MicroService::Reload() { - LoadConfigurationFile(); - LoadMyConfig(); - } - - inline void MicroService::LoadMyConfig() { - NoAPISecurity_ = ConfigGetBool("openwifi.security.restapi.disable",false); - std::string KeyFile = ConfigPath("openwifi.service.key",""); - if(!KeyFile.empty()) { - std::string KeyFilePassword = ConfigPath("openwifi.service.key.password", ""); - AppKey_ = Poco::SharedPtr(new Poco::Crypto::RSAKey("", KeyFile, KeyFilePassword)); - Cipher_ = CipherFactory_.createCipher(*AppKey_); - Signer_.setRSAKey(AppKey_); - Signer_.addAllAlgorithms(); - NoBuiltInCrypto_ = false; - } else { - NoBuiltInCrypto_ = true; - } - - ID_ = Utils::GetSystemId(); - if(!DebugMode_) - DebugMode_ = ConfigGetBool("openwifi.system.debug",false); - MyPrivateEndPoint_ = ConfigGetString("openwifi.system.uri.private"); - MyPublicEndPoint_ = ConfigGetString("openwifi.system.uri.public"); - UIURI_ = ConfigGetString("openwifi.system.uri.ui"); - MyHash_ = Utils::ComputeHash(MyPublicEndPoint_); - } - - void MicroServicePostInitialization(); - - inline void MicroService::InitializeLoggingSystem() { - static auto initialized = false; - - if(!initialized) { - initialized = true; - LoadConfigurationFile(); - - auto LoggingDestination = MicroService::instance().ConfigGetString("logging.type", "file"); - auto LoggingFormat = MicroService::instance().ConfigGetString("logging.format", - "%Y-%m-%d %H:%M:%S.%i %s: [%p][thr:%I] %t"); - if (LoggingDestination == "console") { - Poco::AutoPtr Console(new Poco::ConsoleChannel); - Poco::AutoPtr Async(new Poco::AsyncChannel(Console)); - Poco::AutoPtr Formatter(new Poco::PatternFormatter); - Formatter->setProperty("pattern", LoggingFormat); - Poco::AutoPtr FormattingChannel( - new Poco::FormattingChannel(Formatter, Async)); - Poco::Logger::root().setChannel(FormattingChannel); - } else if (LoggingDestination == "colorconsole") { - Poco::AutoPtr Console(new Poco::ColorConsoleChannel); - Poco::AutoPtr Async(new Poco::AsyncChannel(Console)); - Poco::AutoPtr Formatter(new Poco::PatternFormatter); - Formatter->setProperty("pattern", LoggingFormat); - Poco::AutoPtr FormattingChannel( - new Poco::FormattingChannel(Formatter, Async)); - Poco::Logger::root().setChannel(FormattingChannel); - } else if (LoggingDestination == "sql") { - //"CREATE TABLE T_POCO_LOG (Source VARCHAR, Name VARCHAR, ProcessId INTEGER, Thread VARCHAR, ThreadId INTEGER, Priority INTEGER, Text VARCHAR, DateTime DATE)" - - } else if (LoggingDestination == "syslog") { - - } else { - auto LoggingLocation = - MicroService::instance().ConfigPath("logging.path", "$OWCERT_ROOT/logs") + "/log"; - - Poco::AutoPtr FileChannel(new Poco::FileChannel); - FileChannel->setProperty("rotation", "10 M"); - FileChannel->setProperty("archive", "timestamp"); - FileChannel->setProperty("path", LoggingLocation); - Poco::AutoPtr Async_File(new Poco::AsyncChannel(FileChannel)); - // Poco::AutoPtr Async_Muxer(new Poco::AsyncChannel(LogMuxer())); - // Poco::AutoPtr Splitter(new Poco::SplitterChannel); - // Splitter->addChannel(Async_File); - // Splitter->addChannel(Async_Muxer); - Poco::AutoPtr Formatter(new Poco::PatternFormatter); - Formatter->setProperty("pattern", LoggingFormat); - Poco::AutoPtr FormattingChannel( - new Poco::FormattingChannel(Formatter, Async_File)); - Poco::Logger::root().setChannel(FormattingChannel); - } - auto Level = Poco::Logger::parseLevel(MicroService::instance().ConfigGetString("logging.level", "debug")); - Poco::Logger::root().setLevel(Level); - } - } - - void DaemonPostInitialization(Poco::Util::Application &self); - - inline void MicroService::initialize(Poco::Util::Application &self) { - // add the default services - LoadConfigurationFile(); - InitializeLoggingSystem(); - - SubSystems_.push_back(KafkaManager()); - SubSystems_.push_back(ALBHealthCheckServer()); - SubSystems_.push_back(RESTAPI_ExtServer()); - SubSystems_.push_back(RESTAPI_IntServer()); -#ifndef TIP_SECURITY_SERVICE - SubSystems_.push_back(AuthClient()); -#endif - Poco::Net::initializeSSL(); - Poco::Net::HTTPStreamFactory::registerFactory(); - Poco::Net::HTTPSStreamFactory::registerFactory(); - Poco::Net::FTPStreamFactory::registerFactory(); - Poco::Net::FTPSStreamFactory::registerFactory(); - - Poco::File DataDir(ConfigPath("openwifi.system.data")); - DataDir_ = DataDir.path(); - if(!DataDir.exists()) { - try { - DataDir.createDirectory(); - } catch (const Poco::Exception &E) { - logger().log(E); - } - } - WWWAssetsDir_ = ConfigPath("openwifi.restapi.wwwassets",""); - if(WWWAssetsDir_.empty()) - WWWAssetsDir_ = DataDir_; - - LoadMyConfig(); - - InitializeSubSystemServers(); - ServerApplication::initialize(self); - DaemonPostInitialization(self); - - Types::TopicNotifyFunction F = [this](const std::string &Key,const std::string &Payload) { this->BusMessageReceived(Key, Payload); }; - KafkaManager()->RegisterTopicWatcher(KafkaTopics::SERVICE_EVENTS, F); - } - - inline void MicroService::uninitialize() { - // add your own uninitialization code here - ServerApplication::uninitialize(); - } - - inline void MicroService::reinitialize(Poco::Util::Application &self) { - ServerApplication::reinitialize(self); - // add your own reinitialization code here - } - - inline void MicroService::defineOptions(Poco::Util::OptionSet &options) { - ServerApplication::defineOptions(options); - - options.addOption( - Poco::Util::Option("help", "", "display help information on command line arguments") - .required(false) - .repeatable(false) - .callback(Poco::Util::OptionCallback(this, &MicroService::handleHelp))); - - options.addOption( - Poco::Util::Option("file", "", "specify the configuration file") - .required(false) - .repeatable(false) - .argument("file") - .callback(Poco::Util::OptionCallback(this, &MicroService::handleConfig))); - - options.addOption( - Poco::Util::Option("debug", "", "to run in debug, set to true") - .required(false) - .repeatable(false) - .callback(Poco::Util::OptionCallback(this, &MicroService::handleDebug))); - - options.addOption( - Poco::Util::Option("logs", "", "specify the log directory and file (i.e. dir/file.log)") - .required(false) - .repeatable(false) - .argument("dir") - .callback(Poco::Util::OptionCallback(this, &MicroService::handleLogs))); - - options.addOption( - Poco::Util::Option("version", "", "get the version and quit.") - .required(false) - .repeatable(false) - .callback(Poco::Util::OptionCallback(this, &MicroService::handleVersion))); - - } - - inline void MicroService::handleHelp([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) { - HelpRequested_ = true; - displayHelp(); - stopOptionsProcessing(); - } - - inline void MicroService::handleVersion([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) { - HelpRequested_ = true; - std::cout << Version() << std::endl; - stopOptionsProcessing(); - } - - inline void MicroService::handleDebug([[maybe_unused]] const std::string &name, const std::string &value) { - if(value == "true") - DebugMode_ = true ; - } - - inline void MicroService::handleLogs([[maybe_unused]] const std::string &name, const std::string &value) { - LogDir_ = value; - } - - inline void MicroService::handleConfig([[maybe_unused]] const std::string &name, const std::string &value) { - ConfigFileName_ = value; - } - - inline void MicroService::displayHelp() { - Poco::Util::HelpFormatter helpFormatter(options()); - helpFormatter.setCommand(commandName()); - helpFormatter.setUsage("OPTIONS"); - helpFormatter.setHeader("A " + DAEMON_APP_NAME + " implementation for TIP."); - helpFormatter.format(std::cout); - } - - inline void MicroService::InitializeSubSystemServers() { - for(auto i:SubSystems_) { - addSubsystem(i); - } - } - - inline void MicroService::StartSubSystemServers() { - AddActivity("Starting"); - for(auto i:SubSystems_) { - i->Start(); - } - BusEventManager_ = std::make_unique(Poco::Logger::create("BusEventManager",Poco::Logger::root().getChannel(),Poco::Logger::root().getLevel())); - BusEventManager_->Start(); - } - - inline void MicroService::StopSubSystemServers() { - AddActivity("Stopping"); - BusEventManager_->Stop(); - for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) { - (*i)->Stop(); - } - } - - [[nodiscard]] inline std::string MicroService::CreateUUID() { - static std::random_device rd; - static std::mt19937_64 gen(rd()); - static std::uniform_int_distribution<> dis(0, 15); - static std::uniform_int_distribution<> dis2(8, 11); - - std::stringstream ss; - int i; - ss << std::hex; - for (i = 0; i < 8; i++) { - ss << dis(gen); - } - ss << "-"; - for (i = 0; i < 4; i++) { - ss << dis(gen); - } - ss << "-4"; - for (i = 0; i < 3; i++) { - ss << dis(gen); - } - ss << "-"; - ss << dis2(gen); - for (i = 0; i < 3; i++) { - ss << dis(gen); - } - ss << "-"; - for (i = 0; i < 12; i++) { - ss << dis(gen); - }; - return ss.str(); - } - - inline bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) { - try { - auto P = Poco::Logger::parseLevel(Level); - auto Sub = Poco::toLower(SubSystem); - - if (Sub == "all") { - for (auto i : SubSystems_) { - i->Logger().setLevel(P); - } - return true; - } else { - for (auto i : SubSystems_) { - if (Sub == Poco::toLower(i->Name())) { - i->Logger().setLevel(P); - return true; - } - } - } - } catch (const Poco::Exception & E) { - std::cerr << "Exception" << std::endl; - } - return false; - } - - inline void MicroService::Reload(const std::string &Sub) { - for (auto i : SubSystems_) { - if (Poco::toLower(Sub) == Poco::toLower(i->Name())) { - i->reinitialize(Poco::Util::Application::instance()); - return; - } - } - } - - inline Types::StringVec MicroService::GetSubSystems() const { - Types::StringVec Result; - for(auto i:SubSystems_) - Result.push_back(Poco::toLower(i->Name())); - return Result; - } - - inline Types::StringPairVec MicroService::GetLogLevels() { - Types::StringPairVec Result; - - for(auto &i:SubSystems_) { - auto P = std::make_pair( i->Name(), Utils::LogLevelToString(i->GetLoggingLevel())); - Result.push_back(P); - } - return Result; - } - - inline const Types::StringVec & MicroService::GetLogLevelNames() { - static Types::StringVec LevelNames{"none", "fatal", "critical", "error", "warning", "notice", "information", "debug", "trace" }; - return LevelNames; - } - - inline uint64_t MicroService::ConfigGetInt(const std::string &Key,uint64_t Default) { - return (uint64_t) config().getInt64(Key,Default); - } - - inline uint64_t MicroService::ConfigGetInt(const std::string &Key) { - return config().getInt(Key); - } - - inline uint64_t MicroService::ConfigGetBool(const std::string &Key,bool Default) { - return config().getBool(Key,Default); - } - - inline uint64_t MicroService::ConfigGetBool(const std::string &Key) { - return config().getBool(Key); - } - - inline std::string MicroService::ConfigGetString(const std::string &Key,const std::string & Default) { - return config().getString(Key, Default); - } - - inline std::string MicroService::ConfigGetString(const std::string &Key) { - return config().getString(Key); - } - - inline std::string MicroService::ConfigPath(const std::string &Key,const std::string & Default) { - std::string R = config().getString(Key, Default); - return Poco::Path::expand(R); - } - - inline std::string MicroService::ConfigPath(const std::string &Key) { - std::string R = config().getString(Key); - return Poco::Path::expand(R); - } - - inline std::string MicroService::Encrypt(const std::string &S) { - if(NoBuiltInCrypto_) { - return S; - } - return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; - } - - inline std::string MicroService::Decrypt(const std::string &S) { - if(NoBuiltInCrypto_) { - return S; - } - return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; - } - - inline std::string MicroService::MakeSystemEventMessage( const std::string & Type ) const { - Poco::JSON::Object Obj; - Obj.set(KafkaTopics::ServiceEvents::Fields::EVENT,Type); - Obj.set(KafkaTopics::ServiceEvents::Fields::ID,ID_); - Obj.set(KafkaTopics::ServiceEvents::Fields::TYPE,Poco::toLower(DAEMON_APP_NAME)); - Obj.set(KafkaTopics::ServiceEvents::Fields::PUBLIC,MyPublicEndPoint_); - Obj.set(KafkaTopics::ServiceEvents::Fields::PRIVATE,MyPrivateEndPoint_); - Obj.set(KafkaTopics::ServiceEvents::Fields::KEY,MyHash_); - Obj.set(KafkaTopics::ServiceEvents::Fields::VRSN,Version_); - std::stringstream ResultText; - Poco::JSON::Stringifier::stringify(Obj, ResultText); - return ResultText.str(); - } - - [[nodiscard]] inline bool MicroService::IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request) { - try { - auto APIKEY = Request.get("X-API-KEY"); - return APIKEY == MyHash_; - } catch (const Poco::Exception &E) { - logger().log(E); - } - return false; - } - - inline void MicroService::SavePID() { - try { - std::ofstream O; - O.open(MicroService::instance().DataDir() + "/pidfile",std::ios::binary | std::ios::trunc); - O << Poco::Process::id(); - O.close(); - } catch (...) - { - std::cout << "Could not save system ID" << std::endl; - } - } - - inline SubSystemServer::SubSystemServer(const std::string &Name, const std::string &LoggingPrefix, - const std::string &SubSystemConfigPrefix): - Name_(Name), - LoggerPrefix_(LoggingPrefix), - SubSystemConfigPrefix_(SubSystemConfigPrefix) { - } - - inline int RESTAPI_ExtServer::Start() { - Logger().information("Starting."); - Server_.InitLogging(); - - for(const auto & Svr: ConfigServersList_) { - - if(MicroService::instance().NoAPISecurity()) { - Logger().information(fmt::format("Starting: {}:{}. Security has been disabled for APIs.", Svr.Address(), Svr.Port())); - } else { - Logger().information(fmt::format("Starting: {}:{} Keyfile:{} CertFile: {}", Svr.Address(), Svr.Port(), - Svr.KeyFile(),Svr.CertFile())); - Svr.LogCert(Logger()); - if (!Svr.RootCA().empty()) - Svr.LogCas(Logger()); - } - - Poco::Net::HTTPServerParams::Ptr Params = new Poco::Net::HTTPServerParams; - Params->setKeepAlive(true); - Params->setName("ws:xrest"); - - std::unique_ptr NewServer; - if(MicroService::instance().NoAPISecurity()) { - auto Sock{Svr.CreateSocket(Logger())}; - NewServer = std::make_unique(new ExtRequestHandlerFactory, Pool_, Sock, Params); - } else { - auto Sock{Svr.CreateSecureSocket(Logger())}; - NewServer = std::make_unique(new ExtRequestHandlerFactory, Pool_, Sock, Params); - }; - NewServer->start(); - RESTServers_.push_back(std::move(NewServer)); - } - return 0; - } - - inline int RESTAPI_IntServer::Start() { - Logger().information("Starting."); - Server_.InitLogging(); - - for(const auto & Svr: ConfigServersList_) { - - if(MicroService::instance().NoAPISecurity()) { - Logger().information(fmt::format("Starting: {}:{}. Security has been disabled for APIs.", Svr.Address(), Svr.Port())); - } else { - Logger().information(fmt::format("Starting: {}:{}. Keyfile:{} CertFile: {}", Svr.Address(), Svr.Port(), - Svr.KeyFile(),Svr.CertFile())); - Svr.LogCert(Logger()); - if (!Svr.RootCA().empty()) - Svr.LogCas(Logger()); - } - - auto Params = new Poco::Net::HTTPServerParams; - Params->setKeepAlive(true); - Params->setName("ws:irest"); - - std::unique_ptr NewServer; - if(MicroService::instance().NoAPISecurity()) { - auto Sock{Svr.CreateSocket(Logger())}; - NewServer = std::make_unique(new IntRequestHandlerFactory, Pool_, Sock, Params); - } else { - auto Sock{Svr.CreateSecureSocket(Logger())}; - NewServer = std::make_unique(new IntRequestHandlerFactory, Pool_, Sock, Params); - }; - NewServer->start(); - RESTServers_.push_back(std::move(NewServer)); - } - - return 0; - } - - inline int MicroService::main([[maybe_unused]] const ArgVec &args) { - MicroServiceErrorHandler ErrorHandler(*this); - Poco::ErrorHandler::set(&ErrorHandler); - - if (!HelpRequested_) { - SavePID(); - - Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME); - logger.notice(fmt::format("Starting {} version {}.",DAEMON_APP_NAME, Version())); - - if(Poco::Net::Socket::supportsIPv6()) - logger.information("System supports IPv6."); - else - logger.information("System does NOT support IPv6."); - - if (config().getBool("application.runAsDaemon", false)) { - logger.information("Starting as a daemon."); - } - - logger.information(fmt::format("System ID set to {}",ID_)); - StartSubSystemServers(); - waitForTerminationRequest(); - StopSubSystemServers(); - logger.notice(fmt::format("Stopped {}...",DAEMON_APP_NAME)); - } - - return Application::EXIT_OK; - } - - AppServiceRegistry::AppServiceRegistry() { - FileName = MicroService::instance().DataDir() + "/registry.json"; - Poco::File F(FileName); - - try { - if(F.exists()) { - std::ostringstream OS; - std::ifstream IF(FileName); - Poco::StreamCopier::copyStream(IF, OS); - Registry_ = nlohmann::json::parse(OS.str()); - } - } catch (...) { - Registry_ = nlohmann::json::parse("{}"); - } - } - - inline void SubSystemServer::initialize([[maybe_unused]] Poco::Util::Application &self) { - auto i = 0; - bool good = true; - - auto NewLevel = MicroService::instance().ConfigGetString("logging.level." + Name_, ""); - if(NewLevel.empty()) - Logger_ = std::make_unique(Poco::Logger::create(LoggerPrefix_, Poco::Logger::root().getChannel(), Poco::Logger::root().getLevel())); - else - Logger_ = std::make_unique(Poco::Logger::create(LoggerPrefix_, Poco::Logger::root().getChannel(), Poco::Logger::parseLevel(NewLevel))); - - ConfigServersList_.clear(); - while (good) { - std::string root{SubSystemConfigPrefix_ + ".host." + std::to_string(i) + "."}; - - std::string address{root + "address"}; - if (MicroService::instance().ConfigGetString(address, "").empty()) { - good = false; - } else { - std::string port{root + "port"}; - std::string key{root + "key"}; - std::string key_password{root + "key.password"}; - std::string cert{root + "cert"}; - std::string name{root + "name"}; - std::string backlog{root + "backlog"}; - std::string rootca{root + "rootca"}; - std::string issuer{root + "issuer"}; - std::string clientcas(root + "clientcas"); - std::string cas{root + "cas"}; - - std::string level{root + "security"}; - Poco::Net::Context::VerificationMode M = Poco::Net::Context::VERIFY_RELAXED; - - auto L = MicroService::instance().ConfigGetString(level, ""); - - if (L == "strict") { - M = Poco::Net::Context::VERIFY_STRICT; - } else if (L == "none") { - M = Poco::Net::Context::VERIFY_NONE; - } else if (L == "relaxed") { - M = Poco::Net::Context::VERIFY_RELAXED; - } else if (L == "once") - M = Poco::Net::Context::VERIFY_ONCE; - - PropertiesFileServerEntry entry(MicroService::instance().ConfigGetString(address, ""), - MicroService::instance().ConfigGetInt(port, 0), - MicroService::instance().ConfigPath(key, ""), - MicroService::instance().ConfigPath(cert, ""), - MicroService::instance().ConfigPath(rootca, ""), - MicroService::instance().ConfigPath(issuer, ""), - MicroService::instance().ConfigPath(clientcas, ""), - MicroService::instance().ConfigPath(cas, ""), - MicroService::instance().ConfigGetString(key_password, ""), - MicroService::instance().ConfigGetString(name, ""), M, - (int)MicroService::instance().ConfigGetInt(backlog, 64)); - ConfigServersList_.push_back(entry); - i++; - } - } - } - - inline int ALBHealthCheckServer::Start() { - if(MicroService::instance().ConfigGetBool("alb.enable",false)) { - Running_=true; - Port_ = (int)MicroService::instance().ConfigGetInt("alb.port",15015); - Socket_ = std::make_unique(Port_); - auto Params = new Poco::Net::HTTPServerParams; - Params->setName("ws:alb"); - Server_ = std::make_unique(new ALBRequestHandlerFactory(Logger()), *Socket_, Params); - Server_->start(); - } - - return 0; - } - - inline void BusEventManager::run() { - Running_ = true; - Utils::SetThreadName("fmwk:EventMgr"); - auto Msg = MicroService::instance().MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN); - KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroService::instance().PrivateEndPoint(),Msg, false); - while(Running_) { - Poco::Thread::trySleep((unsigned long)MicroService::instance().DaemonBusTimer()); - if(!Running_) - break; - Msg = MicroService::instance().MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE); - KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroService::instance().PrivateEndPoint(),Msg, false); - } - Msg = MicroService::instance().MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_LEAVE); - KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroService::instance().PrivateEndPoint(),Msg, false); - }; - - inline void BusEventManager::Start() { - if(KafkaManager()->Enabled()) { - Thread_.start(*this); - } - } - - inline void BusEventManager::Stop() { - if(KafkaManager()->Enabled()) { - poco_information(Logger(),"Stopping..."); - Running_ = false; - Thread_.wakeUp(); - Thread_.join(); - poco_information(Logger(),"Stopped..."); - } - } - - inline void KafkaManager::initialize(Poco::Util::Application & self) { - SubSystemServer::initialize(self); - KafkaEnabled_ = MicroService::instance().ConfigGetBool("openwifi.kafka.enable",false); - } - - inline void KafkaLoggerFun([[maybe_unused]] cppkafka::KafkaHandleBase & handle, int level, const std::string & facility, const std::string &message) { - switch ((cppkafka::LogLevel) level) { - case cppkafka::LogLevel::LogNotice: { - poco_notice(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); - } - break; - case cppkafka::LogLevel::LogDebug: { - poco_debug(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); - } - break; - case cppkafka::LogLevel::LogInfo: { - poco_information(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); - } - break; - case cppkafka::LogLevel::LogWarning: { - poco_warning(KafkaManager()->Logger(), fmt::format("kafka-log: facility: {} message: {}",facility, message)); - } - break; - case cppkafka::LogLevel::LogAlert: - case cppkafka::LogLevel::LogCrit: { - poco_critical(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); - } - break; - case cppkafka::LogLevel::LogErr: - case cppkafka::LogLevel::LogEmerg: - default: { - poco_error(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); - } - break; - } - } - - inline void KafkaErrorFun([[maybe_unused]] cppkafka::KafkaHandleBase & handle, int error, const std::string &reason) { - poco_error(KafkaManager()->Logger(),fmt::format("kafka-error: {}, reason: {}", error, reason)); - } - - inline void AddKafkaSecurity(cppkafka::Configuration & Config) { - auto CA = MicroService::instance().ConfigGetString("openwifi.kafka.ssl.ca.location",""); - auto Certificate = MicroService::instance().ConfigGetString("openwifi.kafka.ssl.certificate.location",""); - auto Key = MicroService::instance().ConfigGetString("openwifi.kafka.ssl.key.location",""); - auto Password = MicroService::instance().ConfigGetString("openwifi.kafka.ssl.key.password",""); - - if(CA.empty() || Certificate.empty() || Key.empty()) - return; - - Config.set("ssl.ca.location", CA); - Config.set("ssl.certificate.location", Certificate); - Config.set("ssl.key.location", Key); - if(!Password.empty()) - Config.set("ssl.key.password", Password); - } - - inline void KafkaProducer::run() { - - Utils::SetThreadName("Kafka:Prod"); - cppkafka::Configuration Config({ - { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, - { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") } - }); - - AddKafkaSecurity(Config); - - Config.set_log_callback(KafkaLoggerFun); - Config.set_error_callback(KafkaErrorFun); - - KafkaManager()->SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" + - std::to_string(MicroService::instance().ID()) + - R"lit( , "host" : ")lit" + MicroService::instance().PrivateEndPoint() + - R"lit(" } , "payload" : )lit" ; - - cppkafka::Producer Producer(Config); - Running_ = true; - - Poco::AutoPtr Note(Queue_.waitDequeueNotification()); - while(Note && Running_) { - try { - auto Msg = dynamic_cast(Note.get()); - if (Msg != nullptr) { - Producer.produce( - cppkafka::MessageBuilder(Msg->Topic()).key(Msg->Key()).payload(Msg->Payload())); - } - } catch (const cppkafka::HandleException &E) { - poco_warning(KafkaManager()->Logger(),fmt::format("Caught a Kafka exception (producer): {}", E.what())); - } catch( const Poco::Exception &E) { - KafkaManager()->Logger().log(E); - } catch (...) { - poco_error(KafkaManager()->Logger(),"std::exception"); - } - Note = Queue_.waitDequeueNotification(); - } - } - - inline void KafkaConsumer::run() { - Utils::SetThreadName("Kafka:Cons"); - - cppkafka::Configuration Config({ - { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, - { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") }, - { "group.id", MicroService::instance().ConfigGetString("openwifi.kafka.group.id") }, - { "enable.auto.commit", MicroService::instance().ConfigGetBool("openwifi.kafka.auto.commit",false) }, - { "auto.offset.reset", "latest" } , - { "enable.partition.eof", false } - }); - - AddKafkaSecurity(Config); - - Config.set_log_callback(KafkaLoggerFun); - Config.set_error_callback(KafkaErrorFun); - - cppkafka::TopicConfiguration topic_config = { - { "auto.offset.reset", "smallest" } - }; - - // Now configure it to be the default topic config - Config.set_default_topic_configuration(topic_config); - - cppkafka::Consumer Consumer(Config); - Consumer.set_assignment_callback([](cppkafka::TopicPartitionList& partitions) { - if(!partitions.empty()) { - KafkaManager()->Logger().information(fmt::format("Partition assigned: {}...", - partitions.front().get_partition())); - } - }); - Consumer.set_revocation_callback([](const cppkafka::TopicPartitionList& partitions) { - if(!partitions.empty()) { - KafkaManager()->Logger().information(fmt::format("Partition revocation: {}...", - partitions.front().get_partition())); - } - }); - - bool AutoCommit = MicroService::instance().ConfigGetBool("openwifi.kafka.auto.commit",false); - auto BatchSize = MicroService::instance().ConfigGetInt("openwifi.kafka.consumer.batchsize",20); - - Types::StringVec Topics; - KafkaManager()->Topics(Topics); - Consumer.subscribe(Topics); - - Running_ = true; - while(Running_) { - try { - std::vector MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(100)); - for(auto const &Msg:MsgVec) { - if (!Msg) - continue; - if (Msg.get_error()) { - if (!Msg.is_eof()) { - poco_error(KafkaManager()->Logger(),fmt::format("Error: {}", Msg.get_error().to_string())); - } - if(!AutoCommit) - Consumer.async_commit(Msg); - continue; - } - KafkaManager()->Dispatch(Msg.get_topic(), Msg.get_key(),Msg.get_payload() ); - if (!AutoCommit) - Consumer.async_commit(Msg); - } - } catch (const cppkafka::HandleException &E) { - poco_warning(KafkaManager()->Logger(),fmt::format("Caught a Kafka exception (consumer): {}", E.what())); - } catch (const Poco::Exception &E) { - KafkaManager()->Logger().log(E); - } catch (...) { - poco_error(KafkaManager()->Logger(),"std::exception"); - } - } - Consumer.unsubscribe(); - } - - inline void RESTAPI_ExtServer::reinitialize([[maybe_unused]] Poco::Util::Application &self) { - MicroService::instance().LoadConfigurationFile(); - Logger().information("Reinitializing."); - Stop(); - Start(); - } - - void RESTAPI_IntServer::reinitialize([[maybe_unused]] Poco::Util::Application &self) { - MicroService::instance().LoadConfigurationFile(); - Logger().information("Reinitializing."); - Stop(); - Start(); - } - - class RESTAPI_system_command : public RESTAPIHandler { - public: - RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) - : RESTAPIHandler(bindings, L, - std::vector{Poco::Net::HTTPRequest::HTTP_POST, - Poco::Net::HTTPRequest::HTTP_GET, - Poco::Net::HTTPRequest::HTTP_OPTIONS}, - Server, - TransactionId, - Internal) {} - static auto PathName() { return std::list{"/api/v1/system"};} - - inline void DoGet() { - std::string Arg; - if(HasParameter("command",Arg) && Arg=="info") { - Poco::JSON::Object Answer; - Answer.set(RESTAPI::Protocol::VERSION, MicroService::instance().Version()); - Answer.set(RESTAPI::Protocol::UPTIME, MicroService::instance().uptime().totalSeconds()); - Answer.set(RESTAPI::Protocol::START, MicroService::instance().startTime().epochTime()); - Answer.set(RESTAPI::Protocol::OS, Poco::Environment::osName()); - Answer.set(RESTAPI::Protocol::PROCESSORS, Poco::Environment::processorCount()); - Answer.set(RESTAPI::Protocol::HOSTNAME, Poco::Environment::nodeName()); - Answer.set(RESTAPI::Protocol::UI, MicroService::instance().GetUIURI()); - - Poco::JSON::Array Certificates; - auto SubSystems = MicroService::instance().GetFullSubSystems(); - std::set CertNames; - - for(const auto &i:SubSystems) { - auto Hosts=i->HostSize(); - for(uint64_t j=0;jHost(j).CertFile(); - if(!CertFileName.empty()) { - Poco::File F1(CertFileName); - if(F1.exists()) { - auto InsertResult = CertNames.insert(CertFileName); - if(InsertResult.second) { - Poco::JSON::Object Inner; - Poco::Path F(CertFileName); - Inner.set("filename", F.getFileName()); - Poco::Crypto::X509Certificate C(CertFileName); - auto ExpiresOn = C.expiresOn(); - Inner.set("expiresOn", ExpiresOn.timestamp().epochTime()); - Certificates.add(Inner); - } - } - } - } - } - Answer.set("certificates", Certificates); - return ReturnObject(Answer); - } - if(GetBoolParameter("extraConfiguration")) { - Poco::JSON::Object Answer; - MicroService::instance().GetExtraConfiguration(Answer); - return ReturnObject(Answer); - } - BadRequest(RESTAPI::Errors::InvalidCommand); - } - - inline void DoPost() final { - const auto & Obj = ParsedBody_; - if (Obj->has(RESTAPI::Protocol::COMMAND)) { - auto Command = Poco::toLower(Obj->get(RESTAPI::Protocol::COMMAND).toString()); - if (Command == RESTAPI::Protocol::SETLOGLEVEL) { - if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) && - Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) { - auto ParametersBlock = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS); - for (const auto &i : *ParametersBlock) { - Poco::JSON::Parser pp; - auto InnerObj = pp.parse(i).extract(); - if (InnerObj->has(RESTAPI::Protocol::TAG) && - InnerObj->has(RESTAPI::Protocol::VALUE)) { - auto Name = GetS(RESTAPI::Protocol::TAG, InnerObj); - auto Value = GetS(RESTAPI::Protocol::VALUE, InnerObj); - MicroService::instance().SetSubsystemLogLevel(Name, Value); - Logger_.information( - fmt::format("Setting log level for {} at {}", Name, Value)); - } - } - return OK(); - } - } else if (Command == RESTAPI::Protocol::GETLOGLEVELS) { - auto CurrentLogLevels = MicroService::instance().GetLogLevels(); - Poco::JSON::Object Result; - Poco::JSON::Array Array; - for (auto &[Name, Level] : CurrentLogLevels) { - Poco::JSON::Object Pair; - Pair.set(RESTAPI::Protocol::TAG, Name); - Pair.set(RESTAPI::Protocol::VALUE, Level); - Array.add(Pair); - } - Result.set(RESTAPI::Protocol::TAGLIST, Array); - return ReturnObject(Result); - } else if (Command == RESTAPI::Protocol::GETLOGLEVELNAMES) { - Poco::JSON::Object Result; - Poco::JSON::Array LevelNamesArray; - const Types::StringVec &LevelNames = MicroService::instance().GetLogLevelNames(); - for (const auto &i : LevelNames) - LevelNamesArray.add(i); - Result.set(RESTAPI::Protocol::LIST, LevelNamesArray); - return ReturnObject(Result); - } else if (Command == RESTAPI::Protocol::GETSUBSYSTEMNAMES) { - Poco::JSON::Object Result; - Poco::JSON::Array LevelNamesArray; - const Types::StringVec &SubSystemNames = MicroService::instance().GetSubSystems(); - for (const auto &i : SubSystemNames) - LevelNamesArray.add(i); - Result.set(RESTAPI::Protocol::LIST, LevelNamesArray); - return ReturnObject(Result); - } else if (Command == RESTAPI::Protocol::STATS) { - - } else if (Command == RESTAPI::Protocol::RELOAD) { - if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) && - Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) { - auto SubSystems = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS); - std::vector Names; - for (const auto &i : *SubSystems) - Names.push_back(i.toString()); - std::thread ReloadThread([Names](){ - std::this_thread::sleep_for(10000ms); - for(const auto &i:Names) { - if(i=="daemon") - MicroService::instance().Reload(); - else - MicroService::instance().Reload(i); - } - }); - ReloadThread.detach(); - } - return OK(); - } - } else { - return BadRequest(RESTAPI::Errors::InvalidCommand); - } - BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); - } - - void DoPut() final {}; - void DoDelete() final {}; - }; - - inline Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestGet::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) { - try { - auto Services = MicroService::instance().GetServices(Type_); - for(auto const &Svc:Services) { - Poco::URI URI(Svc.PrivateEndPoint); - - auto Secure = (URI.getScheme() == "https"); - - URI.setPath(EndPoint_); - for (const auto &qp : QueryData_) - URI.addQueryParameter(qp.first, qp.second); - - std::string Path(URI.getPathAndQuery()); - Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET, - Path, - Poco::Net::HTTPMessage::HTTP_1_1); - - poco_debug(Poco::Logger::get("REST-CALLER-GET"),fmt::format(" {}", URI.toString())); - - if(BearerToken.empty()) { - Request.add("X-API-KEY", Svc.AccessKey); - Request.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); - } else { - // Authorization: Bearer ${token} - Request.add("Authorization", "Bearer " + BearerToken); - } - - if(Secure) { - Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); - Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); - - Session.sendRequest(Request); - - Poco::Net::HTTPResponse Response; - std::istream &is = Session.receiveResponse(Response); - if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } - return Response.getStatus(); - } else { - Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); - Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); - - Session.sendRequest(Request); - - Poco::Net::HTTPResponse Response; - std::istream &is = Session.receiveResponse(Response); - if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } - return Response.getStatus(); - } - } - } - catch (const Poco::Exception &E) - { - Poco::Logger::get("REST-CALLER-GET").log(E); - } - return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; - } - - inline Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestPut::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) { - try { - auto Services = MicroService::instance().GetServices(Type_); - for(auto const &Svc:Services) { - Poco::URI URI(Svc.PrivateEndPoint); - - auto Secure = (URI.getScheme() == "https"); - - URI.setPath(EndPoint_); - for (const auto &qp : QueryData_) - URI.addQueryParameter(qp.first, qp.second); - - poco_debug(Poco::Logger::get("REST-CALLER-PUT"),fmt::format("{}", URI.toString())); - - std::string Path(URI.getPathAndQuery()); - - Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_PUT, - Path, - Poco::Net::HTTPMessage::HTTP_1_1); - std::ostringstream obody; - Poco::JSON::Stringifier::stringify(Body_,obody); - - Request.setContentType("application/json"); - Request.setContentLength(obody.str().size()); - - if(BearerToken.empty()) { - Request.add("X-API-KEY", Svc.AccessKey); - Request.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); - } else { - // Authorization: Bearer ${token} - Request.add("Authorization", "Bearer " + BearerToken); - } - - if(Secure) { - Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); - Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); - - std::ostream &os = Session.sendRequest(Request); - os << obody.str(); - - Poco::Net::HTTPResponse Response; - std::istream &is = Session.receiveResponse(Response); - if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } else { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } - return Response.getStatus(); - } else { - Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); - Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); - - std::ostream &os = Session.sendRequest(Request); - os << obody.str(); - - Poco::Net::HTTPResponse Response; - std::istream &is = Session.receiveResponse(Response); - if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } else { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } - return Response.getStatus(); - } - } - } - catch (const Poco::Exception &E) - { - Poco::Logger::get("REST-CALLER-PUT").log(E); - } - return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; - } - - inline Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestPost::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) { - try { - auto Services = MicroService::instance().GetServices(Type_); - - for(auto const &Svc:Services) { - Poco::URI URI(Svc.PrivateEndPoint); - - - auto Secure = (URI.getScheme() == "https"); - - URI.setPath(EndPoint_); - for (const auto &qp : QueryData_) - URI.addQueryParameter(qp.first, qp.second); - - poco_debug(Poco::Logger::get("REST-CALLER-POST"),fmt::format(" {}", URI.toString())); - - std::string Path(URI.getPathAndQuery()); - - Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_POST, - Path, - Poco::Net::HTTPMessage::HTTP_1_1); - std::ostringstream obody; - Poco::JSON::Stringifier::stringify(Body_,obody); - - Request.setContentType("application/json"); - Request.setContentLength(obody.str().size()); - - if(BearerToken.empty()) { - Request.add("X-API-KEY", Svc.AccessKey); - Request.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); - } else { - // Authorization: Bearer ${token} - Request.add("Authorization", "Bearer " + BearerToken); - } - - if(Secure) { - Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); - Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); - std::ostream &os = Session.sendRequest(Request); - os << obody.str(); - - Poco::Net::HTTPResponse Response; - std::istream &is = Session.receiveResponse(Response); - if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } else { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } - return Response.getStatus(); - } else { - Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); - Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); - std::ostream &os = Session.sendRequest(Request); - os << obody.str(); - - Poco::Net::HTTPResponse Response; - std::istream &is = Session.receiveResponse(Response); - if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } else { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - } - return Response.getStatus(); - } - } - } - catch (const Poco::Exception &E) - { - Poco::Logger::get("REST-CALLER-POST").log(E); - } - return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; - } - - inline Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestDelete::Do(const std::string & BearerToken) { - try { - auto Services = MicroService::instance().GetServices(Type_); - - for(auto const &Svc:Services) { - Poco::URI URI(Svc.PrivateEndPoint); - - auto Secure = (URI.getScheme() == "https"); - - URI.setPath(EndPoint_); - for (const auto &qp : QueryData_) - URI.addQueryParameter(qp.first, qp.second); - - poco_debug(Poco::Logger::get("REST-CALLER-DELETE"),fmt::format(" {}", URI.toString())); - - std::string Path(URI.getPathAndQuery()); - - Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_DELETE, - Path, - Poco::Net::HTTPMessage::HTTP_1_1); - if(BearerToken.empty()) { - Request.add("X-API-KEY", Svc.AccessKey); - Request.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); - } else { - // Authorization: Bearer ${token} - Request.add("Authorization", "Bearer " + BearerToken); - } - - if(Secure) { - Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); - Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); - Session.sendRequest(Request); - Poco::Net::HTTPResponse Response; - Session.receiveResponse(Response); - return Response.getStatus(); - } else { - Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); - Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); - Session.sendRequest(Request); - Poco::Net::HTTPResponse Response; - Session.receiveResponse(Response); - return Response.getStatus(); - } - } - } - catch (const Poco::Exception &E) - { - Poco::Logger::get("REST-CALLER-DELETE").log(E); - } - return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; - } - - inline void RESTAPI_GenericServer::InitLogging() { - std::string Public = MicroService::instance().ConfigGetString("apilogging.public.methods","PUT,POST,DELETE"); - SetFlags(true, Public); - std::string Private = MicroService::instance().ConfigGetString("apilogging.private.methods","PUT,POST,DELETE"); - SetFlags(false, Private); - - std::string PublicBadTokens = MicroService::instance().ConfigGetString("apilogging.public.badtokens.methods",""); - LogBadTokens_[0] = (Poco::icompare(PublicBadTokens,"true")==0); - std::string PrivateBadTokens = MicroService::instance().ConfigGetString("apilogging.private.badtokens.methods",""); - LogBadTokens_[1] = (Poco::icompare(PrivateBadTokens,"true")==0); - } - -#ifdef TIP_SECURITY_SERVICE - [[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, std::uint64_t TID, bool & Expired , bool Sub ); -#endif - inline bool RESTAPIHandler::IsAuthorized( bool & Expired , [[maybe_unused]] bool & Contacted , bool Sub ) { - if(Internal_ && Request->has("X-INTERNAL-NAME")) { - auto Allowed = MicroService::instance().IsValidAPIKEY(*Request); - Contacted = true; - if(!Allowed) { - if(Server_.LogBadTokens(false)) { - poco_debug(Logger_,fmt::format("I-REQ-DENIED({}): TID={} Method={} Path={}", - Utils::FormatIPv6(Request->clientAddress().toString()), - TransactionId_, - Request->getMethod(), Request->getURI())); - } - } else { - auto Id = Request->get("X-INTERNAL-NAME", "unknown"); - REST_Requester_ = Id; - if(Server_.LogIt(Request->getMethod(),true)) { - poco_debug(Logger_,fmt::format("I-REQ-ALLOWED({}): TID={} User='{}' Method={} Path={}", - Utils::FormatIPv6(Request->clientAddress().toString()), - TransactionId_, - Id, - Request->getMethod(), Request->getURI())); - } - } - return Allowed; - } else { - if (SessionToken_.empty()) { - try { - Poco::Net::OAuth20Credentials Auth(*Request); - if (Auth.getScheme() == "Bearer") { - SessionToken_ = Auth.getBearerToken(); - } - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - } -#ifdef TIP_SECURITY_SERVICE - if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, TransactionId_, Expired, Sub)) { -#else - if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, TransactionId_, Expired, Contacted, Sub)) { -#endif - REST_Requester_ = UserInfo_.userinfo.email; - if(Server_.LogIt(Request->getMethod(),true)) { - poco_debug(Logger_,fmt::format("X-REQ-ALLOWED({}): TID={} User='{}@{}' Method={} Path={}", - UserInfo_.userinfo.email, - TransactionId_, - Utils::FormatIPv6(Request->clientAddress().toString()), - Request->clientAddress().toString(), - Request->getMethod(), - Request->getURI())); - } - return true; - } else { - if(Server_.LogBadTokens(true)) { - poco_debug(Logger_,fmt::format("X-REQ-DENIED({}): TID={} Method={} Path={}", - Utils::FormatIPv6(Request->clientAddress().toString()), - TransactionId_, - Request->getMethod(), - Request->getURI())); - } - } - return false; - } - } - inline MicroService * MicroService::instance_ = nullptr; - template struct WebSocketNotification { - inline static uint64_t xid=1; - uint64_t notification_id=++xid; - std::string type; - ContentStruct content; - - void to_json(Poco::JSON::Object &Obj) const; - bool from_json(const Poco::JSON::Object::Ptr &Obj); - }; - - template void WebSocketNotification::to_json(Poco::JSON::Object &Obj) const { - RESTAPI_utils::field_to_json(Obj,"notification_id",notification_id); - RESTAPI_utils::field_to_json(Obj,"type",type); - RESTAPI_utils::field_to_json(Obj,"content",content); - } - - template bool WebSocketNotification::from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - RESTAPI_utils::field_from_json(Obj,"notification_id",notification_id); - RESTAPI_utils::field_from_json(Obj,"content",content); - RESTAPI_utils::field_from_json(Obj,"type",type); - return true; - } catch(...) { - - } - return false; - } - - class WebSocketClientProcessor { - public: - virtual void Processor(const Poco::JSON::Object::Ptr &O, std::string &Answer, bool &Done ) = 0; - private: - }; - -/* class MyParallelSocketReactor { - public: - explicit MyParallelSocketReactor(uint32_t NumReactors = 8); - ~MyParallelSocketReactor(); - Poco::Net::SocketReactor &Reactor(); - private: - uint32_t NumReactors_; - Poco::Net::SocketReactor *Reactors_; - Poco::ThreadPool ReactorPool_; - }; -*/ - - class WebSocketClient; - - class WebSocketClientServer : public SubSystemServer, Poco::Runnable { - public: - static auto instance() { - static auto instance_ = new WebSocketClientServer; - return instance_; - } - - int Start() override; - void Stop() override; - void run() override; - // MyParallelSocketReactor &ReactorPool(); - Poco::Net::SocketReactor & Reactor() { return Reactor_; } - void NewClient(Poco::Net::WebSocket &WS, const std::string &Id, const std::string &UserName); - bool Register(WebSocketClient *Client, const std::string &Id); - void SetProcessor(WebSocketClientProcessor *F); - void UnRegister(const std::string &Id); - void SetUser(const std::string &Id, const std::string &UserId); - [[nodiscard]] inline bool GeoCodeEnabled() const { return GeoCodeEnabled_; } - [[nodiscard]] inline std::string GoogleApiKey() const { return GoogleApiKey_; } - [[nodiscard]] bool Send(const std::string &Id, const std::string &Payload); - - template bool - SendUserNotification(const std::string &userName, const WebSocketNotification &Notification) { - - Poco::JSON::Object Payload; - Notification.to_json(Payload); - Poco::JSON::Object Msg; - Msg.set("notification",Payload); - std::ostringstream OO; - Msg.stringify(OO); - - return SendToUser(userName,OO.str()); - } - - template void SendNotification(const WebSocketNotification &Notification) { - Poco::JSON::Object Payload; - Notification.to_json(Payload); - Poco::JSON::Object Msg; - Msg.set("notification",Payload); - std::ostringstream OO; - Msg.stringify(OO); - SendToAll(OO.str()); - } - - [[nodiscard]] bool SendToUser(const std::string &userName, const std::string &Payload); - void SendToAll(const std::string &Payload); - private: - mutable std::atomic_bool Running_ = false; - Poco::Thread Thr_; - // std::unique_ptr ReactorPool_; - Poco::Net::SocketReactor Reactor_; - Poco::Thread ReactorThread_; - bool GeoCodeEnabled_ = false; - std::string GoogleApiKey_; - std::map> Clients_; - WebSocketClientProcessor *Processor_ = nullptr; - WebSocketClientServer() noexcept; - }; - - inline auto WebSocketClientServer() { return WebSocketClientServer::instance(); } - - class WebSocketClient { - public: - explicit WebSocketClient(Poco::Net::WebSocket &WS, - const std::string &Id, - const std::string &UserName, - Poco::Logger &L, - WebSocketClientProcessor *Processor); - virtual ~WebSocketClient(); - [[nodiscard]] inline const std::string &Id(); - [[nodiscard]] Poco::Logger &Logger(); - inline bool Send(const std::string &Payload); - private: - std::unique_ptr WS_; - Poco::Net::SocketReactor &Reactor_; - std::string Id_; - std::string UserName_; - Poco::Logger &Logger_; - std::atomic_bool Authenticated_ = false; - SecurityObjects::UserInfoAndPolicy UserInfo_; - WebSocketClientProcessor *Processor_ = nullptr; - void OnSocketReadable(const Poco::AutoPtr &pNf); - void OnSocketShutdown(const Poco::AutoPtr &pNf); - void OnSocketError(const Poco::AutoPtr &pNf); - }; - - inline void WebSocketClientServer::NewClient(Poco::Net::WebSocket & WS, const std::string &Id, const std::string &UserName ) { - std::lock_guard G(Mutex_); - auto Client = new WebSocketClient(WS,Id,UserName,Logger(), Processor_); - Clients_[Id] = std::make_pair(Client,""); - } - - inline bool WebSocketClientServer::Register( WebSocketClient * Client, const std::string &Id) { - std::lock_guard G(Mutex_); - Clients_[Id] = std::make_pair(Client,""); - return true; - } - - inline void WebSocketClientServer::SetProcessor( WebSocketClientProcessor * F) { - Processor_ = F; - } - - inline void WebSocketClientServer::UnRegister(const std::string &Id) { - std::lock_guard G(Mutex_); - Clients_.erase(Id); - } - - inline void WebSocketClientServer::SetUser(const std::string &Id, const std::string &UserId) { - std::lock_guard G(Mutex_); - - auto it=Clients_.find(Id); - if(it!=Clients_.end()) { - Clients_[Id] = std::make_pair(it->second.first,UserId); - } - } - - [[nodiscard]] inline bool SendToUser(const std::string &userName, const std::string &Payload); - inline WebSocketClientServer::WebSocketClientServer() noexcept: - SubSystemServer("WebSocketClientServer", "UI-WSCLNT-SVR", "websocketclients") - { - } - - inline void WebSocketClientServer::run() { - Running_ = true ; - Utils::SetThreadName("ws:uiclnt-svr"); - while(Running_) { - Poco::Thread::trySleep(2000); - - if(!Running_) - break; - } - }; - - inline int WebSocketClientServer::Start() { - GoogleApiKey_ = MicroService::instance().ConfigGetString("google.apikey",""); - GeoCodeEnabled_ = !GoogleApiKey_.empty(); - // ReactorPool_ = std::make_unique(); - ReactorThread_.start(Reactor_); - Thr_.start(*this); - return 0; - }; - - inline void WebSocketClientServer::Stop() { - if(Running_) { - Reactor_.stop(); - ReactorThread_.join(); - Running_ = false; - Thr_.wakeUp(); - Thr_.join(); - } - }; - - - inline void WebSocketClient::OnSocketError([[maybe_unused]] const Poco::AutoPtr &pNf) { - delete this; - } - - inline bool WebSocketClientServer::Send(const std::string &Id, const std::string &Payload) { - std::lock_guard G(Mutex_); - - auto It = Clients_.find(Id); - if(It!=Clients_.end()) - return It->second.first->Send(Payload); - return false; - } - - inline bool WebSocketClientServer::SendToUser(const std::string &UserName, const std::string &Payload) { - std::lock_guard G(Mutex_); - uint64_t Sent=0; - - for(const auto &client:Clients_) { - if(client.second.second == UserName) { - try { - if (client.second.first->Send(Payload)) - Sent++; - } catch (...) { - return false; - } - } - } - return Sent>0; - } - - inline void WebSocketClientServer::SendToAll(const std::string &Payload) { - std::lock_guard G(Mutex_); - - for(const auto &client:Clients_) { - try { - client.second.first->Send(Payload); - } catch (...) { - - } - } - } - - inline void WebSocketClient::OnSocketReadable([[maybe_unused]] const Poco::AutoPtr &pNf) { - int flags; - int n; - bool Done=false; - try { - Poco::Buffer IncomingFrame(0); - n = WS_->receiveFrame(IncomingFrame, flags); - auto Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK; - - if (n == 0) { - poco_debug(Logger(),fmt::format("CLOSE({}): {} UI Client is closing WS connection.", Id_, UserName_)); - return delete this; - } - - switch (Op) { - case Poco::Net::WebSocket::FRAME_OP_PING: { - WS_->sendFrame("", 0, - (int)Poco::Net::WebSocket::FRAME_OP_PONG | - (int)Poco::Net::WebSocket::FRAME_FLAG_FIN); - } break; - case Poco::Net::WebSocket::FRAME_OP_PONG: { - } break; - case Poco::Net::WebSocket::FRAME_OP_CLOSE: { - poco_debug(Logger(),fmt::format("CLOSE({}): {} UI Client is closing WS connection.", Id_, UserName_)); - Done = true; - } break; - case Poco::Net::WebSocket::FRAME_OP_TEXT: { - IncomingFrame.append(0); - if (!Authenticated_) { - std::string Frame{IncomingFrame.begin()}; - auto Tokens = Utils::Split(Frame, ':'); - bool Expired = false, Contacted = false; - if (Tokens.size() == 2 && - AuthClient()->IsAuthorized(Tokens[1], UserInfo_, 0, Expired, Contacted)) { - Authenticated_ = true; - UserName_ = UserInfo_.userinfo.email; - poco_debug(Logger(),fmt::format("START({}): {} UI Client is starting WS connection.", Id_, UserName_)); - std::string S{"Welcome! Bienvenue! Bienvenidos!"}; - WS_->sendFrame(S.c_str(), S.size()); - WebSocketClientServer()->SetUser(Id_, UserInfo_.userinfo.email); - } else { - std::string S{"Invalid token. Closing connection."}; - WS_->sendFrame(S.c_str(), S.size()); - Done = true; - } - - } else { - try { - Poco::JSON::Parser P; - auto Obj = - P.parse(IncomingFrame.begin()).extract(); - std::string Answer; - if (Processor_ != nullptr) - Processor_->Processor(Obj, Answer, Done); - if (!Answer.empty()) - WS_->sendFrame(Answer.c_str(), (int)Answer.size()); - else { - WS_->sendFrame("{}", 2); - } - } catch (const Poco::JSON::JSONException &E) { - Logger().log(E); - Done=true; - } - } - } break; - default: { - } - } - } catch (...) { - Done=true; - } - - if(Done) { - delete this; - } - } - - inline void WebSocketClient::OnSocketShutdown([[maybe_unused]] const Poco::AutoPtr &pNf) { - delete this; - } - - - inline WebSocketClient::WebSocketClient( Poco::Net::WebSocket & WS , const std::string &Id, const std::string &UserName, Poco::Logger & L, WebSocketClientProcessor * Processor) : - Reactor_(WebSocketClientServer()->Reactor()), - Id_(Id), - UserName_(UserName), - Logger_(L), - Processor_(Processor) { - try { - WS_ = std::make_unique(WS); - Reactor_.addEventHandler(*WS_, - Poco::NObserver( - *this, &WebSocketClient::OnSocketReadable)); - Reactor_.addEventHandler(*WS_, - Poco::NObserver( - *this, &WebSocketClient::OnSocketShutdown)); - Reactor_.addEventHandler(*WS_, - Poco::NObserver( - *this, &WebSocketClient::OnSocketError)); - WS_->setNoDelay(true); - WS_->setKeepAlive(true); - WS_->setBlocking(false); - - } catch (...) { - delete this; - } - } - - inline WebSocketClient::~WebSocketClient() { - try { - WebSocketClientServer()->UnRegister(Id_); - Reactor_.removeEventHandler(*WS_, - Poco::NObserver(*this,&WebSocketClient::OnSocketReadable)); - Reactor_.removeEventHandler(*WS_, - Poco::NObserver(*this,&WebSocketClient::OnSocketShutdown)); - Reactor_.removeEventHandler(*WS_, - Poco::NObserver(*this,&WebSocketClient::OnSocketError)); - (*WS_).shutdown(); - (*WS_).close(); - } catch(...) { - - } - } - - [[nodiscard]] inline const std::string & WebSocketClient::Id() { - return Id_; - }; - - [[nodiscard]] inline Poco::Logger & WebSocketClient::Logger() { - return Logger_; - } - - [[nodiscard]] inline bool WebSocketClient::Send(const std::string &Payload) { - try { - WS_->sendFrame(Payload.c_str(),Payload.size()); - return true; - } catch (...) { - - } - return false; - } - - class RESTAPI_webSocketServer : public RESTAPIHandler { - public: - inline RESTAPI_webSocketServer(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) - : RESTAPIHandler(bindings, L, - std::vector{ Poco::Net::HTTPRequest::HTTP_GET, - Poco::Net::HTTPRequest::HTTP_OPTIONS}, - Server, TransactionId, Internal,false) {} - static auto PathName() { return std::list{"/api/v1/ws"};} - void DoGet() final; - void DoDelete() final {}; - void DoPost() final {}; - void DoPut() final {}; - private: - }; - - inline void RESTAPI_webSocketServer::DoGet() { - try - { - if(Request->find("Upgrade") != Request->end() && Poco::icompare((*Request)["Upgrade"], "websocket") == 0) { - try - { - Poco::Net::WebSocket WS(*Request, *Response); - auto Id = MicroService::CreateUUID(); - WebSocketClientServer()->NewClient(WS,Id,UserInfo_.userinfo.email); - } - catch (...) { - std::cout << "Cannot create websocket client..." << std::endl; - } - } - } catch(...) { - std::cout << "Cannot upgrade connection..." << std::endl; - } - } - - -} - -namespace OpenWifi::Utils { - [[nodiscard]] inline uint64_t GetSystemId() { - uint64_t ID=0; - if(!AppServiceRegistry().Get("systemid",ID)) { - return InitializeSystemId(); - } - return ID; - } -} - -namespace OpenWifi::CIDR { - - static bool cidr_match(const in_addr &addr, const in_addr &net, uint8_t bits) { - if (bits == 0) { - return true; - } - return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits))); - } - - static bool cidr6_match(const in6_addr &address, const in6_addr &network, uint8_t bits) { -#ifdef __linux__ - const uint32_t *a = address.s6_addr32; - const uint32_t *n = network.s6_addr32; -#else - const uint32_t *a = address.__u6_addr.__u6_addr32; - const uint32_t *n = network.__u6_addr.__u6_addr32; -#endif - int bits_whole, bits_incomplete; - bits_whole = bits >> 5; // number of whole u32 - bits_incomplete = bits & 0x1F; // number of bits in incomplete u32 - if (bits_whole) { - if (memcmp(a, n, bits_whole << 2)!=0) { - return false; - } - } - if (bits_incomplete) { - uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete)); - if ((a[bits_whole] ^ n[bits_whole]) & mask) { - return false; - } - } - return true; - } - - static bool ConvertStringToLong(const char *S, unsigned long &L) { - char *end; - L = std::strtol(S,&end,10); - return end != S; - } - - static bool CidrIPinRange(const Poco::Net::IPAddress &IP, const std::string &Range) { - Poco::StringTokenizer TimeTokens(Range,"/",Poco::StringTokenizer::TOK_TRIM); - - Poco::Net::IPAddress RangeIP; - if(Poco::Net::IPAddress::tryParse(TimeTokens[0],RangeIP)) { - if(TimeTokens.count()==2) { - if (RangeIP.family() == Poco::Net::IPAddress::IPv4) { - unsigned long MaskLength; - if (ConvertStringToLong(TimeTokens[1].c_str(), MaskLength)) { - return cidr_match(*static_cast(RangeIP.addr()), - *static_cast(IP.addr()), MaskLength); - } - } else if (RangeIP.family() == Poco::Net::IPAddress::IPv6) { - unsigned long MaskLength; - if (ConvertStringToLong(TimeTokens[1].c_str(), MaskLength)) { - return cidr6_match(*static_cast(RangeIP.addr()), - *static_cast(IP.addr()), MaskLength); - } - } - } - return false; - } - return false; - } - - // - // Ranges can be a single IP, of IP1-IP2, of A set of IPs: IP1,IP2,IP3, or a cidr IP/24 - // These can work for IPv6 too... - // - static bool ValidateRange(const std::string &R) { - - auto Tokens = Poco::StringTokenizer(R,"-"); - if(Tokens.count()==2) { - Poco::Net::IPAddress a,b; - if(!Poco::Net::IPAddress::tryParse(Tokens[0],a) && Poco::Net::IPAddress::tryParse(Tokens[1],b)) - return false; - return a.family() == b.family(); - } - - Tokens = Poco::StringTokenizer(R,","); - if(Tokens.count()>1) { - return std::all_of(Tokens.begin(), Tokens.end(), [](const std::string &A) { - Poco::Net::IPAddress a; - return Poco::Net::IPAddress::tryParse(A,a); - } ); - } - - Tokens = Poco::StringTokenizer(R,"/"); - if(Tokens.count()==2) { - Poco::Net::IPAddress a; - if(!Poco::Net::IPAddress::tryParse(Tokens[0],a)) - return false; - if(std::atoi(Tokens[1].c_str())==0) - return false; - return true; - } - - Poco::Net::IPAddress a; - return Poco::Net::IPAddress::tryParse(R,a); - } - - static bool IpInRange(const Poco::Net::IPAddress & target, const std::string & R) { - - auto Tokens = Poco::StringTokenizer(R,"-"); - if(Tokens.count()==2) { - auto a = Poco::Net::IPAddress::parse(Tokens[0]); - auto b = Poco::Net::IPAddress::parse(Tokens[1]); - if(target.family() != a.family()) - return false; - return (a<=target && b>=target); - } - - Tokens = Poco::StringTokenizer(R,","); - if(Tokens.count()>1) { - return std::any_of(Tokens.begin(), Tokens.end(), [target](const std::string &Element) { - return Poco::Net::IPAddress::parse(Element) == target ; }); - } - - Tokens = Poco::StringTokenizer(R,"/"); - if(Tokens.count()==2) { - return CidrIPinRange(target,R); - } - - return Poco::Net::IPAddress::parse(R)==target; - } - - [[nodiscard]] inline bool IpInRanges(const std::string &IP, const Types::StringVec &R) { - Poco::Net::IPAddress Target; - - if(!Poco::Net::IPAddress::tryParse(IP,Target)) - return false; - - return std::any_of(cbegin(R),cend(R),[Target](const std::string &i) { return IpInRange(Target,i); }); - } - - [[nodiscard]] inline bool ValidateIpRanges(const Types::StringVec & Ranges) { - return std::all_of(cbegin(Ranges), cend(Ranges), ValidateRange); - } } diff --git a/src/framework/MicroServiceExtra.h b/src/framework/MicroServiceExtra.h new file mode 100644 index 0000000..1e06e2a --- /dev/null +++ b/src/framework/MicroServiceExtra.h @@ -0,0 +1,132 @@ +// +// Created by stephane bourque on 2022-10-26. +// + +#pragma once + +#include +#include + +#include "Poco/BasicEvent.h" +#include "Poco/ExpireLRUCache.h" + +namespace OpenWifi { + class ConfigurationEntry { + public: + template explicit ConfigurationEntry(T def) : + Default_(def), + Current_(def){ + } + + template explicit ConfigurationEntry(T def, T cur, const std::string &Hint="") : + Default_(def), + Current_(cur), + Hint_(Hint){ + } + + inline ConfigurationEntry()=default; + inline ~ConfigurationEntry()=default; + + template explicit operator T () const { return std::get(Current_); } + inline ConfigurationEntry & operator=(const char *v) { Current_ = std::string(v); return *this;} + template ConfigurationEntry & operator=(T v) { Current_ = (T) v; return *this;} + + void reset() { + Current_ = Default_; + } + + private: + std::variant Default_, Current_; + std::string Hint_; + }; + inline std::string to_string(const ConfigurationEntry &v) { return (std::string) v; } + + typedef std::map ConfigurationMap_t; + + template class FIFO { + public: + explicit FIFO(uint32_t Size) : + Size_(Size) { + Buffer_ = new T [Size_]; + } + + ~FIFO() { + delete [] Buffer_; + } + + mutable Poco::BasicEvent Writable_; + mutable Poco::BasicEvent Readable_; + + inline bool Read(T &t) { + { + std::lock_guard M(Mutex_); + if (Write_ == Read_) { + return false; + } + + t = Buffer_[Read_++]; + if (Read_ == Size_) { + Read_ = 0; + } + Used_--; + } + bool flag = true; + Writable_.notify(this, flag); + return true; + } + + inline bool Write(const T &t) { + { + std::lock_guard M(Mutex_); + + Buffer_[Write_++] = t; + if (Write_ == Size_) { + Write_ = 0; + } + Used_++; + MaxEverUsed_ = std::max(Used_,MaxEverUsed_); + } + bool flag = true; + Readable_.notify(this, flag); + return false; + } + + inline bool isFull() { + std::lock_guard M(Mutex_); + return Used_==Buffer_->capacity(); + } + + inline auto MaxEverUser() const { return MaxEverUsed_; } + + private: + std::recursive_mutex Mutex_; + uint32_t Size_=0; + uint32_t Read_=0; + uint32_t Write_=0; + uint32_t Used_=0; + uint32_t MaxEverUsed_=0; + T * Buffer_ = nullptr; + }; + + template class RecordCache { + public: + explicit RecordCache( KeyType Record::* Q) : + MemberOffset(Q){ + }; + inline auto update(const Record &R) { + return Cache_.update(R.*MemberOffset, R); + } + inline auto get(const KeyType &K) { + return Cache_.get(K); + } + inline auto remove(const KeyType &K) { + return Cache_.remove(K); + } + inline auto remove(const Record &R) { + return Cache_.remove(R.*MemberOffset); + } + private: + KeyType Record::* MemberOffset; + Poco::ExpireLRUCache Cache_{Size,Expiry}; + }; +} \ No newline at end of file diff --git a/src/framework/MicroServiceFuncs.cpp b/src/framework/MicroServiceFuncs.cpp new file mode 100644 index 0000000..33b9f55 --- /dev/null +++ b/src/framework/MicroServiceFuncs.cpp @@ -0,0 +1,113 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "framework/MicroService.h" +#include "framework/MicroServiceFuncs.h" + +namespace OpenWifi { + const std::string &MicroServiceDataDirectory() { return MicroService::instance().DataDir(); } + + Types::MicroServiceMetaVec MicroServiceGetServices(const std::string &Type) { + return MicroService::instance().GetServices(Type); + } + + Types::MicroServiceMetaVec MicroServiceGetServices() { + return MicroService::instance().GetServices(); + } + + std::string MicroServicePublicEndPoint() { return MicroService::instance().PublicEndPoint(); } + + std::string MicroServiceConfigGetString(const std::string &Key, const std::string &DefaultValue) { + return MicroService::instance().ConfigGetString(Key, DefaultValue); + } + + bool MicroServiceConfigGetBool(const std::string &Key, bool DefaultValue) { + return MicroService::instance().ConfigGetBool(Key, DefaultValue); + } + + std::uint64_t MicroServiceConfigGetInt(const std::string &Key, std::uint64_t DefaultValue) { + return MicroService::instance().ConfigGetInt(Key, DefaultValue); + } + + std::string MicroServicePrivateEndPoint() { return MicroService::instance().PrivateEndPoint(); } + + std::uint64_t MicroServiceID() { return MicroService::instance().ID(); } + + bool MicroServiceIsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request) { + return MicroService::instance().IsValidAPIKEY(Request); + } + + bool MicroServiceNoAPISecurity() { return MicroService::instance().NoAPISecurity(); } + + void MicroServiceLoadConfigurationFile() { MicroService::instance().LoadConfigurationFile(); } + + void MicroServiceReload() { MicroService::instance().Reload(); } + + void MicroServiceReload(const std::string &Type) { MicroService::instance().Reload(Type); } + + const Types::StringVec MicroServiceGetLogLevelNames() { + return MicroService::instance().GetLogLevelNames(); + } + + const Types::StringVec MicroServiceGetSubSystems() { + return MicroService::instance().GetSubSystems(); + } + + Types::StringPairVec MicroServiceGetLogLevels() { return MicroService::instance().GetLogLevels(); } + + bool MicroServiceSetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) { + return MicroService::instance().SetSubsystemLogLevel(SubSystem, Level); + } + + void MicroServiceGetExtraConfiguration(Poco::JSON::Object &Answer) { + MicroService::instance().GetExtraConfiguration(Answer); + } + + std::string MicroServiceVersion() { return MicroService::instance().Version(); } + + std::uint64_t MicroServiceUptimeTotalSeconds() { + return MicroService::instance().uptime().totalSeconds(); + } + + std::uint64_t MicroServiceStartTimeEpochTime() { + return MicroService::instance().startTime().epochTime(); + } + + std::string MicroServiceGetUIURI() { return MicroService::instance().GetUIURI(); } + + const SubSystemVec MicroServiceGetFullSubSystems() { + return MicroService::instance().GetFullSubSystems(); + } + + std::string MicroServiceCreateUUID() { return MicroService::CreateUUID(); } + + std::uint64_t MicroServiceDaemonBusTimer() { return MicroService::instance().DaemonBusTimer(); } + + std::string MicroServiceMakeSystemEventMessage(const std::string &Type) { + return MicroService::instance().MakeSystemEventMessage(Type); + } + + Poco::ThreadPool &MicroServiceTimerPool() { return MicroService::instance().TimerPool(); } + + std::string MicroServiceConfigPath(const std::string &Key, + const std::string &DefaultValue) { + return MicroService::instance().ConfigPath(Key, DefaultValue); + } + + std::string MicroServiceWWWAssetsDir() { + return MicroService::instance().WWWAssetsDir(); + } + + std::uint64_t MicroServiceRandom(std::uint64_t Start,std::uint64_t End) { + return MicroService::instance().Random(Start, End); + } + + std::string MicroServiceSign(Poco::JWT::Token &T, const std::string &Algo) { + return MicroService::instance().Sign(T, Algo); + } + + std::string MicroServiceGetPublicAPIEndPoint() { + return MicroService::instance().GetPublicAPIEndPoint(); + } +} diff --git a/src/framework/MicroServiceFuncs.h b/src/framework/MicroServiceFuncs.h new file mode 100644 index 0000000..e0f9551 --- /dev/null +++ b/src/framework/MicroServiceFuncs.h @@ -0,0 +1,54 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include + +#include "framework/OpenWifiTypes.h" + +#include "Poco/Net/HTTPServerRequest.h" +#include "Poco/JSON/Object.h" +#include "Poco/ThreadPool.h" +#include "Poco/JWT/Token.h" + + +namespace OpenWifi { + class SubSystemServer; + using SubSystemVec=std::vector; + const std::string & MicroServiceDataDirectory(); + Types::MicroServiceMetaVec MicroServiceGetServices(const std::string & Type); + Types::MicroServiceMetaVec MicroServiceGetServices(); + std::string MicroServicePublicEndPoint(); + std::string MicroServiceConfigGetString(const std::string &Key, const std::string &DefaultValue); + bool MicroServiceConfigGetBool(const std::string &Key, bool DefaultValue); + std::uint64_t MicroServiceConfigGetInt(const std::string &Key, std::uint64_t DefaultValue); + std::string MicroServicePrivateEndPoint(); + std::uint64_t MicroServiceID(); + bool MicroServiceIsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); + bool MicroServiceNoAPISecurity(); + void MicroServiceLoadConfigurationFile(); + void MicroServiceReload(); + void MicroServiceReload(const std::string &Type); + const Types::StringVec MicroServiceGetLogLevelNames(); + const Types::StringVec MicroServiceGetSubSystems(); + Types::StringPairVec MicroServiceGetLogLevels(); + bool MicroServiceSetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level); + void MicroServiceGetExtraConfiguration(Poco::JSON::Object &Answer); + std::string MicroServiceVersion(); + std::uint64_t MicroServiceUptimeTotalSeconds(); + std::uint64_t MicroServiceStartTimeEpochTime(); + std::string MicroServiceGetUIURI(); + const SubSystemVec MicroServiceGetFullSubSystems(); + std::string MicroServiceCreateUUID(); + std::uint64_t MicroServiceDaemonBusTimer(); + std::string MicroServiceMakeSystemEventMessage( const std::string & Type ); + Poco::ThreadPool & MicroServiceTimerPool(); + std::string MicroServiceConfigPath(const std::string &Key, + const std::string &DefaultValue); + std::string MicroServiceWWWAssetsDir(); + std::uint64_t MicroServiceRandom(std::uint64_t Start,std::uint64_t End); + std::string MicroServiceSign(Poco::JWT::Token &T, const std::string &Algo); + std::string MicroServiceGetPublicAPIEndPoint(); +} diff --git a/src/framework/MicroServiceNames.h b/src/framework/MicroServiceNames.h new file mode 100644 index 0000000..7563e19 --- /dev/null +++ b/src/framework/MicroServiceNames.h @@ -0,0 +1,22 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include + +namespace OpenWifi { + + static const std::string uSERVICE_SECURITY{"owsec"}; + static const std::string uSERVICE_GATEWAY{"owgw"}; + static const std::string uSERVICE_FIRMWARE{ "owfms"}; + static const std::string uSERVICE_TOPOLOGY{ "owtopo"}; + static const std::string uSERVICE_PROVISIONING{ "owprov"}; + static const std::string uSERVICE_OWLS{ "owls"}; + static const std::string uSERVICE_SUBCRIBER{ "owsub"}; + static const std::string uSERVICE_INSTALLER{ "owinst"}; + static const std::string uSERVICE_ANALYTICS{ "owanalytics"}; + static const std::string uSERVICE_OWRRM{ "owrrm"}; + +} \ No newline at end of file diff --git a/src/framework/OpenAPIRequests.cpp b/src/framework/OpenAPIRequests.cpp new file mode 100644 index 0000000..2dffe22 --- /dev/null +++ b/src/framework/OpenAPIRequests.cpp @@ -0,0 +1,289 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "OpenAPIRequests.h" + +#include "Poco/Logger.h" +#include "Poco/URI.h" +#include "Poco/Net/HTTPRequest.h" +#include "Poco/Net/HTTPSClientSession.h" +#include "Poco/JSON/Parser.h" + +#include "fmt/format.h" +#include "framework/MicroServiceFuncs.h" + +namespace OpenWifi { + + Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestGet::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) { + try { + + auto Services = MicroServiceGetServices(Type_); + for(auto const &Svc:Services) { + Poco::URI URI(Svc.PrivateEndPoint); + + auto Secure = (URI.getScheme() == "https"); + + URI.setPath(EndPoint_); + for (const auto &qp : QueryData_) + URI.addQueryParameter(qp.first, qp.second); + + std::string Path(URI.getPathAndQuery()); + Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET, + Path, + Poco::Net::HTTPMessage::HTTP_1_1); + + poco_debug(Poco::Logger::get("REST-CALLER-GET"),fmt::format(" {}", URI.toString())); + + if(BearerToken.empty()) { + Request.add("X-API-KEY", Svc.AccessKey); + Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); + } else { + // Authorization: Bearer ${token} + Request.add("Authorization", "Bearer " + BearerToken); + } + + if(Secure) { + Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); + Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); + + Session.sendRequest(Request); + + Poco::Net::HTTPResponse Response; + std::istream &is = Session.receiveResponse(Response); + if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } + return Response.getStatus(); + } else { + Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); + Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); + + Session.sendRequest(Request); + + Poco::Net::HTTPResponse Response; + std::istream &is = Session.receiveResponse(Response); + if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } + return Response.getStatus(); + } + } + } + catch (const Poco::Exception &E) + { + Poco::Logger::get("REST-CALLER-GET").log(E); + } + return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; +} + + Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestPut::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) { + try { + auto Services = MicroServiceGetServices(Type_); + for(auto const &Svc:Services) { + Poco::URI URI(Svc.PrivateEndPoint); + + auto Secure = (URI.getScheme() == "https"); + + URI.setPath(EndPoint_); + for (const auto &qp : QueryData_) + URI.addQueryParameter(qp.first, qp.second); + + poco_debug(Poco::Logger::get("REST-CALLER-PUT"),fmt::format("{}", URI.toString())); + + std::string Path(URI.getPathAndQuery()); + + Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_PUT, + Path, + Poco::Net::HTTPMessage::HTTP_1_1); + std::ostringstream obody; + Poco::JSON::Stringifier::stringify(Body_,obody); + + Request.setContentType("application/json"); + Request.setContentLength(obody.str().size()); + + if(BearerToken.empty()) { + Request.add("X-API-KEY", Svc.AccessKey); + Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); + } else { + // Authorization: Bearer ${token} + Request.add("Authorization", "Bearer " + BearerToken); + } + + if(Secure) { + Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); + Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); + + std::ostream &os = Session.sendRequest(Request); + os << obody.str(); + + Poco::Net::HTTPResponse Response; + std::istream &is = Session.receiveResponse(Response); + if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } else { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } + return Response.getStatus(); + } else { + Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); + Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); + + std::ostream &os = Session.sendRequest(Request); + os << obody.str(); + + Poco::Net::HTTPResponse Response; + std::istream &is = Session.receiveResponse(Response); + if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } else { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } + return Response.getStatus(); + } + } + } + catch (const Poco::Exception &E) + { + Poco::Logger::get("REST-CALLER-PUT").log(E); + } + return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; +} + + Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestPost::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) { + try { + auto Services = MicroServiceGetServices(Type_); + + for(auto const &Svc:Services) { + Poco::URI URI(Svc.PrivateEndPoint); + + + auto Secure = (URI.getScheme() == "https"); + + URI.setPath(EndPoint_); + for (const auto &qp : QueryData_) + URI.addQueryParameter(qp.first, qp.second); + + poco_debug(Poco::Logger::get("REST-CALLER-POST"),fmt::format(" {}", URI.toString())); + + std::string Path(URI.getPathAndQuery()); + + Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_POST, + Path, + Poco::Net::HTTPMessage::HTTP_1_1); + std::ostringstream obody; + Poco::JSON::Stringifier::stringify(Body_,obody); + + Request.setContentType("application/json"); + Request.setContentLength(obody.str().size()); + + if(BearerToken.empty()) { + Request.add("X-API-KEY", Svc.AccessKey); + Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); + } else { + // Authorization: Bearer ${token} + Request.add("Authorization", "Bearer " + BearerToken); + } + + if(Secure) { + Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); + Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); + std::ostream &os = Session.sendRequest(Request); + os << obody.str(); + + Poco::Net::HTTPResponse Response; + std::istream &is = Session.receiveResponse(Response); + if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } else { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } + return Response.getStatus(); + } else { + Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); + Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); + std::ostream &os = Session.sendRequest(Request); + os << obody.str(); + + Poco::Net::HTTPResponse Response; + std::istream &is = Session.receiveResponse(Response); + if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } else { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + } + return Response.getStatus(); + } + } + } + catch (const Poco::Exception &E) + { + Poco::Logger::get("REST-CALLER-POST").log(E); + } + return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; +} + + Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestDelete::Do(const std::string & BearerToken) { + try { + auto Services = MicroServiceGetServices(Type_); + + for(auto const &Svc:Services) { + Poco::URI URI(Svc.PrivateEndPoint); + + auto Secure = (URI.getScheme() == "https"); + + URI.setPath(EndPoint_); + for (const auto &qp : QueryData_) + URI.addQueryParameter(qp.first, qp.second); + + poco_debug(Poco::Logger::get("REST-CALLER-DELETE"),fmt::format(" {}", URI.toString())); + + std::string Path(URI.getPathAndQuery()); + + Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_DELETE, + Path, + Poco::Net::HTTPMessage::HTTP_1_1); + if(BearerToken.empty()) { + Request.add("X-API-KEY", Svc.AccessKey); + Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); + } else { + // Authorization: Bearer ${token} + Request.add("Authorization", "Bearer " + BearerToken); + } + + if(Secure) { + Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); + Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); + Session.sendRequest(Request); + Poco::Net::HTTPResponse Response; + Session.receiveResponse(Response); + return Response.getStatus(); + } else { + Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); + Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); + Session.sendRequest(Request); + Poco::Net::HTTPResponse Response; + Session.receiveResponse(Response); + return Response.getStatus(); + } + } + } + catch (const Poco::Exception &E) + { + Poco::Logger::get("REST-CALLER-DELETE").log(E); + } + return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; +} + + +} // namespace OpenWifi \ No newline at end of file diff --git a/src/framework/OpenAPIRequests.h b/src/framework/OpenAPIRequests.h new file mode 100644 index 0000000..6460dfe --- /dev/null +++ b/src/framework/OpenAPIRequests.h @@ -0,0 +1,98 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include + +#include "Poco/JSON/Object.h" +#include "Poco/Net/HTTPServerResponse.h" + +#include "framework/OpenWifiTypes.h" + +namespace OpenWifi { + + class OpenAPIRequestGet { + public: + explicit OpenAPIRequestGet( const std::string & Type, + const std::string & EndPoint, + const Types::StringPairVec & QueryData, + uint64_t msTimeout): + Type_(Type), + EndPoint_(EndPoint), + QueryData_(QueryData), + msTimeout_(msTimeout) {}; + Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = ""); + private: + std::string Type_; + std::string EndPoint_; + Types::StringPairVec QueryData_; + uint64_t msTimeout_; + }; + + class OpenAPIRequestPut { + public: + explicit OpenAPIRequestPut( const std::string & Type, + const std::string & EndPoint, + const Types::StringPairVec & QueryData, + const Poco::JSON::Object & Body, + uint64_t msTimeout): + Type_(Type), + EndPoint_(EndPoint), + QueryData_(QueryData), + msTimeout_(msTimeout), + Body_(Body){}; + + Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = ""); + + private: + std::string Type_; + std::string EndPoint_; + Types::StringPairVec QueryData_; + uint64_t msTimeout_; + Poco::JSON::Object Body_; + }; + + class OpenAPIRequestPost { + public: + explicit OpenAPIRequestPost( const std::string & Type, + const std::string & EndPoint, + const Types::StringPairVec & QueryData, + const Poco::JSON::Object & Body, + uint64_t msTimeout): + Type_(Type), + EndPoint_(EndPoint), + QueryData_(QueryData), + msTimeout_(msTimeout), + Body_(Body){}; + Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = ""); + private: + std::string Type_; + std::string EndPoint_; + Types::StringPairVec QueryData_; + uint64_t msTimeout_; + Poco::JSON::Object Body_; + }; + + class OpenAPIRequestDelete { + public: + explicit OpenAPIRequestDelete( const std::string & Type, + const std::string & EndPoint, + const Types::StringPairVec & QueryData, + uint64_t msTimeout): + Type_(Type), + EndPoint_(EndPoint), + QueryData_(QueryData), + msTimeout_(msTimeout){}; + Poco::Net::HTTPServerResponse::HTTPStatus Do(const std::string & BearerToken = ""); + + private: + std::string Type_; + std::string EndPoint_; + Types::StringPairVec QueryData_; + uint64_t msTimeout_; + Poco::JSON::Object Body_; + }; + +} // namespace OpenWifi diff --git a/src/framework/OpenWifiTypes.h b/src/framework/OpenWifiTypes.h index f49edc0..9671d05 100644 --- a/src/framework/OpenWifiTypes.h +++ b/src/framework/OpenWifiTypes.h @@ -28,6 +28,19 @@ namespace OpenWifi::Types { typedef std::string UUID_t; typedef std::vector UUIDvec_t; typedef std::map> Counted3DMapSII; + + struct MicroServiceMeta { + uint64_t Id=0; + std::string Type; + std::string PrivateEndPoint; + std::string PublicEndPoint; + std::string AccessKey; + std::string Version; + uint64_t LastUpdate=0; + }; + + typedef std::map MicroServiceMetaMap; + typedef std::vector MicroServiceMetaVec; } namespace OpenWifi { diff --git a/src/framework/RESTAPI_ExtServer.cpp b/src/framework/RESTAPI_ExtServer.cpp new file mode 100644 index 0000000..979a850 --- /dev/null +++ b/src/framework/RESTAPI_ExtServer.cpp @@ -0,0 +1,27 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "framework/RESTAPI_ExtServer.h" + +namespace OpenWifi { + + Poco::Net::HTTPRequestHandler *ExtRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest &Request) { + try { + Poco::URI uri(Request.getURI()); + auto TID = NextTransactionId_++; + Utils::SetThreadName(fmt::format("x-rest:{}",TID).c_str()); + return RESTAPI_ExtServer()->CallServer(uri.getPath(), TID); + } catch (...) { + + } + return nullptr; + } + + Poco::Net::HTTPRequestHandler *RESTAPI_ExtServer::CallServer(const std::string &Path, uint64_t Id) { + RESTAPIHandler::BindingMap Bindings; + Utils::SetThreadName(fmt::format("x-rest:{}",Id).c_str()); + return RESTAPI_ExtRouter(Path, Bindings, Logger(), Server_, Id); + } + +} diff --git a/src/framework/RESTAPI_ExtServer.h b/src/framework/RESTAPI_ExtServer.h new file mode 100644 index 0000000..16b359a --- /dev/null +++ b/src/framework/RESTAPI_ExtServer.h @@ -0,0 +1,99 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "Poco/Net/HTTPServer.h" + +#include "framework/SubSystemServer.h" +#include "framework/RESTAPI_Handler.h" + +namespace OpenWifi { + + Poco::Net::HTTPRequestHandler * RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, + Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t Id); + + class ExtRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { + public: + ExtRequestHandlerFactory() = default; + Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override; + private: + static inline std::atomic_uint64_t NextTransactionId_ = 1; + }; + + class RESTAPI_ExtServer : public SubSystemServer { + public: + static auto instance() { + static auto instance_ = new RESTAPI_ExtServer; + return instance_; + } + + inline int Start() override { + Logger().information("Starting."); + Server_.InitLogging(); + + for(const auto & Svr: ConfigServersList_) { + + if(MicroServiceNoAPISecurity()) { + Logger().information(fmt::format("Starting: {}:{}. Security has been disabled for APIs.", Svr.Address(), Svr.Port())); + } else { + Logger().information(fmt::format("Starting: {}:{} Keyfile:{} CertFile: {}", Svr.Address(), Svr.Port(), + Svr.KeyFile(),Svr.CertFile())); + Svr.LogCert(Logger()); + if (!Svr.RootCA().empty()) + Svr.LogCas(Logger()); + } + + Poco::Net::HTTPServerParams::Ptr Params = new Poco::Net::HTTPServerParams; + Params->setKeepAlive(true); + Params->setName("ws:xrest"); + + std::unique_ptr NewServer; + if(MicroServiceNoAPISecurity()) { + auto Sock{Svr.CreateSocket(Logger())}; + NewServer = std::make_unique(new ExtRequestHandlerFactory, Pool_, Sock, Params); + } else { + auto Sock{Svr.CreateSecureSocket(Logger())}; + NewServer = std::make_unique(new ExtRequestHandlerFactory, Pool_, Sock, Params); + }; + NewServer->start(); + RESTServers_.push_back(std::move(NewServer)); + } + return 0; + } + + inline void Stop() override { + Logger().information("Stopping..."); + for( const auto & svr : RESTServers_ ) + svr->stopAll(true); + Pool_.stopAll(); + Pool_.joinAll(); + RESTServers_.clear(); + Logger().information("Stopped..."); + } + + inline void reinitialize([[maybe_unused]] Poco::Util::Application &self) override { + MicroServiceLoadConfigurationFile(); + Logger().information("Reinitializing."); + Stop(); + Start(); + } + + Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id); + const Poco::ThreadPool & Pool() { return Pool_; } + + private: + std::vector> RESTServers_; + Poco::ThreadPool Pool_{"x-rest",8,128}; + RESTAPI_GenericServerAccounting Server_; + + RESTAPI_ExtServer() noexcept: + SubSystemServer("RESTAPI_ExtServer", "REST-XSRV", "openwifi.restapi") + { + } + }; + + inline auto RESTAPI_ExtServer() { return RESTAPI_ExtServer::instance(); }; + +} \ No newline at end of file diff --git a/src/framework/RESTAPI_GWobjects.cpp b/src/framework/RESTAPI_GWobjects.cpp deleted file mode 100644 index 0e05f69..0000000 --- a/src/framework/RESTAPI_GWobjects.cpp +++ /dev/null @@ -1,299 +0,0 @@ -// -// License type: BSD 3-Clause License -// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE -// -// Created by Stephane Bourque on 2021-03-04. -// Arilia Wireless Inc. -// - -#include "Poco/JSON/Parser.h" -#include "Poco/JSON/Stringifier.h" - -#include "Daemon.h" -#ifdef TIP_GATEWAY_SERVICE -#include "DeviceRegistry.h" -#include "CapabilitiesCache.h" -#endif - -#include "RESTAPI_GWobjects.h" -#include "framework/MicroService.h" - -using OpenWifi::RESTAPI_utils::field_to_json; -using OpenWifi::RESTAPI_utils::field_from_json; -using OpenWifi::RESTAPI_utils::EmbedDocument; - -namespace OpenWifi::GWObjects { - - void Device::to_json(Poco::JSON::Object &Obj) const { - field_to_json(Obj,"serialNumber", SerialNumber); -#ifdef TIP_GATEWAY_SERVICE - field_to_json(Obj,"deviceType", CapabilitiesCache::instance()->GetPlatform(Compatible)); -#endif - field_to_json(Obj,"macAddress", MACAddress); - field_to_json(Obj,"manufacturer", Manufacturer); - field_to_json(Obj,"UUID", UUID); - EmbedDocument("configuration", Obj, Configuration); - field_to_json(Obj,"notes", Notes); - field_to_json(Obj,"createdTimestamp", CreationTimestamp); - field_to_json(Obj,"lastConfigurationChange", LastConfigurationChange); - field_to_json(Obj,"lastConfigurationDownload", LastConfigurationDownload); - field_to_json(Obj,"lastFWUpdate", LastFWUpdate); - field_to_json(Obj,"owner", Owner); - field_to_json(Obj,"location", Location); - field_to_json(Obj,"venue", Venue); - field_to_json(Obj,"firmware", Firmware); - field_to_json(Obj,"compatible", Compatible); - field_to_json(Obj,"fwUpdatePolicy", FWUpdatePolicy); - field_to_json(Obj,"devicePassword", DevicePassword); - field_to_json(Obj,"subscriber", subscriber); - field_to_json(Obj,"entity", entity); - field_to_json(Obj,"modified", modified); - field_to_json(Obj,"locale", locale); - } - - void Device::to_json_with_status(Poco::JSON::Object &Obj) const { - to_json(Obj); - -#ifdef TIP_GATEWAY_SERVICE - ConnectionState ConState; - - if (DeviceRegistry()->GetState(SerialNumber, ConState)) { - ConState.to_json(Obj); - } else { - field_to_json(Obj,"ipAddress", ""); - field_to_json(Obj,"txBytes", (uint64_t) 0); - field_to_json(Obj,"rxBytes", (uint64_t )0); - field_to_json(Obj,"messageCount", (uint64_t )0); - field_to_json(Obj,"connected", false); - field_to_json(Obj,"lastContact", ""); - field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); - field_to_json(Obj,"associations_2G", (uint64_t) 0); - field_to_json(Obj,"associations_5G", (uint64_t) 0); - } -#endif - } - - bool Device::from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - field_from_json(Obj,"serialNumber",SerialNumber); - field_from_json(Obj,"deviceType",DeviceType); - field_from_json(Obj,"macAddress",MACAddress); - field_from_json(Obj,"configuration",Configuration); - field_from_json(Obj,"notes",Notes); - field_from_json(Obj,"manufacturer",Manufacturer); - field_from_json(Obj,"owner",Owner); - field_from_json(Obj,"location",Location); - field_from_json(Obj,"venue",Venue); - field_from_json(Obj,"compatible",Compatible); - field_from_json(Obj,"subscriber", subscriber); - field_from_json(Obj,"entity", entity); - field_from_json(Obj,"locale", locale); - return true; - } catch (const Poco::Exception &E) { - } - return false; - } - - void Device::Print() const { - std::cout << "Device: " << SerialNumber << " DeviceType:" << DeviceType << " MACAddress:" << MACAddress << " Manufacturer:" - << Manufacturer << " " << Configuration << std::endl; - } - - void Statistics::to_json(Poco::JSON::Object &Obj) const { - EmbedDocument("data", Obj, Data); - field_to_json(Obj,"UUID", UUID); - field_to_json(Obj,"recorded", Recorded); - } - - void Capabilities::to_json(Poco::JSON::Object &Obj) const { - EmbedDocument("capabilities", Obj, Capabilities); - field_to_json(Obj,"firstUpdate", FirstUpdate); - field_to_json(Obj,"lastUpdate", LastUpdate); - } - - void DeviceLog::to_json(Poco::JSON::Object &Obj) const { - EmbedDocument("data", Obj, Data); - field_to_json(Obj,"log", Log); - field_to_json(Obj,"severity", Severity); - field_to_json(Obj,"recorded", Recorded); - field_to_json(Obj,"logType", LogType); - field_to_json(Obj,"UUID", UUID); - } - - void HealthCheck::to_json(Poco::JSON::Object &Obj) const { - EmbedDocument("values", Obj, Data); - field_to_json(Obj,"UUID", UUID); - field_to_json(Obj,"sanity", Sanity); - field_to_json(Obj,"recorded", Recorded); - } - - void DefaultConfiguration::to_json(Poco::JSON::Object &Obj) const { - EmbedDocument("configuration", Obj, Configuration); - field_to_json(Obj,"name", Name); - field_to_json(Obj,"modelIds", Models); - field_to_json(Obj,"description", Description); - field_to_json(Obj,"created", Created); - field_to_json(Obj,"lastModified", LastModified); - } - - void CommandDetails::to_json(Poco::JSON::Object &Obj) const { - EmbedDocument("details", Obj, Details); - EmbedDocument("results", Obj, Results); - field_to_json(Obj,"UUID", UUID); - field_to_json(Obj,"serialNumber", SerialNumber); - field_to_json(Obj,"command", Command); - field_to_json(Obj,"errorText", ErrorText); - field_to_json(Obj,"submittedBy", SubmittedBy); - field_to_json(Obj,"status", Status); - field_to_json(Obj,"submitted", Submitted); - field_to_json(Obj,"executed", Executed); - field_to_json(Obj,"completed", Completed); - field_to_json(Obj,"when", RunAt); - field_to_json(Obj,"errorCode", ErrorCode); - field_to_json(Obj,"custom", Custom); - field_to_json(Obj,"waitingForFile", WaitingForFile); - field_to_json(Obj,"attachFile", AttachDate); - field_to_json(Obj,"executionTime", executionTime); - } - - bool DefaultConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - field_from_json(Obj,"name",Name); - field_from_json(Obj,"configuration",Configuration); - field_from_json(Obj,"modelIds",Models); - field_from_json(Obj,"description",Description); - return true; - } catch (const Poco::Exception &E) { - } - return false; - } - - void BlackListedDevice::to_json(Poco::JSON::Object &Obj) const { - field_to_json(Obj,"serialNumber", serialNumber); - field_to_json(Obj,"author", author); - field_to_json(Obj,"reason", reason); - field_to_json(Obj,"created", created); - } - - bool BlackListedDevice::from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - field_from_json(Obj,"serialNumber",serialNumber); - field_from_json(Obj,"author",author); - field_from_json(Obj,"reason",reason); - field_from_json(Obj,"created",created); - return true; - } catch (const Poco::Exception &E) { - } - return false; - } - - void ConnectionState::to_json(Poco::JSON::Object &Obj) const { - field_to_json(Obj,"ipAddress", Address); - field_to_json(Obj,"txBytes", TX); - field_to_json(Obj,"rxBytes", RX); - field_to_json(Obj,"messageCount", MessageCount); - field_to_json(Obj,"UUID", UUID); - field_to_json(Obj,"connected", Connected); - field_to_json(Obj,"firmware", Firmware); - field_to_json(Obj,"lastContact", LastContact); - field_to_json(Obj,"associations_2G", Associations_2G); - field_to_json(Obj,"associations_5G", Associations_5G); - field_to_json(Obj,"webSocketClients", webSocketClients); - field_to_json(Obj,"websocketPackets", websocketPackets); - field_to_json(Obj,"kafkaClients", kafkaClients); - field_to_json(Obj,"kafkaPackets", kafkaPackets); - field_to_json(Obj,"locale", locale); - - switch(VerifiedCertificate) { - case NO_CERTIFICATE: - field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); break; - case VALID_CERTIFICATE: - field_to_json(Obj,"verifiedCertificate", "VALID_CERTIFICATE"); break; - case MISMATCH_SERIAL: - field_to_json(Obj,"verifiedCertificate", "MISMATCH_SERIAL"); break; - case VERIFIED: - field_to_json(Obj,"verifiedCertificate", "VERIFIED"); break; - default: - field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); break; - } - } - - void RttySessionDetails::to_json(Poco::JSON::Object &Obj) const { - field_to_json(Obj,"serialNumber", SerialNumber); - field_to_json(Obj,"server", Server); - field_to_json(Obj,"port", Port); - field_to_json(Obj,"token",Token); - field_to_json(Obj,"timeout", TimeOut); - field_to_json(Obj,"connectionId",ConnectionId); - field_to_json(Obj,"commandUUID",CommandUUID); - field_to_json(Obj,"started", Started); - field_to_json(Obj,"viewport",ViewPort); - field_to_json(Obj,"password",DevicePassword); - } - - void Dashboard::to_json(Poco::JSON::Object &Obj) const { - field_to_json(Obj,"commands",commands); - field_to_json(Obj,"upTimes",upTimes); - field_to_json(Obj,"memoryUsed",memoryUsed); - field_to_json(Obj,"load1",load1); - field_to_json(Obj,"load5",load5); - field_to_json(Obj,"load15",load15); - field_to_json(Obj,"vendors",vendors); - field_to_json(Obj,"status",status); - field_to_json(Obj,"deviceType",deviceType); - field_to_json(Obj,"healths",healths); - field_to_json(Obj,"certificates",certificates); - field_to_json(Obj,"lastContact",lastContact); - field_to_json(Obj,"associations",associations); - field_to_json(Obj,"snapshot",snapshot); - field_to_json(Obj,"numberOfDevices",numberOfDevices); - } - - void Dashboard::reset() { - commands.clear(); - upTimes.clear(); - memoryUsed.clear(); - load1.clear(); - load5.clear(); - load15.clear(); - vendors.clear(); - status.clear(); - deviceType.clear(); - healths.clear(); - certificates.clear(); - lastContact.clear(); - associations.clear(); - numberOfDevices = 0 ; - snapshot = OpenWifi::Now(); - } - - void CapabilitiesModel::to_json(Poco::JSON::Object &Obj) const{ - field_to_json(Obj,"deviceType", deviceType); - field_to_json(Obj,"capabilities", capabilities); - }; - - void ScriptRequest::to_json(Poco::JSON::Object &Obj) const { - field_to_json(Obj,"serialNumber",serialNumber); - field_to_json(Obj,"timeout",timeout); - field_to_json(Obj,"type",type); - field_to_json(Obj,"script",script); - field_to_json(Obj,"scriptId",scriptId); - field_to_json(Obj,"when",when); - } - - bool ScriptRequest::from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - field_from_json(Obj,"serialNumber",serialNumber); - field_from_json(Obj,"timeout",timeout); - field_from_json(Obj,"type",type); - field_from_json(Obj,"script",script); - field_from_json(Obj,"scriptId",scriptId); - field_from_json(Obj,"when",when); - return true; - } catch (const Poco::Exception &E) { - } - return false; - - } -} - diff --git a/src/framework/RESTAPI_GWobjects.h b/src/framework/RESTAPI_GWobjects.h deleted file mode 100644 index 9aad851..0000000 --- a/src/framework/RESTAPI_GWobjects.h +++ /dev/null @@ -1,213 +0,0 @@ -// -// License type: BSD 3-Clause License -// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE -// -// Created by Stephane Bourque on 2021-03-04. -// Arilia Wireless Inc. -// - -#pragma once - -#include "Poco/JSON/Object.h" -#include "RESTAPI_SecurityObjects.h" - -namespace OpenWifi::GWObjects { - - enum CertificateValidation { - NO_CERTIFICATE, - VALID_CERTIFICATE, - MISMATCH_SERIAL, - VERIFIED - }; - - struct ConnectionState { - uint64_t MessageCount = 0 ; - std::string Address; - uint64_t UUID = 0 ; - uint64_t PendingUUID = 0 ; - uint64_t TX = 0, RX = 0; - uint64_t Associations_2G=0; - uint64_t Associations_5G=0; - bool Connected = false; - uint64_t LastContact=0; - std::string Firmware; - CertificateValidation VerifiedCertificate = NO_CERTIFICATE; - std::string Compatible; - uint64_t kafkaClients=0; - uint64_t webSocketClients=0; - uint64_t kafkaPackets=0; - uint64_t websocketPackets=0; - std::string locale; - void to_json(Poco::JSON::Object &Obj) const; - }; - - struct Device { - std::string SerialNumber; - std::string DeviceType; - std::string MACAddress; - std::string Manufacturer; - std::string Configuration; - SecurityObjects::NoteInfoVec Notes; - std::string Owner; - std::string Location; - std::string Firmware; - std::string Compatible; - std::string FWUpdatePolicy; - uint64_t UUID = 0 ; - uint64_t CreationTimestamp = 0 ; - uint64_t LastConfigurationChange = 0 ; - uint64_t LastConfigurationDownload = 0 ; - uint64_t LastFWUpdate = 0 ; - std::string Venue; - std::string DevicePassword; - std::string subscriber; - std::string entity; - uint64_t modified=0; - std::string locale; - - void to_json(Poco::JSON::Object &Obj) const; - void to_json_with_status(Poco::JSON::Object &Obj) const; - bool from_json(const Poco::JSON::Object::Ptr &Obj); - void Print() const; - }; - - struct Statistics { - std::string SerialNumber; - uint64_t UUID = 0 ; - std::string Data; - uint64_t Recorded = 0; - void to_json(Poco::JSON::Object &Obj) const; - }; - - struct HealthCheck { - std::string SerialNumber; - uint64_t UUID = 0 ; - std::string Data; - uint64_t Recorded = 0 ; - uint64_t Sanity = 0 ; - void to_json(Poco::JSON::Object &Obj) const; - }; - - struct Capabilities { - std::string Capabilities; - uint64_t FirstUpdate = 0 ; - uint64_t LastUpdate = 0 ; - void to_json(Poco::JSON::Object &Obj) const; - }; - - struct DeviceLog { - enum Level { - LOG_EMERG = 0, /* system is unusable */ - LOG_ALERT = 1, /* action must be taken immediately */ - LOG_CRIT = 2, /* critical conditions */ - LOG_ERR = 3, /* error conditions */ - LOG_WARNING = 4, /* warning conditions */ - LOG_NOTICE = 5, /* normal but significant condition */ - LOG_INFO = 6, /* informational */ - LOG_DEBUG = 7 /* debug-level messages */ - }; - std::string SerialNumber; - std::string Log; - std::string Data; - uint64_t Severity = 0 ; - uint64_t Recorded = 0 ; - uint64_t LogType = 0 ; - uint64_t UUID = 0 ; - void to_json(Poco::JSON::Object &Obj) const; - }; - - struct DefaultConfiguration { - std::string Name; - std::string Configuration; - Types::StringVec Models; - std::string Description; - uint64_t Created; - uint64_t LastModified; - void to_json(Poco::JSON::Object &Obj) const; - bool from_json(const Poco::JSON::Object::Ptr &Obj); - }; - - struct CommandDetails { - std::string UUID; - std::string SerialNumber; - std::string Command; - std::string Status; - std::string SubmittedBy; - std::string Results; - std::string Details; - std::string ErrorText; - uint64_t Submitted = time(nullptr); - uint64_t Executed = 0; - uint64_t Completed = 0 ; - uint64_t RunAt = 0 ; - uint64_t ErrorCode = 0 ; - uint64_t Custom = 0 ; - uint64_t WaitingForFile = 0 ; - uint64_t AttachDate = 0 ; - uint64_t AttachSize = 0 ; - std::string AttachType; - double executionTime = 0.0; - void to_json(Poco::JSON::Object &Obj) const; - }; - - struct BlackListedDevice { - std::string serialNumber; - std::string reason; - std::string author; - uint64_t created; - void to_json(Poco::JSON::Object &Obj) const; - bool from_json(const Poco::JSON::Object::Ptr &Obj); - }; - - struct RttySessionDetails { - std::string SerialNumber; - std::string Server; - uint64_t Port = 0 ; - std::string Token; - uint64_t TimeOut = 0 ; - std::string ConnectionId; - uint64_t Started = 0 ; - std::string CommandUUID; - uint64_t ViewPort = 0 ; - std::string DevicePassword; - void to_json(Poco::JSON::Object &Obj) const; - }; - - struct Dashboard { - uint64_t snapshot = 0 ; - uint64_t numberOfDevices = 0 ; - Types::CountedMap commands; - Types::CountedMap upTimes; - Types::CountedMap memoryUsed; - Types::CountedMap load1; - Types::CountedMap load5; - Types::CountedMap load15; - Types::CountedMap vendors; - Types::CountedMap status; - Types::CountedMap deviceType; - Types::CountedMap healths; - Types::CountedMap certificates; - Types::CountedMap lastContact; - Types::CountedMap associations; - void to_json(Poco::JSON::Object &Obj) const; - void reset(); - }; - - struct CapabilitiesModel { - std::string deviceType; - std::string capabilities; - - void to_json(Poco::JSON::Object &Obj) const; - }; - - struct ScriptRequest { - uint64_t timeout=30; - std::string serialNumber; - std::string type; - std::string script; - std::string scriptId; - uint64_t when=0; - void to_json(Poco::JSON::Object &Obj) const; - bool from_json(const Poco::JSON::Object::Ptr &Obj); - }; -} diff --git a/src/framework/RESTAPI_GenericServerAccounting.h b/src/framework/RESTAPI_GenericServerAccounting.h new file mode 100644 index 0000000..fa91b27 --- /dev/null +++ b/src/framework/RESTAPI_GenericServerAccounting.h @@ -0,0 +1,75 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include +#include + +#include "Poco/StringTokenizer.h" +#include "Poco/String.h" +#include "Poco/Net/HTTPRequest.h" + +#include "framework/MicroServiceFuncs.h" + +namespace OpenWifi { + class RESTAPI_GenericServerAccounting { + public: + + enum { + LOG_GET=0, + LOG_DELETE, + LOG_PUT, + LOG_POST + }; + + void inline SetFlags(bool External, const std::string &Methods) { + Poco::StringTokenizer Tokens(Methods,","); + auto Offset = (External ? 0 : 4); + for(const auto &i:Tokens) { + if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_DELETE)==0) + LogFlags_[Offset+LOG_DELETE]=true; + else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_PUT)==0) + LogFlags_[Offset+LOG_PUT]=true; + else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_POST)==0) + LogFlags_[Offset+LOG_POST]=true; + else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_GET)==0) + LogFlags_[Offset+LOG_GET]=true; + } + } + + inline void InitLogging() { + std::string Public = MicroServiceConfigGetString("apilogging.public.methods","PUT,POST,DELETE"); + SetFlags(true, Public); + std::string Private = MicroServiceConfigGetString("apilogging.private.methods","PUT,POST,DELETE"); + SetFlags(false, Private); + + std::string PublicBadTokens = MicroServiceConfigGetString("apilogging.public.badtokens.methods",""); + LogBadTokens_[0] = (Poco::icompare(PublicBadTokens,"true")==0); + std::string PrivateBadTokens = MicroServiceConfigGetString("apilogging.private.badtokens.methods",""); + LogBadTokens_[1] = (Poco::icompare(PrivateBadTokens,"true")==0); + } + + [[nodiscard]] inline bool LogIt(const std::string &Method, bool External) const { + auto Offset = (External ? 0 : 4); + if(Method == Poco::Net::HTTPRequest::HTTP_GET) + return LogFlags_[Offset+LOG_GET]; + if(Method == Poco::Net::HTTPRequest::HTTP_POST) + return LogFlags_[Offset+LOG_POST]; + if(Method == Poco::Net::HTTPRequest::HTTP_PUT) + return LogFlags_[Offset+LOG_PUT]; + if(Method == Poco::Net::HTTPRequest::HTTP_DELETE) + return LogFlags_[Offset+LOG_DELETE]; + return false; + }; + + [[nodiscard]] inline bool LogBadTokens(bool External) const { + return LogBadTokens_[ (External ? 0 : 1) ]; + }; + + private: + std::array LogFlags_{false}; + std::array LogBadTokens_{false}; + }; +} \ No newline at end of file diff --git a/src/framework/RESTAPI_Handler.cpp b/src/framework/RESTAPI_Handler.cpp new file mode 100644 index 0000000..acaf371 --- /dev/null +++ b/src/framework/RESTAPI_Handler.cpp @@ -0,0 +1,8 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "RESTAPI_Handler.h" + +namespace OpenWifi { +} // namespace OpenWifi \ No newline at end of file diff --git a/src/framework/RESTAPI_Handler.h b/src/framework/RESTAPI_Handler.h new file mode 100644 index 0000000..d0161ec --- /dev/null +++ b/src/framework/RESTAPI_Handler.h @@ -0,0 +1,759 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include +#include +#include + +#include "Poco/Net/HTTPRequestHandler.h" +#include "Poco/Logger.h" +#include "Poco/JSON/Object.h" +#include "Poco/JSON/Parser.h" +#include "Poco/Net/HTTPResponse.h" +#include "Poco/Net/HTTPServerResponse.h" +#include "Poco/DeflatingStream.h" +#include "Poco/TemporaryFile.h" +#include "Poco/Net/OAuth20Credentials.h" + +#include "framework/ow_constants.h" +#include "framework/RESTAPI_GenericServerAccounting.h" +#include "framework/RESTAPI_RateLimiter.h" +#include "framework/utils.h" +#include "framework/RESTAPI_utils.h" +#include "framework/AuthClient.h" +#include "RESTObjects/RESTAPI_SecurityObjects.h" + +using namespace std::chrono_literals; + +namespace OpenWifi { + + class RESTAPIHandler : public Poco::Net::HTTPRequestHandler { + public: + struct QueryBlock { + uint64_t StartDate = 0 , EndDate = 0 , Offset = 0 , Limit = 0, LogType = 0 ; + std::string SerialNumber, Filter; + std::vector Select; + bool Lifetime=false, LastOnly=false, Newest=false, CountOnly=false, AdditionalInfo=false; + }; + typedef std::map BindingMap; + + struct RateLimit { + int64_t Interval=1000; + int64_t MaxCalls=10; + }; + + RESTAPIHandler( BindingMap map, + Poco::Logger &l, + std::vector Methods, + RESTAPI_GenericServerAccounting & Server, + uint64_t TransactionId, + bool Internal, + bool AlwaysAuthorize=true, + bool RateLimited=false, + const RateLimit & Profile = RateLimit{.Interval=1000,.MaxCalls=100}, + bool SubscriberOnly=false) + : Bindings_(std::move(map)), + Logger_(l), + Methods_(std::move(Methods)), + Internal_(Internal), + RateLimited_(RateLimited), + SubOnlyService_(SubscriberOnly), + AlwaysAuthorize_(AlwaysAuthorize), + Server_(Server), + MyRates_(Profile), + TransactionId_(TransactionId) + { + } + + inline bool RoleIsAuthorized([[maybe_unused]] const std::string & Path, [[maybe_unused]] const std::string & Method, [[maybe_unused]] std::string & Reason) { + return true; + } + + inline void handleRequest(Poco::Net::HTTPServerRequest &RequestIn, + Poco::Net::HTTPServerResponse &ResponseIn) final { + try { + Request = &RequestIn; + Response = &ResponseIn; + + // std::string th_name = "restsvr_" + std::to_string(TransactionId_); + // Utils::SetThreadName(th_name.c_str()); + + if(Request->getContentLength()>0) { + if(Request->getContentType().find("application/json")!=std::string::npos) { + ParsedBody_ = IncomingParser_.parse(Request->stream()).extract(); + } + } + + if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls)) { + return UnAuthorized(RESTAPI::Errors::RATE_LIMIT_EXCEEDED); + } + + if (!ContinueProcessing()) + return; + + bool Expired=false, Contacted=false; + if (AlwaysAuthorize_ && !IsAuthorized(Expired, Contacted, SubOnlyService_)) { + if(Expired) + return UnAuthorized(RESTAPI::Errors::EXPIRED_TOKEN); + if(Contacted) + return UnAuthorized(RESTAPI::Errors::INVALID_TOKEN); + else + return UnAuthorized(RESTAPI::Errors::SECURITY_SERVICE_UNREACHABLE); + } + + std::string Reason; + if(!RoleIsAuthorized(RequestIn.getURI(), Request->getMethod(), Reason)) { + return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); + } + + ParseParameters(); + if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_GET) + return DoGet(); + else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_POST) + return DoPost(); + else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) + return DoDelete(); + else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_PUT) + return DoPut(); + return BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod); + } catch (const Poco::Exception &E) { + Logger_.log(E); + return BadRequest(RESTAPI::Errors::InternalError); + } + } + + [[nodiscard]] inline bool NeedAdditionalInfo() const { return QB_.AdditionalInfo; } + [[nodiscard]] inline const std::vector & SelectedRecords() const { return QB_.Select; } + + inline static bool ParseBindings(const std::string & Request, const std::list & EndPoints, BindingMap &bindings) { + bindings.clear(); + auto PathItems = Poco::StringTokenizer(Request, "/"); + + for(const auto &EndPoint:EndPoints) { + auto ParamItems = Poco::StringTokenizer(EndPoint, "/"); + if (PathItems.count() != ParamItems.count()) + continue; + + bool Matched = true; + for (size_t i = 0; i < PathItems.count(); i++) { + if (PathItems[i] != ParamItems[i]) { + if (ParamItems[i][0] == '{') { + auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2); + bindings[Poco::toLower(ParamName)] = PathItems[i]; + } else { + Matched = false; + break; + } + } + } + if(Matched) + return true; + } + return false; + } + + inline void PrintBindings() { + for (const auto &[key, value] : Bindings_) + std::cout << "Key = " << key << " Value= " << value << std::endl; + } + + inline void ParseParameters() { + Poco::URI uri(Request->getURI()); + Parameters_ = uri.getQueryParameters(); + InitQueryBlock(); + } + + inline static bool is_number(const std::string &s) { + return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); + } + + inline static bool is_bool(const std::string &s) { + if (s == "true" || s == "false") + return true; + return false; + } + + [[nodiscard]] inline uint64_t GetParameter(const std::string &Name, const uint64_t Default) { + auto Hint = std::find_if(Parameters_.begin(),Parameters_.end(),[&](const std::pair &S){ return S.first==Name; }); + if(Hint==Parameters_.end() || !is_number(Hint->second)) + return Default; + return std::stoull(Hint->second); + } + + [[nodiscard]] inline bool GetBoolParameter(const std::string &Name, bool Default=false) { + auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair &S){ return S.first==Name; }); + if(Hint==end(Parameters_) || !is_bool(Hint->second)) + return Default; + return Hint->second=="true"; + } + + [[nodiscard]] inline std::string GetParameter(const std::string &Name, const std::string &Default="") { + auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair &S){ return S.first==Name; }); + if(Hint==end(Parameters_)) + return Default; + return Hint->second; + } + + [[nodiscard]] inline bool HasParameter(const std::string &Name, std::string &Value) { + auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair &S){ return S.first==Name; }); + if(Hint==end(Parameters_)) + return false; + Value = Hint->second; + return true; + } + + [[nodiscard]] inline bool HasParameter(const std::string &Name, uint64_t & Value) { + auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair &S){ return S.first==Name; }); + if(Hint==end(Parameters_)) + return false; + Value = std::stoull(Hint->second); + return true; + } + + [[nodiscard]] inline const std::string & GetBinding(const std::string &Name, const std::string &Default="") { + auto E = Bindings_.find(Poco::toLower(Name)); + if (E == Bindings_.end()) + return Default; + + return E->second; + } + + [[nodiscard]] inline static std::string MakeList(const std::vector &L) { + std::string Return; + for (const auto &i : L) { + if (Return.empty()) + Return = i; + else + Return += ", " + i; + } + return Return; + } + + static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Types::UUIDvec_t & Value) { + if(O->has(Field) && O->isArray(Field)) { + auto Arr = O->getArray(Field); + for(const auto &i:*Arr) + Value.emplace_back(i.toString()); + return true; + } + return false; + } + + static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, std::string &Value) { + if(O->has(Field)) { + Value = O->get(Field).toString(); + return true; + } + return false; + } + + static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, uint64_t &Value) { + if(O->has(Field)) { + Value = O->get(Field); + return true; + } + return false; + } + + static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, bool &Value) { + if(O->has(Field)) { + Value = O->get(Field).toString()=="true"; + return true; + } + return false; + } + + static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, double &Value) { + if(O->has(Field)) { + Value = (double) O->get(Field); + return true; + } + return false; + } + + static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Poco::Data::BLOB &Value) { + if(O->has(Field)) { + std::string Content = O->get(Field).toString(); + auto DecodedBlob = Utils::base64decode(Content); + Value.assignRaw((const unsigned char *)&DecodedBlob[0],DecodedBlob.size()); + return true; + } + return false; + } + + + template bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, const T &value, T & assignee) { + if(O->has(Field)) { + assignee = value; + return true; + } + return false; + } + + inline void SetCommonHeaders(bool CloseConnection=false) { + Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1); + Response->setChunkedTransferEncoding(true); + Response->setContentType("application/json"); + auto Origin = Request->find("Origin"); + if (Origin != Request->end()) { + Response->set("Access-Control-Allow-Origin", Origin->second); + } else { + Response->set("Access-Control-Allow-Origin", "*"); + } + Response->set("Vary", "Origin, Accept-Encoding"); + if(CloseConnection) { + Response->set("Connection", "close"); + Response->setKeepAlive(false); + } else { + Response->setKeepAlive(true); + Response->set("Connection", "Keep-Alive"); + Response->set("Keep-Alive", "timeout=30, max=1000"); + } + } + + inline void ProcessOptions() { + Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1); + Response->setChunkedTransferEncoding(true); + auto Origin = Request->find("Origin"); + if (Origin != Request->end()) { + Response->set("Access-Control-Allow-Origin", Origin->second); + } else { + Response->set("Access-Control-Allow-Origin", "*"); + } + Response->set("Access-Control-Allow-Methods", MakeList(Methods_)); + auto RequestHeaders = Request->find("Access-Control-Request-Headers"); + if(RequestHeaders!=Request->end()) + Response->set("Access-Control-Allow-Headers", RequestHeaders->second); + Response->set("Vary", "Origin, Accept-Encoding"); + Response->set("Access-Control-Allow-Credentials", "true"); + Response->set("Access-Control-Max-Age", "86400"); + Response->set("Connection", "Keep-Alive"); + Response->set("Keep-Alive", "timeout=30, max=1000"); + + Response->setContentLength(0); + Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK); + Response->send(); + } + + inline void PrepareResponse(Poco::Net::HTTPResponse::HTTPStatus Status = Poco::Net::HTTPResponse::HTTP_OK, + bool CloseConnection = false) { + Response->setStatus(Status); + SetCommonHeaders(CloseConnection); + } + + inline void BadRequest(const OpenWifi::RESTAPI::Errors::msg &E, const std::string & Extra="") { + PrepareResponse(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",400); + ErrorObject.set("ErrorDetails",Request->getMethod()); + if(Extra.empty()) + ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; + else + ErrorObject.set("ErrorDescription",fmt::format("{}: {} ({})",E.err_num,E.err_txt, Extra)) ; + + std::ostream &Answer = Response->send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); + } + + inline void InternalError(const OpenWifi::RESTAPI::Errors::msg &E) { + PrepareResponse(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",500); + ErrorObject.set("ErrorDetails",Request->getMethod()); + ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; + std::ostream &Answer = Response->send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); + } + + inline void UnAuthorized(const OpenWifi::RESTAPI::Errors::msg &E) { + PrepareResponse(Poco::Net::HTTPResponse::HTTP_FORBIDDEN); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",E.err_num); + ErrorObject.set("ErrorDetails",Request->getMethod()); + ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; + std::ostream &Answer = Response->send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); + } + + inline void NotFound() { + PrepareResponse(Poco::Net::HTTPResponse::HTTP_NOT_FOUND); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",404); + ErrorObject.set("ErrorDetails",Request->getMethod()); + const auto & E = OpenWifi::RESTAPI::Errors::Error404; + ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; + std::ostream &Answer = Response->send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); + poco_debug(Logger_,fmt::format("RES-NOTFOUND: User='{}@{}' Method='{}' Path='{}", + UserInfo_.userinfo.email, + Utils::FormatIPv6(Request->clientAddress().toString()), + Request->getMethod(), + Request->getURI())); + } + + inline void OK() { + PrepareResponse(); + if( Request->getMethod()==Poco::Net::HTTPRequest::HTTP_DELETE || + Request->getMethod()==Poco::Net::HTTPRequest::HTTP_OPTIONS) { + Response->send(); + } else { + Poco::JSON::Object ErrorObject; + ErrorObject.set("Code", 0); + ErrorObject.set("Operation", Request->getMethod()); + ErrorObject.set("Details", "Command completed."); + std::ostream &Answer = Response->send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); + } + } + + inline void SendCompressedTarFile(const std::string & FileName, const std::string & Content) { + Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); + SetCommonHeaders(); + Response->set("Content-Type","application/gzip"); + Response->set("Content-Disposition", "attachment; filename=" + FileName ); + Response->set("Content-Transfer-Encoding","binary"); + Response->set("Accept-Ranges", "bytes"); + Response->set("Cache-Control", "no-store"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK); + Response->setContentLength(Content.size()); + Response->setChunkedTransferEncoding(true); + std::ostream& OutputStream = Response->send(); + OutputStream << Content; + } + + inline void SendFile(Poco::File & File, const std::string & UUID) { + Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); + SetCommonHeaders(); + Response->set("Content-Type","application/octet-stream"); + Response->set("Content-Disposition", "attachment; filename=" + UUID ); + Response->set("Content-Transfer-Encoding","binary"); + Response->set("Accept-Ranges", "bytes"); + Response->set("Cache-Control", "no-store"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + Response->setContentLength(File.getSize()); + Response->sendFile(File.path(),"application/octet-stream"); + } + + inline void SendFile(Poco::File & File) { + Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); + SetCommonHeaders(); + Poco::Path P(File.path()); + auto MT = Utils::FindMediaType(File); + if(MT.Encoding==Utils::BINARY) { + Response->set("Content-Transfer-Encoding","binary"); + Response->set("Accept-Ranges", "bytes"); + } + Response->set("Cache-Control", "no-store"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + Response->sendFile(File.path(),MT.ContentType); + } + + inline void SendFile(Poco::TemporaryFile &TempAvatar, [[maybe_unused]] const std::string &Type, const std::string & Name) { + Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); + SetCommonHeaders(); + auto MT = Utils::FindMediaType(Name); + if(MT.Encoding==Utils::BINARY) { + Response->set("Content-Transfer-Encoding","binary"); + Response->set("Accept-Ranges", "bytes"); + } + Response->set("Content-Disposition", "attachment; filename=" + Name ); + Response->set("Accept-Ranges", "bytes"); + Response->set("Cache-Control", "no-store"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + Response->setContentLength(TempAvatar.getSize()); + Response->sendFile(TempAvatar.path(),MT.ContentType); + } + + inline void SendFileContent(const std::string &Content, const std::string &Type, const std::string & Name) { + Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); + SetCommonHeaders(); + auto MT = Utils::FindMediaType(Name); + if(MT.Encoding==Utils::BINARY) { + Response->set("Content-Transfer-Encoding","binary"); + Response->set("Accept-Ranges", "bytes"); + } + Response->set("Content-Disposition", "attachment; filename=" + Name ); + Response->set("Accept-Ranges", "bytes"); + Response->set("Cache-Control", "no-store"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + Response->setContentLength(Content.size()); + Response->setContentType(Type ); + auto & OutputStream = Response->send(); + OutputStream << Content ; + } + + inline void SendHTMLFileBack(Poco::File & File, + const Types::StringPairVec & FormVars) { + Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); + SetCommonHeaders(); + Response->set("Pragma", "private"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + std::string FormContent = Utils::LoadFile(File.path()); + Utils::ReplaceVariables(FormContent, FormVars); + Response->setContentLength(FormContent.size()); + Response->setChunkedTransferEncoding(true); + Response->setContentType("text/html"); + std::ostream& ostr = Response->send(); + ostr << FormContent; + } + + inline void ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status, bool CloseConnection=false) { + PrepareResponse(Status, CloseConnection); + if(Status == Poco::Net::HTTPResponse::HTTP_NO_CONTENT) { + Response->setContentLength(0); + Response->erase("Content-Type"); + Response->setChunkedTransferEncoding(false); + } + Response->send(); + } + + inline bool ContinueProcessing() { + if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) { + ProcessOptions(); + return false; + } else if (std::find(Methods_.begin(), Methods_.end(), Request->getMethod()) == Methods_.end()) { + BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod); + return false; + } + + return true; + } + + inline bool IsAuthorized(bool & Expired, bool & Contacted, bool SubOnly = false ); + + inline void ReturnObject(Poco::JSON::Object &Object) { + PrepareResponse(); + if(Request!= nullptr) { + // can we compress ??? + auto AcceptedEncoding = Request->find("Accept-Encoding"); + if(AcceptedEncoding!=Request->end()) { + if( AcceptedEncoding->second.find("gzip")!=std::string::npos || + AcceptedEncoding->second.find("compress")!=std::string::npos) { + Response->set("Content-Encoding", "gzip"); + std::ostream &Answer = Response->send(); + Poco::DeflatingOutputStream deflater(Answer, Poco::DeflatingStreamBuf::STREAM_GZIP); + Poco::JSON::Stringifier::stringify(Object, deflater); + deflater.close(); + return; + } + } + } + std::ostream &Answer = Response->send(); + Poco::JSON::Stringifier::stringify(Object, Answer); + } + + inline void ReturnCountOnly(uint64_t Count) { + Poco::JSON::Object Answer; + Answer.set("count", Count); + ReturnObject(Answer); + } + + inline bool InitQueryBlock() { + if(QueryBlockInitialized_) + return true; + QueryBlockInitialized_=true; + QB_.SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, ""); + QB_.StartDate = GetParameter(RESTAPI::Protocol::STARTDATE, 0); + QB_.EndDate = GetParameter(RESTAPI::Protocol::ENDDATE, 0); + QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 0); + QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100); + QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, ""); + QB_.Lifetime = GetBoolParameter(RESTAPI::Protocol::LIFETIME,false); + QB_.LogType = GetParameter(RESTAPI::Protocol::LOGTYPE,0); + QB_.LastOnly = GetBoolParameter(RESTAPI::Protocol::LASTONLY,false); + QB_.Newest = GetBoolParameter(RESTAPI::Protocol::NEWEST,false); + QB_.CountOnly = GetBoolParameter(RESTAPI::Protocol::COUNTONLY,false); + QB_.AdditionalInfo = GetBoolParameter(RESTAPI::Protocol::WITHEXTENDEDINFO,false); + + auto RawSelect = GetParameter(RESTAPI::Protocol::SELECT, ""); + + auto Entries = Poco::StringTokenizer(RawSelect,","); + for(const auto &i:Entries) { + QB_.Select.emplace_back(i); + } + if(QB_.Offset<1) + QB_.Offset=0; + return true; + } + + [[nodiscard]] inline uint64_t Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default=0){ + if(Obj->has(Parameter)) + return Obj->get(Parameter); + return Default; + } + + [[nodiscard]] inline std::string GetS(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, const std::string & Default=""){ + if(Obj->has(Parameter)) + return Obj->get(Parameter).toString(); + return Default; + } + + [[nodiscard]] inline bool GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default=false){ + if(Obj->has(Parameter)) + return Obj->get(Parameter).toString()=="true"; + return Default; + } + + [[nodiscard]] inline uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj) { + return RESTAPIHandler::Get(RESTAPI::Protocol::WHEN, Obj); + } + + template void ReturnObject(const char *Name, const std::vector & Objects) { + Poco::JSON::Object Answer; + RESTAPI_utils::field_to_json(Answer,Name,Objects); + ReturnObject(Answer); + } + + Poco::Logger & Logger() { return Logger_; } + + virtual void DoGet() = 0 ; + virtual void DoDelete() = 0 ; + virtual void DoPost() = 0 ; + virtual void DoPut() = 0 ; + + Poco::Net::HTTPServerRequest *Request= nullptr; + Poco::Net::HTTPServerResponse *Response= nullptr; + SecurityObjects::UserInfoAndPolicy UserInfo_; + QueryBlock QB_; + const std::string & Requester() const { return REST_Requester_; } + protected: + BindingMap Bindings_; + Poco::URI::QueryParameters Parameters_; + Poco::Logger &Logger_; + std::string SessionToken_; + std::vector Methods_; + bool Internal_=false; + bool RateLimited_=false; + bool QueryBlockInitialized_=false; + bool SubOnlyService_=false; + bool AlwaysAuthorize_=true; + Poco::JSON::Parser IncomingParser_; + RESTAPI_GenericServerAccounting & Server_; + RateLimit MyRates_; + uint64_t TransactionId_; + Poco::JSON::Object::Ptr ParsedBody_; + std::string REST_Requester_; + }; + +#ifdef TIP_SECURITY_SERVICE + [[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, std::uint64_t TID, bool & Expired , bool Sub ); +#endif + inline bool RESTAPIHandler::IsAuthorized( bool & Expired , [[maybe_unused]] bool & Contacted , bool Sub ) { + if(Internal_ && Request->has("X-INTERNAL-NAME")) { + auto Allowed = MicroServiceIsValidAPIKEY(*Request); + Contacted = true; + if(!Allowed) { + if(Server_.LogBadTokens(false)) { + poco_debug(Logger_,fmt::format("I-REQ-DENIED({}): TID={} Method={} Path={}", + Utils::FormatIPv6(Request->clientAddress().toString()), + TransactionId_, + Request->getMethod(), Request->getURI())); + } + } else { + auto Id = Request->get("X-INTERNAL-NAME", "unknown"); + REST_Requester_ = Id; + if(Server_.LogIt(Request->getMethod(),true)) { + poco_debug(Logger_,fmt::format("I-REQ-ALLOWED({}): TID={} User='{}' Method={} Path={}", + Utils::FormatIPv6(Request->clientAddress().toString()), + TransactionId_, + Id, + Request->getMethod(), Request->getURI())); + } + } + return Allowed; + } else { + if (SessionToken_.empty()) { + try { + Poco::Net::OAuth20Credentials Auth(*Request); + if (Auth.getScheme() == "Bearer") { + SessionToken_ = Auth.getBearerToken(); + } + } catch (const Poco::Exception &E) { + Logger_.log(E); + } + } +#ifdef TIP_SECURITY_SERVICE + if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, TransactionId_, Expired, Sub)) { +#else + if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, TransactionId_, Expired, Contacted, Sub)) { +#endif + REST_Requester_ = UserInfo_.userinfo.email; + if(Server_.LogIt(Request->getMethod(),true)) { + poco_debug(Logger_,fmt::format("X-REQ-ALLOWED({}): TID={} User='{}@{}' Method={} Path={}", + UserInfo_.userinfo.email, + TransactionId_, + Utils::FormatIPv6(Request->clientAddress().toString()), + Request->clientAddress().toString(), + Request->getMethod(), + Request->getURI())); + } + return true; + } else { + if(Server_.LogBadTokens(true)) { + poco_debug(Logger_,fmt::format("X-REQ-DENIED({}): TID={} Method={} Path={}", + Utils::FormatIPv6(Request->clientAddress().toString()), + TransactionId_, + Request->getMethod(), + Request->getURI())); + } + } + return false; + } + } + + class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { + public: + RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) + : RESTAPIHandler(bindings, L, std::vector{}, Server, TransactionId, Internal) {} + inline void DoGet() override {}; + inline void DoPost() override {}; + inline void DoPut() override {}; + inline void DoDelete() override {}; + }; + + template + constexpr auto test_has_PathName_method(T*) + -> decltype( T::PathName() , std::true_type{} ) + { + return std::true_type{}; + } + constexpr auto test_has_PathName_method(...) -> std::false_type + { + return std::false_type{}; + } + + template + RESTAPIHandler * RESTAPI_Router(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, + Poco::Logger & Logger, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId) { + static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method."); + if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) { + return new T(Bindings, Logger, Server, TransactionId, false); + } + + if constexpr (sizeof...(Args) == 0) { + return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server, TransactionId, false); + } else { + return RESTAPI_Router(RequestedPath, Bindings, Logger, Server, TransactionId); + } + } + + template + RESTAPIHandler * RESTAPI_Router_I(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, + Poco::Logger & Logger, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId) { + static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method."); + if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) { + return new T(Bindings, Logger, Server, TransactionId, true ); + } + + if constexpr (sizeof...(Args) == 0) { + return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server, TransactionId, true); + } else { + return RESTAPI_Router_I(RequestedPath, Bindings, Logger, Server, TransactionId); + } + } + +} // namespace OpenWifi diff --git a/src/framework/RESTAPI_IntServer.cpp b/src/framework/RESTAPI_IntServer.cpp new file mode 100644 index 0000000..ee4b09f --- /dev/null +++ b/src/framework/RESTAPI_IntServer.cpp @@ -0,0 +1,17 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "RESTAPI_IntServer.h" + +namespace OpenWifi { + + Poco::Net::HTTPRequestHandler * + IntRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest &Request) { + auto TID = NextTransactionId_++; + Utils::SetThreadName(fmt::format("i-rest:{}", TID).c_str()); + Poco::URI uri(Request.getURI()); + return RESTAPI_IntServer()->CallServer(uri.getPath(), TID); + } + +} // namespace OpenWifi \ No newline at end of file diff --git a/src/framework/RESTAPI_IntServer.h b/src/framework/RESTAPI_IntServer.h new file mode 100644 index 0000000..cff9946 --- /dev/null +++ b/src/framework/RESTAPI_IntServer.h @@ -0,0 +1,104 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "Poco/Net/HTTPServer.h" + +#include "framework/SubSystemServer.h" +#include "framework/RESTAPI_Handler.h" + +namespace OpenWifi { + + Poco::Net::HTTPRequestHandler * RESTAPI_IntRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, + Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t Id); + + class IntRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { + public: + inline IntRequestHandlerFactory() = default; + Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override; + private: + static inline std::atomic_uint64_t NextTransactionId_ = 1; + }; + + class RESTAPI_IntServer : public SubSystemServer { + public: + static auto instance() { + static auto instance_ = new RESTAPI_IntServer; + return instance_; + } + + inline int Start() override { + Logger().information("Starting."); + Server_.InitLogging(); + + for(const auto & Svr: ConfigServersList_) { + + if(MicroServiceNoAPISecurity()) { + Logger().information(fmt::format("Starting: {}:{}. Security has been disabled for APIs.", Svr.Address(), Svr.Port())); + } else { + Logger().information(fmt::format("Starting: {}:{}. Keyfile:{} CertFile: {}", Svr.Address(), Svr.Port(), + Svr.KeyFile(),Svr.CertFile())); + Svr.LogCert(Logger()); + if (!Svr.RootCA().empty()) + Svr.LogCas(Logger()); + } + + auto Params = new Poco::Net::HTTPServerParams; + Params->setKeepAlive(true); + Params->setName("ws:irest"); + + std::unique_ptr NewServer; + if(MicroServiceNoAPISecurity()) { + auto Sock{Svr.CreateSocket(Logger())}; + NewServer = std::make_unique(new IntRequestHandlerFactory, Pool_, Sock, Params); + } else { + auto Sock{Svr.CreateSecureSocket(Logger())}; + NewServer = std::make_unique(new IntRequestHandlerFactory, Pool_, Sock, Params); + }; + NewServer->start(); + RESTServers_.push_back(std::move(NewServer)); + } + + return 0; + } + + inline void Stop() override { + Logger().information("Stopping..."); + for( const auto & svr : RESTServers_ ) + svr->stopAll(true); + Pool_.stopAll(); + Pool_.joinAll(); + Logger().information("Stopped..."); + } + + inline void reinitialize([[maybe_unused]] Poco::Util::Application &self) override { + MicroServiceLoadConfigurationFile(); + Logger().information("Reinitializing."); + Stop(); + Start(); + } + + inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) { + RESTAPIHandler::BindingMap Bindings; + Utils::SetThreadName(fmt::format("i-rest:{}",Id).c_str()); + return RESTAPI_IntRouter(Path, Bindings, Logger(), Server_, Id); + } + const Poco::ThreadPool & Pool() { return Pool_; } + private: + std::vector> RESTServers_; + Poco::ThreadPool Pool_{"i-rest",4,64}; + RESTAPI_GenericServerAccounting Server_; + + RESTAPI_IntServer() noexcept: + SubSystemServer("RESTAPI_IntServer", "REST-ISRV", "openwifi.internal.restapi") + { + } + }; + + inline auto RESTAPI_IntServer() { return RESTAPI_IntServer::instance(); }; + + +} // namespace OpenWifi + diff --git a/src/framework/RESTAPI_PartHandler.h b/src/framework/RESTAPI_PartHandler.h new file mode 100644 index 0000000..4e0f2f0 --- /dev/null +++ b/src/framework/RESTAPI_PartHandler.h @@ -0,0 +1,66 @@ +// +// Created by stephane bourque on 2022-10-26. +// + +#pragma once + +#include +#include "Poco/Net/PartHandler.h" +#include "Poco/Net/MessageHeader.h" +#include "Poco/CountingStream.h" +#include "Poco/NullStream.h" +#include "Poco/StreamCopier.h" + +namespace OpenWifi { + class RESTAPI_PartHandler: public Poco::Net::PartHandler { + public: + RESTAPI_PartHandler(): + _length(0) + { + } + + inline void handlePart(const Poco::Net::MessageHeader& header, std::istream& stream) override + { + _type = header.get("Content-Type", "(unspecified)"); + if (header.has("Content-Disposition")) + { + std::string disp; + Poco::Net::NameValueCollection params; + Poco::Net::MessageHeader::splitParameters(header["Content-Disposition"], disp, params); + _name = params.get("name", "(unnamed)"); + _fileName = params.get("filename", "(unnamed)"); + } + + Poco::CountingInputStream istr(stream); + Poco::NullOutputStream ostr; + Poco::StreamCopier::copyStream(istr, ostr); + _length = (int)istr.chars(); + } + + [[nodiscard]] inline int length() const + { + return _length; + } + + [[nodiscard]] inline const std::string& name() const + { + return _name; + } + + [[nodiscard]] inline const std::string& fileName() const + { + return _fileName; + } + + [[nodiscard]] inline const std::string& contentType() const + { + return _type; + } + + private: + int _length; + std::string _type; + std::string _name; + std::string _fileName; + }; +} diff --git a/src/framework/RESTAPI_RateLimiter.h b/src/framework/RESTAPI_RateLimiter.h new file mode 100644 index 0000000..954227e --- /dev/null +++ b/src/framework/RESTAPI_RateLimiter.h @@ -0,0 +1,75 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "framework/SubSystemServer.h" + +#include "Poco/URI.h" +#include "Poco/Net/HTTPServerRequest.h" +#include "Poco/ExpireLRUCache.h" + +#include "fmt/format.h" + +namespace OpenWifi { + + class RESTAPI_RateLimiter : public SubSystemServer { + public: + + struct ClientCacheEntry { + int64_t Start=0; + int Count=0; + }; + + static auto instance() { + static auto instance_ = new RESTAPI_RateLimiter; + return instance_; + } + + inline int Start() final { return 0;}; + inline void Stop() final { }; + + inline bool IsRateLimited(const Poco::Net::HTTPServerRequest &R, int64_t Period, int64_t MaxCalls) { + Poco::URI uri(R.getURI()); + auto H = str_hash(uri.getPath() + R.clientAddress().host().toString()); + auto E = Cache_.get(H); + auto Now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + if(E.isNull()) { + Cache_.add(H,ClientCacheEntry{.Start=Now, .Count=1}); + return false; + } + if((Now-E->Start)Count++; + Cache_.update(H,E); + if(E->Count > MaxCalls) { + poco_warning(Logger(),fmt::format("RATE-LIMIT-EXCEEDED: from '{}'", R.clientAddress().toString())); + return true; + } + return false; + } + E->Start = Now; + E->Count = 1; + Cache_.update(H,E); + return false; + } + + inline void Clear() { + Cache_.clear(); + } + + private: + Poco::ExpireLRUCache Cache_{2048}; + std::hash str_hash; + + RESTAPI_RateLimiter() noexcept: + SubSystemServer("RateLimiter", "RATE-LIMITER", "rate.limiter") + { + } + + }; + + inline auto RESTAPI_RateLimiter() { return RESTAPI_RateLimiter::instance(); } + + +} \ No newline at end of file diff --git a/src/framework/RESTAPI_SystemCommand.h b/src/framework/RESTAPI_SystemCommand.h new file mode 100644 index 0000000..d00ae65 --- /dev/null +++ b/src/framework/RESTAPI_SystemCommand.h @@ -0,0 +1,157 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "framework/RESTAPI_Handler.h" + +#include "Poco/Environment.h" + +using namespace std::chrono_literals; + +namespace OpenWifi { + + class RESTAPI_system_command : public RESTAPIHandler { + public: + RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) + : RESTAPIHandler(bindings, L, + std::vector{Poco::Net::HTTPRequest::HTTP_POST, + Poco::Net::HTTPRequest::HTTP_GET, + Poco::Net::HTTPRequest::HTTP_OPTIONS}, + Server, + TransactionId, + Internal) {} + static auto PathName() { return std::list{"/api/v1/system"};} + + inline void DoGet() { + std::string Arg; + if(HasParameter("command",Arg) && Arg=="info") { + Poco::JSON::Object Answer; + Answer.set(RESTAPI::Protocol::VERSION, MicroServiceVersion()); + Answer.set(RESTAPI::Protocol::UPTIME, MicroServiceUptimeTotalSeconds()); + Answer.set(RESTAPI::Protocol::START, MicroServiceStartTimeEpochTime()); + Answer.set(RESTAPI::Protocol::OS, Poco::Environment::osName()); + Answer.set(RESTAPI::Protocol::PROCESSORS, Poco::Environment::processorCount()); + Answer.set(RESTAPI::Protocol::HOSTNAME, Poco::Environment::nodeName()); + Answer.set(RESTAPI::Protocol::UI, MicroServiceGetUIURI()); + + Poco::JSON::Array Certificates; + auto SubSystems = MicroServiceGetFullSubSystems(); + std::set CertNames; + + for(const auto &i:SubSystems) { + auto Hosts=i->HostSize(); + for(uint64_t j=0;jHost(j).CertFile(); + if(!CertFileName.empty()) { + Poco::File F1(CertFileName); + if(F1.exists()) { + auto InsertResult = CertNames.insert(CertFileName); + if(InsertResult.second) { + Poco::JSON::Object Inner; + Poco::Path F(CertFileName); + Inner.set("filename", F.getFileName()); + Poco::Crypto::X509Certificate C(CertFileName); + auto ExpiresOn = C.expiresOn(); + Inner.set("expiresOn", ExpiresOn.timestamp().epochTime()); + Certificates.add(Inner); + } + } + } + } + } + Answer.set("certificates", Certificates); + return ReturnObject(Answer); + } + if(GetBoolParameter("extraConfiguration")) { + Poco::JSON::Object Answer; + MicroServiceGetExtraConfiguration(Answer); + return ReturnObject(Answer); + } + BadRequest(RESTAPI::Errors::InvalidCommand); + } + + inline void DoPost() final { + const auto & Obj = ParsedBody_; + if (Obj->has(RESTAPI::Protocol::COMMAND)) { + auto Command = Poco::toLower(Obj->get(RESTAPI::Protocol::COMMAND).toString()); + if (Command == RESTAPI::Protocol::SETLOGLEVEL) { + if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) && + Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) { + auto ParametersBlock = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS); + for (const auto &i : *ParametersBlock) { + Poco::JSON::Parser pp; + auto InnerObj = pp.parse(i).extract(); + if (InnerObj->has(RESTAPI::Protocol::TAG) && + InnerObj->has(RESTAPI::Protocol::VALUE)) { + auto Name = GetS(RESTAPI::Protocol::TAG, InnerObj); + auto Value = GetS(RESTAPI::Protocol::VALUE, InnerObj); + MicroServiceSetSubsystemLogLevel(Name, Value); + Logger_.information( + fmt::format("Setting log level for {} at {}", Name, Value)); + } + } + return OK(); + } + } else if (Command == RESTAPI::Protocol::GETLOGLEVELS) { + auto CurrentLogLevels = MicroServiceGetLogLevels(); + Poco::JSON::Object Result; + Poco::JSON::Array Array; + for (auto &[Name, Level] : CurrentLogLevels) { + Poco::JSON::Object Pair; + Pair.set(RESTAPI::Protocol::TAG, Name); + Pair.set(RESTAPI::Protocol::VALUE, Level); + Array.add(Pair); + } + Result.set(RESTAPI::Protocol::TAGLIST, Array); + return ReturnObject(Result); + } else if (Command == RESTAPI::Protocol::GETLOGLEVELNAMES) { + Poco::JSON::Object Result; + Poco::JSON::Array LevelNamesArray; + const Types::StringVec &LevelNames = MicroServiceGetLogLevelNames(); + for (const auto &i : LevelNames) + LevelNamesArray.add(i); + Result.set(RESTAPI::Protocol::LIST, LevelNamesArray); + return ReturnObject(Result); + } else if (Command == RESTAPI::Protocol::GETSUBSYSTEMNAMES) { + Poco::JSON::Object Result; + Poco::JSON::Array LevelNamesArray; + const Types::StringVec &SubSystemNames = MicroServiceGetSubSystems(); + for (const auto &i : SubSystemNames) + LevelNamesArray.add(i); + Result.set(RESTAPI::Protocol::LIST, LevelNamesArray); + return ReturnObject(Result); + } else if (Command == RESTAPI::Protocol::STATS) { + + } else if (Command == RESTAPI::Protocol::RELOAD) { + if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) && + Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) { + auto SubSystems = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS); + std::vector Names; + for (const auto &i : *SubSystems) + Names.push_back(i.toString()); + std::thread ReloadThread([Names](){ + std::this_thread::sleep_for(10000ms); + for(const auto &i:Names) { + if(i=="daemon") + MicroServiceReload(); + else + MicroServiceReload(i); + } + }); + ReloadThread.detach(); + } + return OK(); + } + } else { + return BadRequest(RESTAPI::Errors::InvalidCommand); + } + BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + void DoPut() final {}; + void DoDelete() final {}; + }; + +} \ No newline at end of file diff --git a/src/framework/RESTAPI_WebSocketServer.h b/src/framework/RESTAPI_WebSocketServer.h new file mode 100644 index 0000000..5ce76b8 --- /dev/null +++ b/src/framework/RESTAPI_WebSocketServer.h @@ -0,0 +1,45 @@ +// +// Created by stephane bourque on 2022-10-26. +// + +#pragma once + +#include "framework/RESTAPI_Handler.h" +#include "Poco/Net/WebSocket.h" + +#include "framework/UI_WebSocketClientServer.h" +#include "framework/MicroServiceFuncs.h" + +namespace OpenWifi { + class RESTAPI_webSocketServer : public RESTAPIHandler { + public: + inline RESTAPI_webSocketServer(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal) + : RESTAPIHandler(bindings, L, + std::vector{ Poco::Net::HTTPRequest::HTTP_GET, + Poco::Net::HTTPRequest::HTTP_OPTIONS}, + Server, TransactionId, Internal,false) {} + static auto PathName() { return std::list{"/api/v1/ws"};} + void DoGet() final { + try + { + if(Request->find("Upgrade") != Request->end() && Poco::icompare((*Request)["Upgrade"], "websocket") == 0) { + try + { + Poco::Net::WebSocket WS(*Request, *Response); + auto Id = MicroServiceCreateUUID(); + UI_WebSocketClientServer()->NewClient(WS,Id,UserInfo_.userinfo.email); + } + catch (...) { + std::cout << "Cannot create websocket client..." << std::endl; + } + } + } catch(...) { + std::cout << "Cannot upgrade connection..." << std::endl; + } + }; + void DoDelete() final {}; + void DoPost() final {}; + void DoPut() final {}; + private: + }; +} \ No newline at end of file diff --git a/src/framework/RESTAPI_utils.h b/src/framework/RESTAPI_utils.h new file mode 100644 index 0000000..566b20f --- /dev/null +++ b/src/framework/RESTAPI_utils.h @@ -0,0 +1,484 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include + +#include "Poco/JSON/Object.h" +#include "Poco/JSON/Parser.h" +#include "Poco/Data/LOB.h" +#include "Poco/Net/HTTPServerRequest.h" + +#include "framework/OpenWifiTypes.h" +#include "framework/utils.h" + +namespace OpenWifi::RESTAPI_utils { + + inline void EmbedDocument(const std::string & ObjName, Poco::JSON::Object & Obj, const std::string &ObjStr) { + std::string D = ObjStr.empty() ? "{}" : ObjStr; + Poco::JSON::Parser P; + Poco::Dynamic::Var result = P.parse(D); + const auto &DetailsObj = result.extract(); + Obj.set(ObjName, DetailsObj); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, bool V) { + Obj.set(Field,V); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, double V) { + Obj.set(Field,V); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, float V) { + Obj.set(Field,V); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::string & S) { + Obj.set(Field,S); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const char * S) { + Obj.set(Field,S); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, int16_t Value) { + Obj.set(Field, Value); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, int32_t Value) { + Obj.set(Field, Value); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, int64_t Value) { + Obj.set(Field, Value); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint16_t Value) { + Obj.set(Field, Value); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint32_t Value) { + Obj.set(Field, Value); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint64_t Value) { + Obj.set(Field,Value); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Poco::Data::BLOB &Value) { + auto Result = Utils::base64encode((const unsigned char *)Value.rawContent(),Value.size()); + Obj.set(Field,Result); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringPairVec & S) { + Poco::JSON::Array Array; + for(const auto &i:S) { + Poco::JSON::Object O; + O.set("tag",i.first); + O.set("value", i.second); + Array.add(O); + } + Obj.set(Field,Array); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringVec &V) { + Poco::JSON::Array A; + for(const auto &i:V) + A.add(i); + Obj.set(Field,A); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::TagList &V) { + Poco::JSON::Array A; + for(const auto &i:V) + A.add(i); + Obj.set(Field,A); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::CountedMap &M) { + Poco::JSON::Array A; + for(const auto &[Key,Value]:M) { + Poco::JSON::Object O; + O.set("tag",Key); + O.set("value", Value); + A.add(O); + } + Obj.set(Field,A); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::Counted3DMapSII &M) { + Poco::JSON::Array A; + for(const auto &[OrgName,MonthlyNumberMap]:M) { + Poco::JSON::Object OrgObject; + OrgObject.set("tag",OrgName); + Poco::JSON::Array MonthlyArray; + for(const auto &[Month,Counter]:MonthlyNumberMap) { + Poco::JSON::Object Inner; + Inner.set("value", Month); + Inner.set("counter", Counter); + MonthlyArray.add(Inner); + } + OrgObject.set("index",MonthlyArray); + A.add(OrgObject); + } + Obj.set(Field, A); + } + + template void field_to_json(Poco::JSON::Object &Obj, + const char *Field, + const T &V, + std::function F) { + Obj.set(Field, F(V)); + } + + template void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::vector &Value) { + Poco::JSON::Array Arr; + for(const auto &i:Value) { + Poco::JSON::Object AO; + i.to_json(AO); + Arr.add(AO); + } + Obj.set(Field, Arr); + } + + template void field_to_json(Poco::JSON::Object &Obj, const char *Field, const T &Value) { + Poco::JSON::Object Answer; + Value.to_json(Answer); + Obj.set(Field, Answer); + } + + /////////////////////////// + /////////////////////////// + /////////////////////////// + /////////////////////////// + + template bool field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, T & V, + std::function F) { + if(Obj->has(Field) && !Obj->isNull(Field)) + V = F(Obj->get(Field).toString()); + return true; + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::string &S) { + if(Obj->has(Field) && !Obj->isNull(Field)) + S = Obj->get(Field).toString(); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, double & Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) + Value = (double)Obj->get(Field); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, float & Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) + Value = (float)Obj->get(Field); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, bool &Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) + Value = (Obj->get(Field).toString() == "true"); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, int16_t &Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) + Value = (int16_t)Obj->get(Field); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, int32_t &Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) + Value = (int32_t) Obj->get(Field); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, int64_t &Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) + Value = (int64_t)Obj->get(Field); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint16_t &Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) + Value = (uint16_t)Obj->get(Field); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint32_t &Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) + Value = (uint32_t)Obj->get(Field); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint64_t &Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) + Value = (uint64_t)Obj->get(Field); + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Poco::Data::BLOB &Value) { + if(Obj->has(Field) && !Obj->isNull(Field)) { + auto Result = Utils::base64decode(Obj->get(Field).toString()); + Value.assignRaw((const unsigned char *)&Result[0],Result.size()); + } + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::StringPairVec &Vec) { + if(Obj->isArray(Field) && !Obj->isNull(Field)) { + auto O = Obj->getArray(Field); + for(const auto &i:*O) { + std::string S1,S2; + auto Inner = i.extract(); + if(Inner->has("tag")) + S1 = Inner->get("tag").toString(); + if(Inner->has("value")) + S2 = Inner->get("value").toString(); + auto P = std::make_pair(S1,S2); + Vec.push_back(P); + } + } + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::StringVec &Value) { + if(Obj->isArray(Field) && !Obj->isNull(Field)) { + Value.clear(); + Poco::JSON::Array::Ptr A = Obj->getArray(Field); + for(const auto &i:*A) { + Value.push_back(i.toString()); + } + } + } + + inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::TagList &Value) { + if(Obj->isArray(Field) && !Obj->isNull(Field)) { + Value.clear(); + Poco::JSON::Array::Ptr A = Obj->getArray(Field); + for(const auto &i:*A) { + Value.push_back(i); + } + } + } + + template void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::vector &Value) { + if(Obj->isArray(Field) && !Obj->isNull(Field)) { + Poco::JSON::Array::Ptr Arr = Obj->getArray(Field); + for(auto &i:*Arr) { + auto InnerObj = i.extract(); + T NewItem; + NewItem.from_json(InnerObj); + Value.push_back(NewItem); + } + } + } + + template void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, T &Value) { + if(Obj->isObject(Field) && !Obj->isNull(Field)) { + Poco::JSON::Object::Ptr A = Obj->getObject(Field); + Value.from_json(A); + } + } + + inline std::string to_string(const Types::TagList & ObjectArray) { + Poco::JSON::Array OutputArr; + if(ObjectArray.empty()) + return "[]"; + for(auto const &i:ObjectArray) { + OutputArr.add(i); + } + std::ostringstream OS; + Poco::JSON::Stringifier::stringify(OutputArr,OS, 0,0, Poco::JSON_PRESERVE_KEY_ORDER ); + return OS.str(); + } + + inline std::string to_string(const Types::StringVec & ObjectArray) { + Poco::JSON::Array OutputArr; + if(ObjectArray.empty()) + return "[]"; + for(auto const &i:ObjectArray) { + OutputArr.add(i); + } + std::ostringstream OS; + Poco::JSON::Stringifier::condense(OutputArr,OS); + return OS.str(); + } + + inline std::string to_string(const Types::StringPairVec & ObjectArray) { + Poco::JSON::Array OutputArr; + if(ObjectArray.empty()) + return "[]"; + for(auto const &i:ObjectArray) { + Poco::JSON::Array InnerArray; + InnerArray.add(i.first); + InnerArray.add(i.second); + OutputArr.add(InnerArray); + } + std::ostringstream OS; + Poco::JSON::Stringifier::condense(OutputArr,OS); + return OS.str(); + } + + template std::string to_string(const std::vector & ObjectArray) { + Poco::JSON::Array OutputArr; + if(ObjectArray.empty()) + return "[]"; + for(auto const &i:ObjectArray) { + Poco::JSON::Object O; + i.to_json(O); + OutputArr.add(O); + } + std::ostringstream OS; + Poco::JSON::Stringifier::condense(OutputArr,OS); + return OS.str(); + } + + template std::string to_string(const std::vector> & ObjectArray) { + Poco::JSON::Array OutputArr; + if(ObjectArray.empty()) + return "[]"; + for(auto const &i:ObjectArray) { + Poco::JSON::Array InnerArr; + for(auto const &j:i) { + if constexpr(std::is_integral::value) { + InnerArr.add(j); + } if constexpr(std::is_same_v) { + InnerArr.add(j); + } else { + InnerArr.add(j); + Poco::JSON::Object O; + j.to_json(O); + InnerArr.add(O); + } + } + OutputArr.add(InnerArr); + } + std::ostringstream OS; + Poco::JSON::Stringifier::condense(OutputArr,OS); + return OS.str(); + } + + template std::string to_string(const T & Object) { + Poco::JSON::Object OutputObj; + Object.to_json(OutputObj); + std::ostringstream OS; + Poco::JSON::Stringifier::condense(OutputObj,OS); + return OS.str(); + } + + inline Types::StringVec to_object_array(const std::string & ObjectString) { + + Types::StringVec Result; + if(ObjectString.empty()) + return Result; + + try { + Poco::JSON::Parser P; + auto Object = P.parse(ObjectString).template extract(); + for (auto const &i : *Object) { + Result.push_back(i.toString()); + } + } catch (...) { + + } + return Result; + } + + inline OpenWifi::Types::TagList to_taglist(const std::string & ObjectString) { + Types::TagList Result; + if(ObjectString.empty()) + return Result; + + try { + Poco::JSON::Parser P; + auto Object = P.parse(ObjectString).template extract(); + for (auto const &i : *Object) { + Result.push_back(i); + } + } catch (...) { + + } + return Result; + } + + inline Types::StringPairVec to_stringpair_array(const std::string &S) { + Types::StringPairVec R; + if(S.empty()) + return R; + try { + Poco::JSON::Parser P; + auto Object = P.parse(S).template extract(); + for (const auto &i : *Object) { + auto InnerObject = i.template extract(); + if(InnerObject->size()==2) { + auto S1 = InnerObject->getElement(0); + auto S2 = InnerObject->getElement(1); + R.push_back(std::make_pair(S1,S2)); + } + } + } catch (...) { + + } + + return R; + } + + template std::vector to_object_array(const std::string & ObjectString) { + std::vector Result; + if(ObjectString.empty()) + return Result; + + try { + Poco::JSON::Parser P; + auto Object = P.parse(ObjectString).template extract(); + for (auto const &i : *Object) { + auto InnerObject = i.template extract(); + T Obj; + Obj.from_json(InnerObject); + Result.push_back(Obj); + } + } catch (...) { + + } + return Result; + } + + template std::vector> to_array_of_array_of_object(const std::string & ObjectString) { + std::vector> Result; + if(ObjectString.empty()) + return Result; + try { + Poco::JSON::Parser P1; + auto OutterArray = P1.parse(ObjectString).template extract(); + for (auto const &i : *OutterArray) { + Poco::JSON::Parser P2; + auto InnerArray = P2.parse(i).template extract(); + std::vector InnerVector; + for(auto const &j: *InnerArray) { + auto Object = j.template extract(); + T Obj; + Obj.from_json(Object); + InnerVector.push_back(Obj); + } + Result.push_back(InnerVector); + } + } catch (...) { + + } + return Result; + } + + template T to_object(const std::string & ObjectString) { + T Result; + + if(ObjectString.empty()) + return Result; + + Poco::JSON::Parser P; + auto Object = P.parse(ObjectString).template extract(); + Result.from_json(Object); + + return Result; + } + + template bool from_request(T & Obj, Poco::Net::HTTPServerRequest &Request) { + Poco::JSON::Parser IncomingParser; + auto RawObject = IncomingParser.parse(Request.stream()).extract(); + Obj.from_json(RawObject); + return true; + } +} diff --git a/src/framework/StorageClass.h b/src/framework/StorageClass.h index d9f3b98..e9f3feb 100644 --- a/src/framework/StorageClass.h +++ b/src/framework/StorageClass.h @@ -14,7 +14,8 @@ #include "Poco/Data/MySQL/Connector.h" #endif -#include "framework/MicroService.h" +#include "framework/SubSystemServer.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { enum DBType { @@ -34,7 +35,7 @@ namespace OpenWifi { std::lock_guard Guard(Mutex_); Logger().notice("Starting."); - std::string DBType = MicroService::instance().ConfigGetString("storage.type"); + std::string DBType = MicroServiceConfigGetString("storage.type",""); if (DBType == "sqlite") { Setup_SQLite(); @@ -72,9 +73,9 @@ namespace OpenWifi { inline int StorageClass::Setup_SQLite() { Logger().notice("SQLite StorageClass enabled."); dbType_ = sqlite; - auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db"); - int NumSessions = (int) MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64); - int IdleTime = (int) MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60); + auto DBName = MicroServiceDataDirectory() + "/" + MicroServiceConfigGetString("storage.type.sqlite.db",""); + int NumSessions = (int) MicroServiceConfigGetInt("storage.type.sqlite.maxsessions", 64); + int IdleTime = (int) MicroServiceConfigGetInt("storage.type.sqlite.idletime", 60); Poco::Data::SQLite::Connector::registerConnector(); // Pool_ = std::make_unique(new Poco::Data::SessionPool(SQLiteConn_.name(), DBName, 8, @@ -87,13 +88,13 @@ namespace OpenWifi { inline int StorageClass::Setup_MySQL() { Logger().notice("MySQL StorageClass enabled."); dbType_ = mysql; - int NumSessions = (int) MicroService::instance().ConfigGetInt("storage.type.mysql.maxsessions", 64); - int IdleTime = (int) MicroService::instance().ConfigGetInt("storage.type.mysql.idletime", 60); - auto Host = MicroService::instance().ConfigGetString("storage.type.mysql.host"); - auto Username = MicroService::instance().ConfigGetString("storage.type.mysql.username"); - auto Password = MicroService::instance().ConfigGetString("storage.type.mysql.password"); - auto Database = MicroService::instance().ConfigGetString("storage.type.mysql.database"); - auto Port = MicroService::instance().ConfigGetString("storage.type.mysql.port"); + int NumSessions = (int) MicroServiceConfigGetInt("storage.type.mysql.maxsessions", 64); + int IdleTime = (int) MicroServiceConfigGetInt("storage.type.mysql.idletime", 60); + auto Host = MicroServiceConfigGetString("storage.type.mysql.host",""); + auto Username = MicroServiceConfigGetString("storage.type.mysql.username",""); + auto Password = MicroServiceConfigGetString("storage.type.mysql.password",""); + auto Database = MicroServiceConfigGetString("storage.type.mysql.database",""); + auto Port = MicroServiceConfigGetString("storage.type.mysql.port",""); std::string ConnectionStr = "host=" + Host + @@ -112,14 +113,14 @@ namespace OpenWifi { inline int StorageClass::Setup_PostgreSQL() { Logger().notice("PostgreSQL StorageClass enabled."); dbType_ = pgsql; - int NumSessions = (int) MicroService::instance().ConfigGetInt("storage.type.postgresql.maxsessions", 64); - int IdleTime = (int) MicroService::instance().ConfigGetInt("storage.type.postgresql.idletime", 60); - auto Host = MicroService::instance().ConfigGetString("storage.type.postgresql.host"); - auto Username = MicroService::instance().ConfigGetString("storage.type.postgresql.username"); - auto Password = MicroService::instance().ConfigGetString("storage.type.postgresql.password"); - auto Database = MicroService::instance().ConfigGetString("storage.type.postgresql.database"); - auto Port = MicroService::instance().ConfigGetString("storage.type.postgresql.port"); - auto ConnectionTimeout = MicroService::instance().ConfigGetString("storage.type.postgresql.connectiontimeout"); + int NumSessions = (int) MicroServiceConfigGetInt("storage.type.postgresql.maxsessions", 64); + int IdleTime = (int) MicroServiceConfigGetInt("storage.type.postgresql.idletime", 60); + auto Host = MicroServiceConfigGetString("storage.type.postgresql.host", ""); + auto Username = MicroServiceConfigGetString("storage.type.postgresql.username", ""); + auto Password = MicroServiceConfigGetString("storage.type.postgresql.password", ""); + auto Database = MicroServiceConfigGetString("storage.type.postgresql.database", ""); + auto Port = MicroServiceConfigGetString("storage.type.postgresql.port", ""); + auto ConnectionTimeout = MicroServiceConfigGetString("storage.type.postgresql.connectiontimeout", ""); std::string ConnectionStr = "host=" + Host + diff --git a/src/framework/SubSystemServer.cpp b/src/framework/SubSystemServer.cpp new file mode 100644 index 0000000..0222a83 --- /dev/null +++ b/src/framework/SubSystemServer.cpp @@ -0,0 +1,320 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "fmt/format.h" + +#include "framework/SubSystemServer.h" + +#include "Poco/Net/SSLManager.h" +#include "Poco/DateTimeFormatter.h" +#include "Poco/DateTimeFormat.h" + +#include "framework/MicroServiceFuncs.h" + +namespace OpenWifi { + + PropertiesFileServerEntry::PropertiesFileServerEntry(std::string Address, uint32_t port, std::string Key_file, + std::string Cert_file, std::string RootCa, std::string Issuer, + std::string ClientCas, std::string Cas, + std::string Key_file_password, std::string Name, + Poco::Net::Context::VerificationMode M, + int backlog) + : address_(std::move(Address)), + port_(port), + cert_file_(std::move(Cert_file)), + key_file_(std::move(Key_file)), + root_ca_(std::move(RootCa)), + key_file_password_(std::move(Key_file_password)), + issuer_cert_file_(std::move(Issuer)), + client_cas_(std::move(ClientCas)), + cas_(std::move(Cas)), + name_(std::move(Name)), + backlog_(backlog), + level_(M) { + + }; + + [[nodiscard]] Poco::Net::SecureServerSocket PropertiesFileServerEntry::CreateSecureSocket(Poco::Logger &L) const { + Poco::Net::Context::Params P; + + P.verificationMode = level_; + P.verificationDepth = 9; + P.loadDefaultCAs = root_ca_.empty(); + P.cipherList = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"; + P.dhUse2048Bits = true; + P.caLocation = cas_; + + auto Context = Poco::AutoPtr(new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P)); + + if(!key_file_password_.empty()) { + auto PassphraseHandler = Poco::SharedPtr( new MyPrivateKeyPassphraseHandler(key_file_password_,L)); + Poco::Net::SSLManager::instance().initializeServer(PassphraseHandler, nullptr,Context); + } + + if (!cert_file_.empty() && !key_file_.empty()) { + Poco::Crypto::X509Certificate Cert(cert_file_); + Poco::Crypto::X509Certificate Root(root_ca_); + + Context->useCertificate(Cert); + Context->addChainCertificate(Root); + + Context->addCertificateAuthority(Root); + + if (level_ == Poco::Net::Context::VERIFY_STRICT) { + if (issuer_cert_file_.empty()) { + L.fatal("In strict mode, you must supply ans issuer certificate"); + } + if (client_cas_.empty()) { + L.fatal("In strict mode, client cas must be supplied"); + } + Poco::Crypto::X509Certificate Issuing(issuer_cert_file_); + Context->addChainCertificate(Issuing); + Context->addCertificateAuthority(Issuing); + } + + Poco::Crypto::RSAKey Key("", key_file_, key_file_password_); + Context->usePrivateKey(Key); + + SSL_CTX *SSLCtx = Context->sslContext(); + if (!SSL_CTX_check_private_key(SSLCtx)) { + L.fatal(fmt::format("Wrong Certificate({}) for Key({})", cert_file_, key_file_)); + } + + SSL_CTX_set_verify(SSLCtx, SSL_VERIFY_PEER, nullptr); + + if (level_ == Poco::Net::Context::VERIFY_STRICT) { + SSL_CTX_set_client_CA_list(SSLCtx, SSL_load_client_CA_file(client_cas_.c_str())); + } + SSL_CTX_enable_ct(SSLCtx, SSL_CT_VALIDATION_STRICT); + SSL_CTX_dane_enable(SSLCtx); + + Context->enableSessionCache(); + Context->setSessionCacheSize(0); + Context->setSessionTimeout(60); + Context->enableExtendedCertificateVerification(true); + Context->disableStatelessSessionResumption(); + } + + if (address_ == "*") { + Poco::Net::IPAddress Addr(Poco::Net::IPAddress::wildcard( + Poco::Net::Socket::supportsIPv6() ? Poco::Net::AddressFamily::IPv6 + : Poco::Net::AddressFamily::IPv4)); + Poco::Net::SocketAddress SockAddr(Addr, port_); + + return Poco::Net::SecureServerSocket(SockAddr, backlog_, Context); + } else { + Poco::Net::IPAddress Addr(address_); + Poco::Net::SocketAddress SockAddr(Addr, port_); + + return Poco::Net::SecureServerSocket(SockAddr, backlog_, Context); + } + } + + [[nodiscard]] Poco::Net::ServerSocket PropertiesFileServerEntry::CreateSocket([[maybe_unused]] Poco::Logger &L) const { + Poco::Net::Context::Params P; + + if (address_ == "*") { + Poco::Net::IPAddress Addr(Poco::Net::IPAddress::wildcard( + Poco::Net::Socket::supportsIPv6() ? Poco::Net::AddressFamily::IPv6 + : Poco::Net::AddressFamily::IPv4)); + Poco::Net::SocketAddress SockAddr(Addr, port_); + return Poco::Net::ServerSocket(SockAddr, backlog_); + } else { + Poco::Net::IPAddress Addr(address_); + Poco::Net::SocketAddress SockAddr(Addr, port_); + return Poco::Net::ServerSocket(SockAddr, backlog_); + } + } + + void PropertiesFileServerEntry::LogCertInfo(Poco::Logger &L, const Poco::Crypto::X509Certificate &C) const { + L.information("============================================================================================="); + L.information(fmt::format("> Issuer: {}", C.issuerName())); + L.information("---------------------------------------------------------------------------------------------"); + L.information(fmt::format("> Common Name: {}", + C.issuerName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); + L.information(fmt::format("> Country: {}", + C.issuerName(Poco::Crypto::X509Certificate::NID_COUNTRY))); + L.information(fmt::format("> Locality: {}", + C.issuerName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); + L.information(fmt::format("> State/Prov: {}", + C.issuerName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); + L.information(fmt::format("> Org name: {}", + C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); + L.information( + fmt::format("> Org unit: {}", + C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); + L.information( + fmt::format("> Email: {}", + C.issuerName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); + L.information(fmt::format("> Serial#: {}", + C.issuerName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); + L.information("---------------------------------------------------------------------------------------------"); + L.information(fmt::format("> Subject: {}", C.subjectName())); + L.information("---------------------------------------------------------------------------------------------"); + L.information(fmt::format("> Common Name: {}", + C.subjectName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); + L.information(fmt::format("> Country: {}", + C.subjectName(Poco::Crypto::X509Certificate::NID_COUNTRY))); + L.information(fmt::format("> Locality: {}", + C.subjectName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); + L.information( + fmt::format("> State/Prov: {}", + C.subjectName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); + L.information( + fmt::format("> Org name: {}", + C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); + L.information( + fmt::format("> Org unit: {}", + C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); + L.information( + fmt::format("> Email: {}", + C.subjectName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); + L.information(fmt::format("> Serial#: {}", + C.subjectName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); + L.information("---------------------------------------------------------------------------------------------"); + L.information(fmt::format("> Signature Algo: {}", C.signatureAlgorithm())); + auto From = Poco::DateTimeFormatter::format(C.validFrom(), Poco::DateTimeFormat::HTTP_FORMAT); + L.information(fmt::format("> Valid from: {}", From)); + auto Expires = + Poco::DateTimeFormatter::format(C.expiresOn(), Poco::DateTimeFormat::HTTP_FORMAT); + L.information(fmt::format("> Expires on: {}", Expires)); + L.information(fmt::format("> Version: {}", (int)C.version())); + L.information(fmt::format("> Serial #: {}", C.serialNumber())); + L.information("============================================================================================="); + } + + void PropertiesFileServerEntry::LogCert(Poco::Logger &L) const { + try { + Poco::Crypto::X509Certificate C(cert_file_); + L.information("============================================================================================="); + L.information("============================================================================================="); + L.information(fmt::format("Certificate Filename: {}", cert_file_)); + LogCertInfo(L, C); + L.information("============================================================================================="); + + if (!issuer_cert_file_.empty()) { + Poco::Crypto::X509Certificate C1(issuer_cert_file_); + L.information("============================================================================================="); + L.information("============================================================================================="); + L.information(fmt::format("Issues Certificate Filename: {}", issuer_cert_file_)); + LogCertInfo(L, C1); + L.information("============================================================================================="); + } + + if (!client_cas_.empty()) { + std::vector Certs = + Poco::Net::X509Certificate::readPEM(client_cas_); + + L.information("============================================================================================="); + L.information("============================================================================================="); + L.information(fmt::format("Client CAs Filename: {}", client_cas_)); + L.information("============================================================================================="); + auto i = 1; + for (const auto &C3 : Certs) { + L.information(fmt::format(" Index: {}", i)); + L.information("============================================================================================="); + LogCertInfo(L, C3); + i++; + } + L.information("============================================================================================="); + } + + } catch (const Poco::Exception &E) { + L.log(E); + } + } + + void PropertiesFileServerEntry::LogCas(Poco::Logger &L) const { + try { + std::vector Certs = + Poco::Net::X509Certificate::readPEM(root_ca_); + + L.information("============================================================================================="); + L.information("============================================================================================="); + L.information(fmt::format("CA Filename: {}", root_ca_)); + L.information("============================================================================================="); + auto i = 1; + for (const auto &C : Certs) { + L.information(fmt::format(" Index: {}", i)); + L.information("============================================================================================="); + LogCertInfo(L, C); + i++; + } + L.information("============================================================================================="); + } catch (const Poco::Exception &E) { + L.log(E); + } + } + + SubSystemServer::SubSystemServer(const std::string &Name, const std::string &LoggingPrefix, + const std::string &SubSystemConfigPrefix): + Name_(Name), + LoggerPrefix_(LoggingPrefix), + SubSystemConfigPrefix_(SubSystemConfigPrefix) { + } + + void SubSystemServer::initialize([[maybe_unused]] Poco::Util::Application &self) { + auto i = 0; + bool good = true; + + auto NewLevel = MicroServiceConfigGetString("logging.level." + Name_, ""); + if(NewLevel.empty()) + Logger_ = std::make_unique(Poco::Logger::create(LoggerPrefix_, Poco::Logger::root().getChannel(), Poco::Logger::root().getLevel())); + else + Logger_ = std::make_unique(Poco::Logger::create(LoggerPrefix_, Poco::Logger::root().getChannel(), Poco::Logger::parseLevel(NewLevel))); + + ConfigServersList_.clear(); + while (good) { + std::string root{SubSystemConfigPrefix_ + ".host." + std::to_string(i) + "."}; + + std::string address{root + "address"}; + if (MicroServiceConfigGetString(address, "").empty()) { + good = false; + } else { + std::string port{root + "port"}; + std::string key{root + "key"}; + std::string key_password{root + "key.password"}; + std::string cert{root + "cert"}; + std::string name{root + "name"}; + std::string backlog{root + "backlog"}; + std::string rootca{root + "rootca"}; + std::string issuer{root + "issuer"}; + std::string clientcas(root + "clientcas"); + std::string cas{root + "cas"}; + + std::string level{root + "security"}; + Poco::Net::Context::VerificationMode M = Poco::Net::Context::VERIFY_RELAXED; + + auto L = MicroServiceConfigGetString(level, ""); + + if (L == "strict") { + M = Poco::Net::Context::VERIFY_STRICT; + } else if (L == "none") { + M = Poco::Net::Context::VERIFY_NONE; + } else if (L == "relaxed") { + M = Poco::Net::Context::VERIFY_RELAXED; + } else if (L == "once") + M = Poco::Net::Context::VERIFY_ONCE; + + PropertiesFileServerEntry entry(MicroServiceConfigGetString(address, ""), + MicroServiceConfigGetInt(port, 0), + MicroServiceConfigPath(key, ""), + MicroServiceConfigPath(cert, ""), + MicroServiceConfigPath(rootca, ""), + MicroServiceConfigPath(issuer, ""), + MicroServiceConfigPath(clientcas, ""), + MicroServiceConfigPath(cas, ""), + MicroServiceConfigGetString(key_password, ""), + MicroServiceConfigGetString(name, ""), M, + (int)MicroServiceConfigGetInt(backlog, 64)); + ConfigServersList_.push_back(entry); + i++; + } + } + } + + + + +} // namespace OpenWifi \ No newline at end of file diff --git a/src/framework/SubSystemServer.h b/src/framework/SubSystemServer.h new file mode 100644 index 0000000..d7625d4 --- /dev/null +++ b/src/framework/SubSystemServer.h @@ -0,0 +1,124 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include +#include + +#include "Poco/Util/Application.h" +#include "Poco/Net/Context.h" +#include "Poco/Net/SecureServerSocket.h" +#include "Poco/Net/PrivateKeyPassphraseHandler.h" + +namespace OpenWifi { + + class MyPrivateKeyPassphraseHandler : public Poco::Net::PrivateKeyPassphraseHandler { + public: + explicit MyPrivateKeyPassphraseHandler(const std::string &Password, Poco::Logger & Logger): + PrivateKeyPassphraseHandler(true), + Password_(Password), + Logger_(Logger) { + } + + void onPrivateKeyRequested([[maybe_unused]] const void * pSender,std::string & privateKey) { + Logger_.information("Returning key passphrase."); + privateKey = Password_; + }; + inline Poco::Logger & Logger() { return Logger_; } + private: + std::string Password_; + Poco::Logger & Logger_; + }; + + class PropertiesFileServerEntry { + public: + PropertiesFileServerEntry(std::string Address, uint32_t port, std::string Key_file, + std::string Cert_file, std::string RootCa, std::string Issuer, + std::string ClientCas, std::string Cas, + std::string Key_file_password = "", std::string Name = "", + Poco::Net::Context::VerificationMode M = + Poco::Net::Context::VerificationMode::VERIFY_RELAXED, + int backlog = 64); + + [[nodiscard]] inline const std::string &Address() const { return address_; }; + [[nodiscard]] inline uint32_t Port() const { return port_; }; + [[nodiscard]] inline auto KeyFile() const { return key_file_; }; + [[nodiscard]] inline auto CertFile() const { return cert_file_; }; + [[nodiscard]] inline auto RootCA() const { return root_ca_; }; + [[nodiscard]] inline auto KeyFilePassword() const { return key_file_password_; }; + [[nodiscard]] inline auto IssuerCertFile() const { return issuer_cert_file_; }; + [[nodiscard]] inline auto Name() const { return name_; }; + [[nodiscard]] inline int Backlog() const { return backlog_; } + [[nodiscard]] inline auto Cas() const { return cas_; } + + [[nodiscard]] Poco::Net::SecureServerSocket CreateSecureSocket(Poco::Logger &L) const; + [[nodiscard]] Poco::Net::ServerSocket CreateSocket([[maybe_unused]] Poco::Logger &L) const; + void LogCertInfo(Poco::Logger &L, const Poco::Crypto::X509Certificate &C) const; + void LogCert(Poco::Logger &L) const; + void LogCas(Poco::Logger &L) const; + + private: + std::string address_; + uint32_t port_; + std::string cert_file_; + std::string key_file_; + std::string root_ca_; + std::string key_file_password_; + std::string issuer_cert_file_; + std::string client_cas_; + std::string cas_; + std::string name_; + int backlog_; + Poco::Net::Context::VerificationMode level_; + }; + + class SubSystemServer : public Poco::Util::Application::Subsystem { + public: + SubSystemServer(const std::string & Name, const std::string &LoggingPrefix, + const std::string & SubSystemConfigPrefix); + + void initialize(Poco::Util::Application &self) override; + inline void uninitialize() override { + } + inline void reinitialize([[maybe_unused]] Poco::Util::Application &self) override { + Logger_->L_.information("Reloading of this subsystem is not supported."); + } + inline void defineOptions([[maybe_unused]] Poco::Util::OptionSet &options) override { + } + inline const std::string & Name() const { return Name_; }; + inline const char * name() const override { return Name_.c_str(); } + + inline const PropertiesFileServerEntry & Host(uint64_t index) { return ConfigServersList_[index]; }; + inline uint64_t HostSize() const { return ConfigServersList_.size(); } + inline Poco::Logger & Logger() const { return Logger_->L_; } + inline void SetLoggingLevel(const std::string & levelName) { + Logger_->L_.setLevel(Poco::Logger::parseLevel(levelName)); + } + inline int GetLoggingLevel() { return Logger_->L_.getLevel(); } + + virtual int Start() = 0; + virtual void Stop() = 0; + + struct LoggerWrapper { + Poco::Logger & L_; + LoggerWrapper(Poco::Logger &L) : + L_(L) { + } + }; + + protected: + std::recursive_mutex Mutex_; + std::vector ConfigServersList_; + + private: + std::unique_ptr Logger_; + std::string Name_; + std::string LoggerPrefix_; + std::string SubSystemConfigPrefix_; + }; + + typedef std::vector SubSystemVec; + +} // namespace OpenWifi diff --git a/src/framework/UI_WebSocketClientServer.cpp b/src/framework/UI_WebSocketClientServer.cpp new file mode 100644 index 0000000..5246188 --- /dev/null +++ b/src/framework/UI_WebSocketClientServer.cpp @@ -0,0 +1,266 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include + +#include "Poco/NObserver.h" +#include "Poco/JSON/JSONException.h" +#include "Poco/JSON/Parser.h" +#include "Poco/Logger.h" + +#include "framework/UI_WebSocketClientServer.h" +#include "framework/AuthClient.h" +#include "framework/MicroServiceFuncs.h" + +#include "fmt/format.h" + +namespace OpenWifi { + + void UI_WebSocketClientServer::NewClient(Poco::Net::WebSocket & WS, const std::string &Id, const std::string &UserName ) { + std::lock_guard G(Mutex_); + auto Client = std::make_unique(WS,Id,UserName,Logger(), Processor_); + Clients_[Id] = std::make_pair(std::move(Client),""); + } + + void UI_WebSocketClientServer::SetProcessor( UI_WebSocketClientProcessor * F) { + Processor_ = F; + } + + void UI_WebSocketClientServer::UnRegister(const std::string &Id) { + std::lock_guard G(Mutex_); + Clients_.erase(Id); + } + + void UI_WebSocketClientServer::SetUser(const std::string &Id, const std::string &UserId) { + std::lock_guard G(Mutex_); + + auto it=Clients_.find(Id); + if(it!=Clients_.end()) { + Clients_[Id] = std::make_pair(std::move(it->second.first),UserId); + } + } + + [[nodiscard]] inline bool SendToUser(const std::string &userName, const std::string &Payload); + UI_WebSocketClientServer::UI_WebSocketClientServer() noexcept: + SubSystemServer("WebSocketClientServer", "UI-WSCLNT-SVR", "websocketclients") + { + } + + void UI_WebSocketClientServer::run() { + Running_ = true ; + Utils::SetThreadName("ws:uiclnt-svr"); + while(Running_) { + Poco::Thread::trySleep(2000); + + if(!Running_) + break; + } + }; + + int UI_WebSocketClientServer::Start() { + GoogleApiKey_ = MicroServiceConfigGetString("google.apikey",""); + GeoCodeEnabled_ = !GoogleApiKey_.empty(); + // ReactorPool_ = std::make_unique(); + ReactorThread_.start(Reactor_); + Thr_.start(*this); + return 0; + }; + + void UI_WebSocketClientServer::Stop() { + if(Running_) { + Reactor_.stop(); + ReactorThread_.join(); + Running_ = false; + Thr_.wakeUp(); + Thr_.join(); + } + }; + + bool UI_WebSocketClientServer::Send(const std::string &Id, const std::string &Payload) { + std::lock_guard G(Mutex_); + + auto It = Clients_.find(Id); + if(It!=Clients_.end()) + return It->second.first->Send(Payload); + return false; + } + + bool UI_WebSocketClientServer::SendToUser(const std::string &UserName, const std::string &Payload) { + std::lock_guard G(Mutex_); + uint64_t Sent=0; + + for(const auto &client:Clients_) { + if(client.second.second == UserName) { + try { + if (client.second.first->Send(Payload)) + Sent++; + } catch (...) { + return false; + } + } + } + return Sent>0; + } + + void UI_WebSocketClientServer::SendToAll(const std::string &Payload) { + std::lock_guard G(Mutex_); + + for(const auto &client:Clients_) { + try { + client.second.first->Send(Payload); + } catch (...) { + + } + } + } + + void UI_WebSocketClient::OnSocketError([[maybe_unused]] const Poco::AutoPtr &pNf) { + EndConnection(); + } + + void UI_WebSocketClient::OnSocketReadable([[maybe_unused]] const Poco::AutoPtr &pNf) { + int flags; + int n; + bool Done=false; + try { + Poco::Buffer IncomingFrame(0); + n = WS_->receiveFrame(IncomingFrame, flags); + auto Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK; + + if (n == 0) { + poco_debug(Logger(),fmt::format("CLOSE({}): {} UI Client is closing WS connection.", Id_, UserName_)); + return EndConnection(); + } + + switch (Op) { + case Poco::Net::WebSocket::FRAME_OP_PING: { + WS_->sendFrame("", 0, + (int)Poco::Net::WebSocket::FRAME_OP_PONG | + (int)Poco::Net::WebSocket::FRAME_FLAG_FIN); + } break; + case Poco::Net::WebSocket::FRAME_OP_PONG: { + } break; + case Poco::Net::WebSocket::FRAME_OP_CLOSE: { + poco_debug(Logger(),fmt::format("CLOSE({}): {} UI Client is closing WS connection.", Id_, UserName_)); + Done = true; + } break; + case Poco::Net::WebSocket::FRAME_OP_TEXT: { + IncomingFrame.append(0); + if (!Authenticated_) { + std::string Frame{IncomingFrame.begin()}; + auto Tokens = Utils::Split(Frame, ':'); + bool Expired = false, Contacted = false; + if (Tokens.size() == 2 && + AuthClient()->IsAuthorized(Tokens[1], UserInfo_, 0, Expired, Contacted)) { + Authenticated_ = true; + UserName_ = UserInfo_.userinfo.email; + poco_debug(Logger(),fmt::format("START({}): {} UI Client is starting WS connection.", Id_, UserName_)); + std::string S{"Welcome! Bienvenue! Bienvenidos!"}; + WS_->sendFrame(S.c_str(), S.size()); + UI_WebSocketClientServer()->SetUser(Id_, UserInfo_.userinfo.email); + } else { + std::string S{"Invalid token. Closing connection."}; + WS_->sendFrame(S.c_str(), S.size()); + Done = true; + } + + } else { + try { + Poco::JSON::Parser P; + auto Obj = + P.parse(IncomingFrame.begin()).extract(); + std::string Answer; + if (Processor_ != nullptr) + Processor_->Processor(Obj, Answer, Done); + if (!Answer.empty()) + WS_->sendFrame(Answer.c_str(), (int)Answer.size()); + else { + WS_->sendFrame("{}", 2); + } + } catch (const Poco::JSON::JSONException &E) { + Logger().log(E); + Done=true; + } + } + } break; + default: { + } + } + } catch (...) { + Done=true; + } + + if(Done) { + return EndConnection(); + } + } + + void UI_WebSocketClient::OnSocketShutdown([[maybe_unused]] const Poco::AutoPtr &pNf) { + EndConnection(); + } + + + UI_WebSocketClient::UI_WebSocketClient( Poco::Net::WebSocket & WS , const std::string &Id, const std::string &UserName, Poco::Logger & L, UI_WebSocketClientProcessor * Processor) : + Reactor_(UI_WebSocketClientServer()->Reactor()), + Id_(Id), + UserName_(UserName), + Logger_(L), + Processor_(Processor) { + WS_ = std::make_unique(WS); + WS_->setNoDelay(true); + WS_->setKeepAlive(true); + WS_->setBlocking(false); + Reactor_.addEventHandler(*WS_, + Poco::NObserver( + *this, &UI_WebSocketClient::OnSocketReadable)); + Reactor_.addEventHandler(*WS_, + Poco::NObserver( + *this, &UI_WebSocketClient::OnSocketShutdown)); + Reactor_.addEventHandler(*WS_, + Poco::NObserver( + *this, &UI_WebSocketClient::OnSocketError)); + SocketRegistered_ = true; + } + + void UI_WebSocketClient::EndConnection() { + if(SocketRegistered_) { + SocketRegistered_ = false; + (*WS_).shutdown(); + Reactor_.removeEventHandler(*WS_, + Poco::NObserver(*this,&UI_WebSocketClient::OnSocketReadable)); + Reactor_.removeEventHandler(*WS_, + Poco::NObserver(*this,&UI_WebSocketClient::OnSocketShutdown)); + Reactor_.removeEventHandler(*WS_, + Poco::NObserver(*this,&UI_WebSocketClient::OnSocketError)); + UI_WebSocketClientServer()->UnRegister(Id_); + } + } + + UI_WebSocketClient::~UI_WebSocketClient() { + EndConnection(); + } + + [[nodiscard]] const std::string & UI_WebSocketClient::Id() { + return Id_; + }; + + [[nodiscard]] Poco::Logger & UI_WebSocketClient::Logger() { + return Logger_; + } + + [[nodiscard]] bool UI_WebSocketClient::Send(const std::string &Payload) { + try { + WS_->sendFrame(Payload.c_str(),Payload.size()); + return true; + } catch (...) { + + } + return false; + } + + +} // namespace OpenWifi \ No newline at end of file diff --git a/src/framework/UI_WebSocketClientServer.h b/src/framework/UI_WebSocketClientServer.h new file mode 100644 index 0000000..4f6adf7 --- /dev/null +++ b/src/framework/UI_WebSocketClientServer.h @@ -0,0 +1,118 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include +#include + +#include "Poco/Runnable.h" +#include "Poco/Net/SocketReactor.h" +#include "Poco/Net/WebSocket.h" +#include "Poco/JSON/Object.h" +#include "Poco/Net/SocketNotification.h" + +#include "RESTObjects/RESTAPI_SecurityObjects.h" +#include "framework/SubSystemServer.h" +#include "framework/WebSocketClientNotifications.h" + +namespace OpenWifi { + + class UI_WebSocketClient; + + class UI_WebSocketClientProcessor { + public: + virtual void Processor(const Poco::JSON::Object::Ptr &O, std::string &Answer, bool &Done ) = 0; + private: + }; + + class UI_WebSocketClientServer : public SubSystemServer, Poco::Runnable { + + public: + static auto instance() { + static auto instance_ = new UI_WebSocketClientServer; + return instance_; + } + + int Start() override; + void Stop() override; + void run() override; + Poco::Net::SocketReactor & Reactor() { return Reactor_; } + void NewClient(Poco::Net::WebSocket &WS, const std::string &Id, const std::string &UserName); + void SetProcessor(UI_WebSocketClientProcessor *F); + void UnRegister(const std::string &Id); + void SetUser(const std::string &Id, const std::string &UserId); + [[nodiscard]] inline bool GeoCodeEnabled() const { return GeoCodeEnabled_; } + [[nodiscard]] inline std::string GoogleApiKey() const { return GoogleApiKey_; } + [[nodiscard]] bool Send(const std::string &Id, const std::string &Payload); + + template bool + SendUserNotification(const std::string &userName, const WebSocketNotification &Notification) { + + Poco::JSON::Object Payload; + Notification.to_json(Payload); + Poco::JSON::Object Msg; + Msg.set("notification",Payload); + std::ostringstream OO; + Msg.stringify(OO); + + return SendToUser(userName,OO.str()); + } + + template void SendNotification(const WebSocketNotification &Notification) { + Poco::JSON::Object Payload; + Notification.to_json(Payload); + Poco::JSON::Object Msg; + Msg.set("notification",Payload); + std::ostringstream OO; + Msg.stringify(OO); + SendToAll(OO.str()); + } + + [[nodiscard]] bool SendToUser(const std::string &userName, const std::string &Payload); + void SendToAll(const std::string &Payload); + + private: + mutable std::atomic_bool Running_ = false; + Poco::Thread Thr_; + Poco::Net::SocketReactor Reactor_; + Poco::Thread ReactorThread_; + bool GeoCodeEnabled_ = false; + std::string GoogleApiKey_; + std::map, std::string>> Clients_; + UI_WebSocketClientProcessor *Processor_ = nullptr; + UI_WebSocketClientServer() noexcept; + }; + + inline auto UI_WebSocketClientServer() { return UI_WebSocketClientServer::instance(); } + + class UI_WebSocketClient { + public: + explicit UI_WebSocketClient(Poco::Net::WebSocket &WS, + const std::string &Id, + const std::string &UserName, + Poco::Logger &L, + UI_WebSocketClientProcessor *Processor); + virtual ~UI_WebSocketClient(); + [[nodiscard]] inline const std::string &Id(); + [[nodiscard]] Poco::Logger &Logger(); + bool Send(const std::string &Payload); + void EndConnection(); + private: + std::unique_ptr WS_; + Poco::Net::SocketReactor &Reactor_; + std::string Id_; + std::string UserName_; + Poco::Logger &Logger_; + std::atomic_bool Authenticated_ = false; + volatile bool SocketRegistered_=false; + SecurityObjects::UserInfoAndPolicy UserInfo_; + UI_WebSocketClientProcessor *Processor_ = nullptr; + void OnSocketReadable(const Poco::AutoPtr &pNf); + void OnSocketShutdown(const Poco::AutoPtr &pNf); + void OnSocketError(const Poco::AutoPtr &pNf); + }; + +}; + diff --git a/src/framework/WebSocketClientNotification.cpp b/src/framework/WebSocketClientNotification.cpp new file mode 100644 index 0000000..1bfedc9 --- /dev/null +++ b/src/framework/WebSocketClientNotification.cpp @@ -0,0 +1,146 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "framework/WebSocketClientNotifications.h" +#include "framework/UI_WebSocketClientServer.h" + +namespace OpenWifi { + + + void WebSocketClientNotificationNumberOfConnections(std::uint64_t numberOfDevices, + std::uint64_t averageConnectedTime, + std::uint64_t numberOfConnectingDevices) { + WebSocketNotification N; + N.content.numberOfDevices = numberOfDevices; + N.content.averageConnectedTime = averageConnectedTime; + N.content.numberOfConnectingDevices = numberOfConnectingDevices; + N.type = "device_connections_statistics"; + UI_WebSocketClientServer()->SendNotification(N); + } + + void WebSocketClientNotificationDeviceConfigurationChange(const std::string &SerialNumber, uint64_t oldUUID, uint64_t newUUID) { + WebSocketNotification N; + N.content.serialNumber = SerialNumber; + N.content.oldUUID = oldUUID; + N.content.newUUID = newUUID; + N.type = "device_configuration_upgrade"; + UI_WebSocketClientServer()->SendNotification(N); + } + + void WebSocketClientNotificationDeviceFirmwareUpdated(const std::string &SerialNumber, const std::string &Firmware) { + WebSocketNotification N; + N.content.serialNumber = SerialNumber; + N.content.newFirmware = Firmware; + N.type = "device_firmware_upgrade"; + UI_WebSocketClientServer()->SendNotification(N); + } + + void WebSocketClientNotificationDeviceConnected(const std::string &SerialNumber) { + WebSocketNotification N; + N.content.serialNumber = SerialNumber; + N.type = "device_connection"; + UI_WebSocketClientServer()->SendNotification(N); + } + + void WebSocketClientNotificationDeviceDisconnected(const std::string & SerialNumber) { + WebSocketNotification N; + N.content.serialNumber = SerialNumber; + N.type = "device_disconnection"; + UI_WebSocketClientServer()->SendNotification(N); + } + + void WebSocketNotificationJobContent::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj,"title",title); + RESTAPI_utils::field_to_json(Obj,"jobId",jobId); + RESTAPI_utils::field_to_json(Obj,"success",success); + RESTAPI_utils::field_to_json(Obj,"error",error); + RESTAPI_utils::field_to_json(Obj,"warning",warning); + RESTAPI_utils::field_to_json(Obj,"timeStamp",timeStamp); + RESTAPI_utils::field_to_json(Obj,"details",details); + } + + bool WebSocketNotificationJobContent::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json(Obj,"title",title); + RESTAPI_utils::field_from_json(Obj,"jobId",jobId); + RESTAPI_utils::field_from_json(Obj,"success",success); + RESTAPI_utils::field_from_json(Obj,"error",error); + RESTAPI_utils::field_from_json(Obj,"warning",warning); + RESTAPI_utils::field_from_json(Obj,"timeStamp",timeStamp); + RESTAPI_utils::field_from_json(Obj,"details",details); + return true; + } catch(...) { + + } + return false; + } + + void WebSocketClientNotificationVenueUpdateJobCompletionToUser( const std::string & User, WebSocketClientNotificationVenueUpdateJob_t &N) { + N.type = "venue_configuration_update"; + UI_WebSocketClientServer()->SendUserNotification(User,N); + } + + void WebSocketNotificationRebootList::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj,"title",title); + RESTAPI_utils::field_to_json(Obj,"jobId",jobId); + RESTAPI_utils::field_to_json(Obj,"success",success); + RESTAPI_utils::field_to_json(Obj,"warning",warning); + RESTAPI_utils::field_to_json(Obj,"timeStamp",timeStamp); + RESTAPI_utils::field_to_json(Obj,"details",details); + } + + bool WebSocketNotificationRebootList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json(Obj,"title",title); + RESTAPI_utils::field_from_json(Obj,"jobId",jobId); + RESTAPI_utils::field_from_json(Obj,"success",success); + RESTAPI_utils::field_from_json(Obj,"warning",warning); + RESTAPI_utils::field_from_json(Obj,"timeStamp",timeStamp); + RESTAPI_utils::field_from_json(Obj,"details",details); + return true; + } catch(...) { + + } + return false; + } + + void WebSocketClientNotificationVenueRebootCompletionToUser( const std::string & User, WebSocketClientNotificationVenueRebootList_t &N) { + N.type = "venue_rebooter"; + UI_WebSocketClientServer()->SendUserNotification(User,N); + } + + void WebSocketNotificationUpgradeList::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj,"title",title); + RESTAPI_utils::field_to_json(Obj,"jobId",jobId); + RESTAPI_utils::field_to_json(Obj,"success",success); + RESTAPI_utils::field_to_json(Obj,"notConnected",not_connected); + RESTAPI_utils::field_to_json(Obj,"noFirmware",no_firmware); + RESTAPI_utils::field_to_json(Obj,"skipped",skipped); + RESTAPI_utils::field_to_json(Obj,"timeStamp",timeStamp); + RESTAPI_utils::field_to_json(Obj,"details",details); + } + + bool WebSocketNotificationUpgradeList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json(Obj,"title",title); + RESTAPI_utils::field_from_json(Obj,"jobId",jobId); + RESTAPI_utils::field_from_json(Obj,"success",success); + RESTAPI_utils::field_from_json(Obj,"notConnected",not_connected); + RESTAPI_utils::field_from_json(Obj,"noFirmware",no_firmware); + RESTAPI_utils::field_from_json(Obj,"skipped",skipped); + RESTAPI_utils::field_from_json(Obj,"timeStamp",timeStamp); + RESTAPI_utils::field_from_json(Obj,"details",details); + return true; + } catch(...) { + + } + return false; + } + + void WebSocketClientNotificationVenueUpgradeCompletionToUser( const std::string & User, WebSocketClientNotificationVenueUpgradeList_t &N) { + N.type = "venue_upgrader"; + UI_WebSocketClientServer()->SendUserNotification(User,N); + } +} + diff --git a/src/framework/WebSocketClientNotifications.h b/src/framework/WebSocketClientNotifications.h index 435732c..2687f3c 100644 --- a/src/framework/WebSocketClientNotifications.h +++ b/src/framework/WebSocketClientNotifications.h @@ -4,10 +4,39 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" +#include "framework/utils.h" namespace OpenWifi { + template struct WebSocketNotification { + inline static uint64_t xid=1; + uint64_t notification_id=++xid; + std::string type; + ContentStruct content; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + template void WebSocketNotification::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj,"notification_id",notification_id); + RESTAPI_utils::field_to_json(Obj,"type",type); + RESTAPI_utils::field_to_json(Obj,"content",content); + } + + template bool WebSocketNotification::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json(Obj,"notification_id",notification_id); + RESTAPI_utils::field_from_json(Obj,"content",content); + RESTAPI_utils::field_from_json(Obj,"type",type); + return true; + } catch(...) { + + } + return false; + } + struct WebNotificationSingleDevice { std::string serialNumber; inline void to_json(Poco::JSON::Object &Obj) const { @@ -69,36 +98,37 @@ namespace OpenWifi { } }; - inline void WebSocketClientNotificationDeviceConfigurationChange(const std::string &SerialNumber, uint64_t oldUUID, uint64_t newUUID) { - WebSocketNotification N; - N.content.serialNumber = SerialNumber; - N.content.oldUUID = oldUUID; - N.content.newUUID = newUUID; - N.type = "device_configuration_upgrade"; - WebSocketClientServer()->SendNotification(N); - } + struct WebSocketClientNotificationNumberOfConnection { + std::uint64_t numberOfDevices=0; + std::uint64_t averageConnectedTime=0; + std::uint64_t numberOfConnectingDevices=0; - inline void WebSocketClientNotificationDeviceFirmwareUpdated(const std::string &SerialNumber, const std::string &Firmware) { - WebSocketNotification N; - N.content.serialNumber = SerialNumber; - N.content.newFirmware = Firmware; - N.type = "device_firmware_upgrade"; - WebSocketClientServer()->SendNotification(N); - } + inline void to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj,"numberOfDevices", numberOfDevices); + RESTAPI_utils::field_to_json(Obj,"averageConnectedTime", averageConnectedTime); + RESTAPI_utils::field_to_json(Obj,"numberOfConnectingDevices", numberOfConnectingDevices); + } - inline void WebSocketClientNotificationDeviceConnected(const std::string &SerialNumber) { - WebSocketNotification N; - N.content.serialNumber = SerialNumber; - N.type = "device_connection"; - WebSocketClientServer()->SendNotification(N); - } + inline bool from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json(Obj,"numberOfDevices", numberOfDevices); + RESTAPI_utils::field_from_json(Obj,"averageConnectedTime", averageConnectedTime); + RESTAPI_utils::field_from_json(Obj,"numberOfConnectingDevices", numberOfConnectingDevices); + return true; + } catch (...) { - inline void WebSocketClientNotificationDeviceDisconnected(const std::string & SerialNumber) { - WebSocketNotification N; - N.content.serialNumber = SerialNumber; - N.type = "device_disconnection"; - WebSocketClientServer()->SendNotification(N); - } + } + return false; + } + }; + + void WebSocketClientNotificationNumberOfConnections(std::uint64_t numberOfDevices, + std::uint64_t averageConnectedTime, + std::uint64_t numberOfConnectingDevices); + void WebSocketClientNotificationDeviceConfigurationChange(const std::string &SerialNumber, uint64_t oldUUID, uint64_t newUUID); + void WebSocketClientNotificationDeviceFirmwareUpdated(const std::string &SerialNumber, const std::string &Firmware); + void WebSocketClientNotificationDeviceConnected(const std::string &SerialNumber); + void WebSocketClientNotificationDeviceDisconnected(const std::string & SerialNumber); struct WebSocketNotificationJobContent { std::string title, @@ -107,44 +137,15 @@ namespace OpenWifi { std::vector success, error, warning; - uint64_t timeStamp=OpenWifi::Now(); + uint64_t timeStamp=OpenWifi::Utils::Now(); void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; - inline void WebSocketNotificationJobContent::to_json(Poco::JSON::Object &Obj) const { - RESTAPI_utils::field_to_json(Obj,"title",title); - RESTAPI_utils::field_to_json(Obj,"jobId",jobId); - RESTAPI_utils::field_to_json(Obj,"success",success); - RESTAPI_utils::field_to_json(Obj,"error",error); - RESTAPI_utils::field_to_json(Obj,"warning",warning); - RESTAPI_utils::field_to_json(Obj,"timeStamp",timeStamp); - RESTAPI_utils::field_to_json(Obj,"details",details); - } - - inline bool WebSocketNotificationJobContent::from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - RESTAPI_utils::field_from_json(Obj,"title",title); - RESTAPI_utils::field_from_json(Obj,"jobId",jobId); - RESTAPI_utils::field_from_json(Obj,"success",success); - RESTAPI_utils::field_from_json(Obj,"error",error); - RESTAPI_utils::field_from_json(Obj,"warning",warning); - RESTAPI_utils::field_from_json(Obj,"timeStamp",timeStamp); - RESTAPI_utils::field_from_json(Obj,"details",details); - return true; - } catch(...) { - - } - return false; - } - typedef WebSocketNotification WebSocketClientNotificationVenueUpdateJob_t; - inline void WebSocketClientNotificationVenueUpdateJobCompletionToUser( const std::string & User, WebSocketClientNotificationVenueUpdateJob_t &N) { - N.type = "venue_configuration_update"; - WebSocketClientServer()->SendUserNotification(User,N); - } + void WebSocketClientNotificationVenueUpdateJobCompletionToUser( const std::string & User, WebSocketClientNotificationVenueUpdateJob_t &N); ///// ///// @@ -156,7 +157,7 @@ namespace OpenWifi { jobId; std::vector success, warning; - uint64_t timeStamp=OpenWifi::Now(); + uint64_t timeStamp=OpenWifi::Utils::Now(); void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); @@ -164,38 +165,7 @@ namespace OpenWifi { typedef WebSocketNotification WebSocketClientNotificationVenueRebootList_t; - inline void WebSocketNotificationRebootList::to_json(Poco::JSON::Object &Obj) const { - RESTAPI_utils::field_to_json(Obj,"title",title); - RESTAPI_utils::field_to_json(Obj,"jobId",jobId); - RESTAPI_utils::field_to_json(Obj,"success",success); - RESTAPI_utils::field_to_json(Obj,"warning",warning); - RESTAPI_utils::field_to_json(Obj,"timeStamp",timeStamp); - RESTAPI_utils::field_to_json(Obj,"details",details); - } - - inline bool WebSocketNotificationRebootList::from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - RESTAPI_utils::field_from_json(Obj,"title",title); - RESTAPI_utils::field_from_json(Obj,"jobId",jobId); - RESTAPI_utils::field_from_json(Obj,"success",success); - RESTAPI_utils::field_from_json(Obj,"warning",warning); - RESTAPI_utils::field_from_json(Obj,"timeStamp",timeStamp); - RESTAPI_utils::field_from_json(Obj,"details",details); - return true; - } catch(...) { - - } - return false; - } - - inline void WebSocketClientNotificationVenueRebootCompletionToUser( const std::string & User, WebSocketClientNotificationVenueRebootList_t &N) { - N.type = "venue_rebooter"; - WebSocketClientServer()->SendUserNotification(User,N); - } - - ///// - ///// - ///// + void WebSocketClientNotificationVenueRebootCompletionToUser( const std::string & User, WebSocketClientNotificationVenueRebootList_t &N); struct WebSocketNotificationUpgradeList { std::string title, @@ -205,7 +175,7 @@ namespace OpenWifi { skipped, no_firmware, not_connected; - uint64_t timeStamp=OpenWifi::Now(); + uint64_t timeStamp=OpenWifi::Utils::Now(); void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); @@ -213,38 +183,8 @@ namespace OpenWifi { typedef WebSocketNotification WebSocketClientNotificationVenueUpgradeList_t; - inline void WebSocketNotificationUpgradeList::to_json(Poco::JSON::Object &Obj) const { - RESTAPI_utils::field_to_json(Obj,"title",title); - RESTAPI_utils::field_to_json(Obj,"jobId",jobId); - RESTAPI_utils::field_to_json(Obj,"success",success); - RESTAPI_utils::field_to_json(Obj,"notConnected",not_connected); - RESTAPI_utils::field_to_json(Obj,"noFirmware",no_firmware); - RESTAPI_utils::field_to_json(Obj,"skipped",skipped); - RESTAPI_utils::field_to_json(Obj,"timeStamp",timeStamp); - RESTAPI_utils::field_to_json(Obj,"details",details); - } - inline bool WebSocketNotificationUpgradeList::from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - RESTAPI_utils::field_from_json(Obj,"title",title); - RESTAPI_utils::field_from_json(Obj,"jobId",jobId); - RESTAPI_utils::field_from_json(Obj,"success",success); - RESTAPI_utils::field_from_json(Obj,"notConnected",not_connected); - RESTAPI_utils::field_from_json(Obj,"noFirmware",no_firmware); - RESTAPI_utils::field_from_json(Obj,"skipped",skipped); - RESTAPI_utils::field_from_json(Obj,"timeStamp",timeStamp); - RESTAPI_utils::field_from_json(Obj,"details",details); - return true; - } catch(...) { - - } - return false; - } - - inline void WebSocketClientNotificationVenueUpgradeCompletionToUser( const std::string & User, WebSocketClientNotificationVenueUpgradeList_t &N) { - N.type = "venue_upgrader"; - WebSocketClientServer()->SendUserNotification(User,N); - } + void WebSocketClientNotificationVenueUpgradeCompletionToUser( const std::string & User, WebSocketClientNotificationVenueUpgradeList_t &N); } // namespace OpenWifi diff --git a/src/framework/WebSocketLogger.h b/src/framework/WebSocketLogger.h new file mode 100644 index 0000000..6ff6dfe --- /dev/null +++ b/src/framework/WebSocketLogger.h @@ -0,0 +1,89 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "framework/SubSystemServer.h" + +namespace OpenWifi { + + class WebSocketLogger : public Poco::Channel { + public: + + inline std::string getProperty( [[maybe_unused]] const std::string &p ) const final { + return ""; + } + + inline void close() final { + } + + inline void open() final { + } + + inline static std::string to_string(Poco::Message::Priority p) { + switch(p) { + case Poco::Message::PRIO_INFORMATION: return "information"; + case Poco::Message::PRIO_CRITICAL: return "critical"; + case Poco::Message::PRIO_DEBUG: return "debug"; + case Poco::Message::PRIO_ERROR: return "error"; + case Poco::Message::PRIO_FATAL: return "level"; + case Poco::Message::PRIO_NOTICE: return "notice"; + case Poco::Message::PRIO_TRACE: return "trace"; + case Poco::Message::PRIO_WARNING: return "warning"; + default: return "none"; + } + } + + inline void log(const Poco::Message &m) final { + if(Enabled_) { + /* + nlohmann::json log_msg; + log_msg["msg"] = m.getText(); + log_msg["level"] = to_string(m.getPriority()); + log_msg["timestamp"] = Poco::DateTimeFormatter::format(m.getTime(), Poco::DateTimeFormat::ISO8601_FORMAT); + log_msg["source"] = m.getSource(); + log_msg["thread_name"] = m.getThread(); + log_msg["thread_id"] = m.getTid(); + + std::cout << log_msg << std::endl; + */ + std::lock_guard G(Mutex_); + std::vector Remove; + for(const auto &[Id,CallBack]:CallBacks_) { + try { + CallBack(m); + } catch (...) { + Remove.push_back(Id); + } + } + for(const auto &i:Remove) + CallBacks_.erase(i); + } + } + + inline void setProperty([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) final { + + } + + inline static auto instance() { + static auto instance_ = new WebSocketLogger; + return instance_; + } + inline void Enable(bool enable) { Enabled_ = enable; } + typedef std::function logmuxer_callback_func_t; + inline void RegisterCallback(const logmuxer_callback_func_t & R, uint64_t &Id) { + std::lock_guard G(Mutex_); + Id = CallBackId_++; + CallBacks_[Id] = R; + } + private: + std::recursive_mutex Mutex_; + std::map CallBacks_; + inline static uint64_t CallBackId_=1; + bool Enabled_ = false; + }; + + inline auto WebSocketLogger() { return WebSocketLogger::instance(); } + +} \ No newline at end of file diff --git a/src/framework/ow_constants.h b/src/framework/ow_constants.h index 5d70763..3b258a2 100644 --- a/src/framework/ow_constants.h +++ b/src/framework/ow_constants.h @@ -6,6 +6,7 @@ #include #include + #include "Poco/String.h" #if defined(__GNUC__) @@ -18,6 +19,26 @@ #pragma clang diagnostic ignored "-Wunused-variable" #endif +namespace OpenWifi { + enum UNAUTHORIZED_REASON { + SUCCESS=0, + PASSWORD_CHANGE_REQUIRED, + INVALID_CREDENTIALS, + PASSWORD_ALREADY_USED, + USERNAME_PENDING_VERIFICATION, + PASSWORD_INVALID, + INTERNAL_ERROR, + ACCESS_DENIED, + INVALID_TOKEN, + EXPIRED_TOKEN, + RATE_LIMIT_EXCEEDED, + BAD_MFA_TRANSACTION, + MFA_FAILURE, + SECURITY_SERVICE_UNREACHABLE, + CANNOT_REFRESH_TOKEN + }; +} + namespace OpenWifi::RESTAPI::Errors { struct msg { uint64_t err_num; std::string err_txt; }; static const struct msg Error404{404,"Resource does not exist."}; diff --git a/src/framework/utils.cpp b/src/framework/utils.cpp new file mode 100644 index 0000000..7983727 --- /dev/null +++ b/src/framework/utils.cpp @@ -0,0 +1,523 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "Poco/Path.h" + +#include "framework/utils.h" +#include "framework/AppServiceRegistry.h" + +namespace OpenWifi::Utils { + +bool NormalizeMac(std::string & Mac) { + Poco::replaceInPlace(Mac,":",""); + Poco::replaceInPlace(Mac,"-",""); + if(Mac.size()!=12) + return false; + for(const auto &i:Mac) { + if(!std::isxdigit(i)) + return false; + } + Poco::toLowerInPlace(Mac); + return true; +} + +[[nodiscard]] bool ValidSerialNumber(const std::string &Serial) { + return ((Serial.size() < uCentralProtocol::SERIAL_NUMBER_LENGTH) && + std::all_of(Serial.begin(),Serial.end(),[](auto i){return std::isxdigit(i);})); +} + +[[nodiscard]] bool ValidUUID(const std::string &UUID) { + if(UUID.size()>36) + return false; + uint dashes=0; + return (std::all_of(UUID.begin(),UUID.end(),[&](auto i){ if(i=='-') dashes++; return i=='-' || std::isxdigit(i);})) && (dashes>0); +} + +[[nodiscard]] std::vector Split(const std::string &List, char Delimiter ) { + std::vector ReturnList; + + unsigned long P=0; + + while(P12) + R = R.substr(0,12); + + char buf[18]; + + buf[0] = R[0]; buf[1] = R[1] ; buf[2] = ':' ; + buf[3] = R[2] ; buf[4] = R[3]; buf[5] = ':' ; + buf[6] = R[4]; buf[7] = R[5] ; buf[8] = ':' ; + buf[9] = R[6] ; buf[10]= R[7]; buf[11] = ':'; + buf[12] = R[8] ; buf[13]= R[9]; buf[14] = ':'; + buf[15] = R[10] ; buf[16]= R[11];buf[17] = 0; + + return buf; +} + +uint64_t MACToInt(const std::string &MAC) { + uint64_t Result = 0 ; + for(const auto &c:MAC) { + if(c==':') + continue; + Result <<= 4; + if(c>='0' && c<='9') { + Result += (c - '0'); + } else if (c>='a' && c<='f') { + Result += (c-'a'+10); + } else if (c>='A' && c<='F') { + Result += (c-'A'+10); + } + } + return Result; +} + +[[nodiscard]] std::string ToHex(const std::vector & B) { + std::string R; + R.reserve(B.size()*2); + + static const char hex[] = "0123456789abcdef"; + + for(const auto &i:B) + { + R += (hex[ (i & 0xf0) >> 4]); + R += (hex[ (i & 0x0f) ]); + } + + return R; +} + +inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +inline static const char kPadCharacter = '='; + +using byte = std::uint8_t; + +[[nodiscard]] std::string base64encode(const byte *input, uint32_t size) { + std::string encoded; + encoded.reserve(((size / 3) + (size % 3 > 0)) * 4); + + std::uint32_t temp,i,ee; + ee = (size/3); + + for (i = 0; i < 3*ee; ++i) { + temp = input[i++] << 16; + temp += input[i++] << 8; + temp += input[i]; + encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); + encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); + encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]); + encoded.append(1, kEncodeLookup[(temp & 0x0000003F)]); + } + + switch (size % 3) { + case 1: + temp = input[i] << 16; + encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); + encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); + encoded.append(2, kPadCharacter); + break; + case 2: + temp = input[i++] << 16; + temp += input[i] << 8; + encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); + encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); + encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]); + encoded.append(1, kPadCharacter); + break; + } + + return encoded; +} + +[[nodiscard]] std::vector base64decode(const std::string& input) +{ + if(input.length() % 4) + throw std::runtime_error("Invalid base64 length!"); + + std::size_t padding=0; + + if(input.length()) + { + if(input[input.length() - 1] == kPadCharacter) padding++; + if(input[input.length() - 2] == kPadCharacter) padding++; + } + + std::vector decoded; + decoded.reserve(((input.length() / 4) * 3) - padding); + + std::uint32_t temp=0; + auto it = input.begin(); + + while(it < input.end()) + { + for(std::size_t i = 0; i < 4; ++i) + { + temp <<= 6; + if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41; + else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47; + else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04; + else if(*it == 0x2B) temp |= 0x3E; + else if(*it == 0x2F) temp |= 0x3F; + else if(*it == kPadCharacter) + { + switch(input.end() - it) + { + case 1: + decoded.push_back((temp >> 16) & 0x000000FF); + decoded.push_back((temp >> 8 ) & 0x000000FF); + return decoded; + case 2: + decoded.push_back((temp >> 10) & 0x000000FF); + return decoded; + default: + throw std::runtime_error("Invalid padding in base64!"); + } + } + else throw std::runtime_error("Invalid character in base64!"); + + ++it; + } + + decoded.push_back((temp >> 16) & 0x000000FF); + decoded.push_back((temp >> 8 ) & 0x000000FF); + decoded.push_back((temp ) & 0x000000FF); + } + + return decoded; +} + +bool ParseTime(const std::string &Time, int & Hours, int & Minutes, int & Seconds) { + Poco::StringTokenizer TimeTokens(Time,":",Poco::StringTokenizer::TOK_TRIM); + + Hours = Minutes = Seconds = 0 ; + if(TimeTokens.count()==1) { + Hours = std::atoi(TimeTokens[0].c_str()); + } else if(TimeTokens.count()==2) { + Hours = std::atoi(TimeTokens[0].c_str()); + Minutes = std::atoi(TimeTokens[1].c_str()); + } else if(TimeTokens.count()==3) { + Hours = std::atoi(TimeTokens[0].c_str()); + Minutes = std::atoi(TimeTokens[1].c_str()); + Seconds = std::atoi(TimeTokens[2].c_str()); + } else + return false; + return true; +} + + +bool ParseDate(const std::string &Time, int & Year, int & Month, int & Day) { + Poco::StringTokenizer DateTokens(Time,"-",Poco::StringTokenizer::TOK_TRIM); + + Year = Month = Day = 0 ; + if(DateTokens.count()==3) { + Year = std::atoi(DateTokens[0].c_str()); + Month = std::atoi(DateTokens[1].c_str()); + Day = std::atoi(DateTokens[2].c_str()); + } else + return false; + return true; +} + +bool CompareTime( int H1, int H2, int M1, int M2, int S1, int S2) { + if(H1H2) + return false; + if(M1M1) + return false; + if(S1<=S2) + return true; + return false; +} + +[[nodiscard]] std::string LogLevelToString(int Level) { + switch(Level) { + case Poco::Message::PRIO_DEBUG: return "debug"; + case Poco::Message::PRIO_INFORMATION: return "information"; + case Poco::Message::PRIO_FATAL: return "fatal"; + case Poco::Message::PRIO_WARNING: return "warning"; + case Poco::Message::PRIO_NOTICE: return "notice"; + case Poco::Message::PRIO_CRITICAL: return "critical"; + case Poco::Message::PRIO_ERROR: return "error"; + case Poco::Message::PRIO_TRACE: return "trace"; + default: return "none"; + } +} + +[[nodiscard]] uint64_t SerialNumberToInt(const std::string & S) { + return std::stoull(S,nullptr,16); +} + +[[nodiscard]] std::string IntToSerialNumber(uint64_t S) { + char b[16]; + for(int i=0;i<12;++i) { + int B = (S & 0x0f); + if(B<10) + b[11-i] = B+'0'; + else + b[11-i] = B - 10 + 'a'; + S >>= 4 ; + } + b[12]=0; + return b; +} + + +[[nodiscard]] bool SerialNumberMatch(const std::string &S1, const std::string &S2, int Bits) { + auto S1_i = SerialNumberToInt(S1); + auto S2_i = SerialNumberToInt(S2); + return ((S1_i>>Bits)==(S2_i>>Bits)); +} + +[[nodiscard]] uint64_t SerialNumberToOUI(const std::string & S) { + uint64_t Result = 0 ; + int Digits=0; + + for(const auto &i:S) { + if(std::isxdigit(i)) { + if(i>='0' && i<='9') { + Result <<=4; + Result += i-'0'; + } else if(i>='A' && i<='F') { + Result <<=4; + Result += i-'A'+10; + } else if(i>='a' && i<='f') { + Result <<=4; + Result += i-'a'+10; + } + Digits++; + if(Digits==6) + break; + } + } + return Result; +} + +[[nodiscard]] uint64_t GetDefaultMacAsInt64() { + uint64_t Result=0; + auto IFaceList = Poco::Net::NetworkInterface::list(); + + for(const auto &iface:IFaceList) { + if(iface.isRunning() && !iface.isLoopback()) { + auto MAC = iface.macAddress(); + for (auto const &i : MAC) { + Result <<= 8; + Result += (uint8_t)i; + } + if (Result != 0) + break; + } + } + return Result; +} + +[[nodiscard]] uint64_t InitializeSystemId() { + std::random_device RDev; + std::srand(RDev()); + std::chrono::high_resolution_clock Clock; + auto Now = Clock.now().time_since_epoch().count(); + auto S = (GetDefaultMacAsInt64() + std::rand() + Now) ; + OpenWifi::AppServiceRegistry().Set("systemid",S); + return S; +} + +[[nodiscard]] uint64_t GetSystemId() { + uint64_t ID=0; + if(!AppServiceRegistry().Get("systemid",ID)) { + return InitializeSystemId(); + } + return ID; +} + +[[nodiscard]] bool ValidEMailAddress(const std::string &email) { + // define a regular expression + static const std::regex pattern + ("[_a-z0-9-]+(\\.[_a-z0-9-]+)*(\\+[a-z0-9-]+)?@[a-z0-9-]+(\\.[a-z0-9-]+)*"); + // try to match the string with the regular expression + return std::regex_match(email, pattern); +} + +[[nodiscard]] std::string LoadFile( const Poco::File & F) { + std::string Result; + try { + std::ostringstream OS; + std::ifstream IF(F.path()); + Poco::StreamCopier::copyStream(IF, OS); + Result = OS.str(); + } catch (...) { + + } + return Result; +} + +void ReplaceVariables( std::string & Content , const Types::StringPairVec & P) { + for(const auto &[Variable,Value]:P) { + Poco::replaceInPlace(Content,"${" + Variable + "}", Value); + } +} + +[[nodiscard]] MediaTypeEncoding FindMediaType(const Poco::File &F) { + const auto E = Poco::Path(F.path()).getExtension(); + if(E=="png") + return MediaTypeEncoding{ .Encoding = BINARY, + .ContentType = "image/png" }; + if(E=="gif") + return MediaTypeEncoding{ .Encoding = BINARY, + .ContentType = "image/gif" }; + if(E=="jpeg" || E=="jpg") + return MediaTypeEncoding{ .Encoding = BINARY, + .ContentType = "image/jpeg" }; + if(E=="svg" || E=="svgz") + return MediaTypeEncoding{ .Encoding = PLAIN, + .ContentType = "image/svg+xml" }; + if(E=="html") + return MediaTypeEncoding{ .Encoding = PLAIN, + .ContentType = "text/html" }; + if(E=="css") + return MediaTypeEncoding{ .Encoding = PLAIN, + .ContentType = "text/css" }; + if(E=="js") + return MediaTypeEncoding{ .Encoding = PLAIN, + .ContentType = "application/javascript" }; + return MediaTypeEncoding{ .Encoding = BINARY, + .ContentType = "application/octet-stream" }; +} + +[[nodiscard]] std::string BinaryFileToHexString(const Poco::File &F) { + static const char hex[] = "0123456789abcdef"; + std::string Result; + try { + std::ifstream IF(F.path()); + + int Count = 0; + while (IF.good()) { + if (Count) + Result += ", "; + if ((Count % 32) == 0) + Result += "\r\n"; + Count++; + unsigned char C = IF.get(); + Result += "0x"; + Result += (char) (hex[(C & 0xf0) >> 4]); + Result += (char) (hex[(C & 0x0f)]); + } + } catch(...) { + + } + return Result; +} + +[[nodiscard]] std::string SecondsToNiceText(uint64_t Seconds) { + std::string Result; + int Days = Seconds / (24*60*60); + Seconds -= Days * (24*60*60); + int Hours= Seconds / (60*60); + Seconds -= Hours * (60*60); + int Minutes = Seconds / 60; + Seconds -= Minutes * 60; + Result = std::to_string(Days) +" days, " + std::to_string(Hours) + ":" + std::to_string(Minutes) + ":" + std::to_string(Seconds); + return Result; +} + +[[nodiscard]] bool wgets(const std::string &URL, std::string &Response) { + try { + Poco::URI uri(URL); + Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort()); + + // prepare path + std::string path(uri.getPathAndQuery()); + if (path.empty()) { + path = "/"; + } + + // send request + Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_GET, path, Poco::Net::HTTPMessage::HTTP_1_1); + session.sendRequest(req); + + Poco::Net::HTTPResponse res; + std::istream &is = session.receiveResponse(res); + std::ostringstream os; + + Poco::StreamCopier::copyStream(is,os); + Response = os.str(); + + return true; + } catch (...) { + + } + return false; +} + +bool ExtractBase64CompressedData(const std::string &CompressedData, + std::string &UnCompressedData, uint64_t compress_sz ) { + std::istringstream ifs(CompressedData); + Poco::Base64Decoder b64in(ifs); + std::ostringstream ofs; + Poco::StreamCopier::copyStream(b64in, ofs); + + int factor = 20; + unsigned long MaxSize = compress_sz ? (unsigned long) (compress_sz + 5000) : (unsigned long) (ofs.str().size() * factor); + while(true) { + std::vector UncompressedBuffer(MaxSize); + unsigned long FinalSize = MaxSize; + auto status = uncompress((uint8_t *)&UncompressedBuffer[0], &FinalSize, + (uint8_t *)ofs.str().c_str(), ofs.str().size()); + if(status==Z_OK) { + UncompressedBuffer[FinalSize] = 0; + UnCompressedData = (char *)&UncompressedBuffer[0]; + return true; + } + if(status==Z_BUF_ERROR) { + if(factor<300) { + factor+=10; + MaxSize = ofs.str().size() * factor; + continue; + } else { + return false; + } + } + return false; + } + return false; +} + +} diff --git a/src/framework/utils.h b/src/framework/utils.h new file mode 100644 index 0000000..ce33fd1 --- /dev/null +++ b/src/framework/utils.h @@ -0,0 +1,129 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "Poco/Thread.h" +#include "Poco/StringTokenizer.h" +#include "Poco/String.h" +#include "Poco/SHA2Engine.h" +#include "Poco/Message.h" +#include "Poco/StreamCopier.h" +#include "Poco/File.h" +#include "Poco/Net/NetworkInterface.h" +#include "Poco/URI.h" +#include "Poco/Net/HTTPRequest.h" +#include "Poco/Net/HTTPClientSession.h" +#include "Poco/Net/HTTPSClientSession.h" +#include "Poco/Net/HTTPResponse.h" +#include "Poco/Base64Decoder.h" +#include "Poco/Base64Encoder.h" +#include "Poco/zlib.h" + +#include "framework/ow_constants.h" +#include "framework/OpenWifiTypes.h" + +namespace OpenWifi::Utils { + + inline uint64_t Now() { return std::time(nullptr); }; + + bool NormalizeMac(std::string & Mac); + + inline void SetThreadName(const char *name) { + #ifdef __linux__ + Poco::Thread::current()->setName(name); + pthread_setname_np(pthread_self(), name); + #endif + #ifdef __APPLE__ + Poco::Thread::current()->setName(name); + pthread_setname_np(name); + #endif + } + + inline void SetThreadName(Poco::Thread &thr, const char *name) { + #ifdef __linux__ + thr.setName(name); + pthread_setname_np(thr.tid(), name); + #endif + #ifdef __APPLE__ + thr.setName(name); + #endif + } + + enum MediaTypeEncodings { + PLAIN, + BINARY, + BASE64 + }; + + struct MediaTypeEncoding { + MediaTypeEncodings Encoding=PLAIN; + std::string ContentType; + }; + + [[nodiscard]] bool ValidSerialNumber(const std::string &Serial); + [[nodiscard]] bool ValidUUID(const std::string &UUID); + + template std::string ComputeHash(Args&&... args) { + Poco::SHA2Engine E; + auto as_string = [](auto p) { + if constexpr(std::is_arithmetic_v) { + return std::to_string(p); + } else { + return p; + } + }; + (E.update(as_string(args)),...); + return Poco::SHA2Engine::digestToHex(E.digest()); + } + + [[nodiscard]] std::vector Split(const std::string &List, char Delimiter=',' ); + [[nodiscard]] std::string FormatIPv6(const std::string & I ); + void padTo(std::string& str, size_t num, char paddingChar = '\0'); + [[nodiscard]] std::string SerialToMAC(const std::string &Serial); + uint64_t MACToInt(const std::string &MAC); + [[nodiscard]] std::string ToHex(const std::vector & B); + + using byte = std::uint8_t; + + [[nodiscard]] std::string base64encode(const byte *input, uint32_t size); + [[nodiscard]] std::vector base64decode(const std::string& input);; + bool ParseTime(const std::string &Time, int & Hours, int & Minutes, int & Seconds); + bool ParseDate(const std::string &Time, int & Year, int & Month, int & Day); + bool CompareTime( int H1, int H2, int M1, int M2, int S1, int S2); + [[nodiscard]] std::string LogLevelToString(int Level); + [[nodiscard]] uint64_t SerialNumberToInt(const std::string & S); + [[nodiscard]] std::string IntToSerialNumber(uint64_t S); + [[nodiscard]] bool SerialNumberMatch(const std::string &S1, const std::string &S2, int Bits=2); + [[nodiscard]] uint64_t SerialNumberToOUI(const std::string & S); + [[nodiscard]] uint64_t GetDefaultMacAsInt64(); + [[nodiscard]] uint64_t InitializeSystemId(); + [[nodiscard]] uint64_t GetSystemId(); + [[nodiscard]] bool ValidEMailAddress(const std::string &email); + [[nodiscard]] std::string LoadFile( const Poco::File & F); + void ReplaceVariables( std::string & Content , const Types::StringPairVec & P); + [[nodiscard]] MediaTypeEncoding FindMediaType(const Poco::File &F); + [[nodiscard]] std::string BinaryFileToHexString(const Poco::File &F); + [[nodiscard]] std::string SecondsToNiceText(uint64_t Seconds); + [[nodiscard]] bool wgets(const std::string &URL, std::string &Response); + + template< typename T > + std::string int_to_hex( T i ) + { + std::stringstream stream; + stream << std::setfill ('0') << std::setw(12) + << std::hex << i; + return stream.str(); + } + bool ExtractBase64CompressedData(const std::string &CompressedData, + std::string &UnCompressedData, uint64_t compress_sz ); +} diff --git a/src/ow_version.h b/src/ow_version.h index 598de10..cf6d426 100644 --- a/src/ow_version.h +++ b/src/ow_version.h @@ -7,7 +7,7 @@ #include namespace OW_VERSION { - inline static const std::string VERSION{"2.6.0"}; - inline static const std::string BUILD{"128"}; - inline static const std::string HASH{"3b0f2a0"}; + inline static const std::string VERSION{"2.8.0"}; + inline static const std::string BUILD{"1"}; + inline static const std::string HASH{"v2.7.0-RC4-19-g9daee84"}; } diff --git a/src/sdks/SDK_fms.cpp b/src/sdks/SDK_fms.cpp index f90de96..f97c51a 100644 --- a/src/sdks/SDK_fms.cpp +++ b/src/sdks/SDK_fms.cpp @@ -3,9 +3,12 @@ // #include "SDK_fms.h" -#include "framework/MicroService.h" + #include "RESTObjects/RESTAPI_FMSObjects.h" +#include "framework/MicroServiceNames.h" +#include "framework/OpenAPIRequests.h" + namespace OpenWifi::SDK::FMS { namespace Firmware { diff --git a/src/sdks/SDK_fms.h b/src/sdks/SDK_fms.h index b1dafdb..c299b51 100644 --- a/src/sdks/SDK_fms.h +++ b/src/sdks/SDK_fms.h @@ -4,7 +4,6 @@ #pragma once -#include #include "RESTObjects/RESTAPI_FMSObjects.h" namespace OpenWifi::SDK::FMS { diff --git a/src/sdks/SDK_gw.cpp b/src/sdks/SDK_gw.cpp index 9cd1403..6fc3e9d 100644 --- a/src/sdks/SDK_gw.cpp +++ b/src/sdks/SDK_gw.cpp @@ -2,9 +2,11 @@ // Created by stephane bourque on 2022-01-11. // -#include "framework/MicroService.h" #include "SDK_gw.h" +#include "framework/MicroServiceNames.h" +#include "framework/OpenAPIRequests.h" + namespace OpenWifi::SDK::GW { namespace Device { diff --git a/src/sdks/SDK_gw.h b/src/sdks/SDK_gw.h index 2787c89..8102344 100644 --- a/src/sdks/SDK_gw.h +++ b/src/sdks/SDK_gw.h @@ -4,6 +4,7 @@ #pragma once +#include "framework/RESTAPI_Handler.h" #include "RESTObjects/RESTAPI_GWobjects.h" namespace OpenWifi::SDK::GW { diff --git a/src/sdks/SDK_prov.cpp b/src/sdks/SDK_prov.cpp index 773eb0d..bfa8745 100644 --- a/src/sdks/SDK_prov.cpp +++ b/src/sdks/SDK_prov.cpp @@ -3,6 +3,8 @@ // #include "SDK_prov.h" +#include "framework/MicroServiceNames.h" +#include "framework/OpenAPIRequests.h" namespace OpenWifi::SDK::Prov { diff --git a/src/sdks/SDK_prov.h b/src/sdks/SDK_prov.h index daf8909..27802cd 100644 --- a/src/sdks/SDK_prov.h +++ b/src/sdks/SDK_prov.h @@ -4,7 +4,7 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "RESTObjects/RESTAPI_ProvObjects.h" namespace OpenWifi::SDK::Prov { diff --git a/src/sdks/SDK_sec.cpp b/src/sdks/SDK_sec.cpp index 563edb1..4a86e29 100644 --- a/src/sdks/SDK_sec.cpp +++ b/src/sdks/SDK_sec.cpp @@ -3,6 +3,8 @@ // #include "SDK_sec.h" +#include "framework/MicroServiceNames.h" +#include "framework/OpenAPIRequests.h" namespace OpenWifi::SDK::Sec { diff --git a/src/sdks/SDK_sec.h b/src/sdks/SDK_sec.h index 08694be..5a828c3 100644 --- a/src/sdks/SDK_sec.h +++ b/src/sdks/SDK_sec.h @@ -4,7 +4,7 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" namespace OpenWifi::SDK::Sec { diff --git a/src/storage/storage_configurations.cpp b/src/storage/storage_configurations.cpp index 92b139f..364b619 100644 --- a/src/storage/storage_configurations.cpp +++ b/src/storage/storage_configurations.cpp @@ -9,7 +9,7 @@ #include "storage_configurations.h" #include "framework/OpenWifiTypes.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "StorageService.h" diff --git a/src/storage/storage_contact.cpp b/src/storage/storage_contact.cpp index 4ab4281..a2d2775 100644 --- a/src/storage/storage_contact.cpp +++ b/src/storage/storage_contact.cpp @@ -9,7 +9,7 @@ #include "storage_contact.h" #include "framework/OpenWifiTypes.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" diff --git a/src/storage/storage_entity.cpp b/src/storage/storage_entity.cpp index 14cd57a..481cbac 100644 --- a/src/storage/storage_entity.cpp +++ b/src/storage/storage_entity.cpp @@ -10,7 +10,9 @@ #include "framework/OpenWifiTypes.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "StorageService.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" +#include "framework/CIDR.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { @@ -147,7 +149,7 @@ namespace OpenWifi { const auto & Child = i.extract(); ProvObjects::Venue V; V.info.name = Child->get("name").toString(); - V.info.id = MicroService::CreateUUID(); + V.info.id = MicroServiceCreateUUID(); V.parent = Parent; V.info.created = V.info.modified = OpenWifi::Now(); // StorageService()->VenueDB().CreateShortCut(V); @@ -176,7 +178,7 @@ namespace OpenWifi { ProvObjects::Entity E; E.info.name = Child->get("name").toString(); - E.info.id = MicroService::CreateUUID(); + E.info.id = MicroServiceCreateUUID(); E.parent = Parent; E.info.created = E.info.modified = OpenWifi::Now(); // StorageService()->EntityDB().CreateShortCut(E); @@ -188,7 +190,7 @@ namespace OpenWifi { const auto & Child = i.extract(); ProvObjects::Venue V; V.info.name = Child->get("name").toString(); - V.info.id = MicroService::CreateUUID(); + V.info.id = MicroServiceCreateUUID(); V.entity = Parent; V.info.created = V.info.modified = OpenWifi::Now(); // StorageService()->VenueDB().CreateShortCut(V); diff --git a/src/storage/storage_inventory.cpp b/src/storage/storage_inventory.cpp index f0de7c2..50c4fe9 100644 --- a/src/storage/storage_inventory.cpp +++ b/src/storage/storage_inventory.cpp @@ -9,13 +9,13 @@ #include "storage_inventory.h" #include "framework/OpenWifiTypes.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" +#include "framework/MicroServiceFuncs.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "StorageService.h" #include "sdks/SDK_gw.h" -#include "AutoDiscovery.h" #include "SerialNumberCache.h" -#include "Daemon.h" +#include "nlohmann/json.hpp" namespace OpenWifi { @@ -99,7 +99,7 @@ namespace OpenWifi { if(Tokens.count()==3) { IP = Tokens[1]; } - NewDevice.info.id = MicroService::CreateUUID(); + NewDevice.info.id = MicroServiceCreateUUID(); NewDevice.info.name = SerialNumber; NewDevice.info.created = NewDevice.info.modified = Now; NewDevice.info.notes.push_back(SecurityObjects::NoteInfo{.created=Now,.createdBy="*system",.note="Auto discovered"}); diff --git a/src/storage/storage_location.cpp b/src/storage/storage_location.cpp index 9df3408..7014d51 100644 --- a/src/storage/storage_location.cpp +++ b/src/storage/storage_location.cpp @@ -9,7 +9,7 @@ #include "storage_location.h" #include "framework/OpenWifiTypes.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" diff --git a/src/storage/storage_management_roles.cpp b/src/storage/storage_management_roles.cpp index 9b02fdc..f9e3feb 100644 --- a/src/storage/storage_management_roles.cpp +++ b/src/storage/storage_management_roles.cpp @@ -9,7 +9,7 @@ #include "storage_management_roles.h" #include "framework/OpenWifiTypes.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" namespace OpenWifi { diff --git a/src/storage/storage_maps.cpp b/src/storage/storage_maps.cpp index fe224a5..1f42a6e 100644 --- a/src/storage/storage_maps.cpp +++ b/src/storage/storage_maps.cpp @@ -3,6 +3,7 @@ // #include "storage_maps.h" +#include "framework/RESTAPI_utils.h" namespace OpenWifi { diff --git a/src/storage/storage_op_contacts.cpp b/src/storage/storage_op_contacts.cpp index 97fe571..32dee3c 100644 --- a/src/storage/storage_op_contacts.cpp +++ b/src/storage/storage_op_contacts.cpp @@ -4,7 +4,7 @@ #include "storage_op_contacts.h" #include "framework/OpenWifiTypes.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" diff --git a/src/storage/storage_op_locations.cpp b/src/storage/storage_op_locations.cpp index 55bfc7a..3bf00e6 100644 --- a/src/storage/storage_op_locations.cpp +++ b/src/storage/storage_op_locations.cpp @@ -4,7 +4,7 @@ #include "storage_op_locations.h" #include "framework/OpenWifiTypes.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" diff --git a/src/storage/storage_operataor.cpp b/src/storage/storage_operataor.cpp index 352c0ee..370c794 100644 --- a/src/storage/storage_operataor.cpp +++ b/src/storage/storage_operataor.cpp @@ -6,7 +6,8 @@ #include "framework/OpenWifiTypes.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "StorageService.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" +#include "framework/CIDR.h" namespace OpenWifi { diff --git a/src/storage/storage_policies.cpp b/src/storage/storage_policies.cpp index cda5de2..a90c7ce 100644 --- a/src/storage/storage_policies.cpp +++ b/src/storage/storage_policies.cpp @@ -7,7 +7,7 @@ // -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "storage_policies.h" #include "framework/OpenWifiTypes.h" diff --git a/src/storage/storage_service_class.cpp b/src/storage/storage_service_class.cpp index 7b48760..6368d78 100644 --- a/src/storage/storage_service_class.cpp +++ b/src/storage/storage_service_class.cpp @@ -3,6 +3,7 @@ // #include "storage_service_class.h" +#include "framework/RESTAPI_utils.h" // // License type: BSD 3-Clause License @@ -12,11 +13,10 @@ // Arilia Wireless Inc. // -#include "storage_entity.h" #include "framework/OpenWifiTypes.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "StorageService.h" -#include "framework/MicroService.h" +#include "fmt/format.h" namespace OpenWifi { static ORM::FieldVec ServiceClassDB_Fields{ diff --git a/src/storage/storage_signup.cpp b/src/storage/storage_signup.cpp index c10893a..ffbe2b7 100644 --- a/src/storage/storage_signup.cpp +++ b/src/storage/storage_signup.cpp @@ -7,9 +7,10 @@ #include "framework/OpenWifiTypes.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "StorageService.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "Signup.h" #include "sdks/SDK_sec.h" +#include "nlohmann/json.hpp" namespace OpenWifi { diff --git a/src/storage/storage_sub_devices.cpp b/src/storage/storage_sub_devices.cpp index 4aa0f1f..18e560b 100644 --- a/src/storage/storage_sub_devices.cpp +++ b/src/storage/storage_sub_devices.cpp @@ -6,7 +6,7 @@ #include "framework/OpenWifiTypes.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "StorageService.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" namespace OpenWifi { diff --git a/src/storage/storage_tags.cpp b/src/storage/storage_tags.cpp index ee86a6a..dbbd3e7 100644 --- a/src/storage/storage_tags.cpp +++ b/src/storage/storage_tags.cpp @@ -4,7 +4,7 @@ #include "storage_tags.h" #include "framework/OpenWifiTypes.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "StorageService.h" #include diff --git a/src/storage/storage_variables.cpp b/src/storage/storage_variables.cpp index fc10f49..1f2742f 100644 --- a/src/storage/storage_variables.cpp +++ b/src/storage/storage_variables.cpp @@ -6,7 +6,7 @@ #include "framework/OpenWifiTypes.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" namespace OpenWifi { diff --git a/src/storage/storage_venue.cpp b/src/storage/storage_venue.cpp index 7b13e92..6ef9224 100644 --- a/src/storage/storage_venue.cpp +++ b/src/storage/storage_venue.cpp @@ -9,10 +9,11 @@ #include #include "storage_venue.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" #include "framework/OpenWifiTypes.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "StorageService.h" +#include "framework/CIDR.h" namespace OpenWifi {