diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 199d03e..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,21 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml -# Editor-based HTTP Client requests -/httpRequests/ -/certs/ -/logs/ -*.csr -*.db -/docker-compose/certs/ -/docker-compose/*-data/data/ -/docker-compose/*-data/uploads/ -/docker-compose/.env -/docker-compose/.env_* -/cmake-build/ -*.pem -result.json -token.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a697cb..deca207 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.13) -project(owsec VERSION 2.7.0) +project(owsec VERSION 2.8.0) set(CMAKE_CXX_STANDARD 17) @@ -75,19 +75,63 @@ add_executable( owsec src/framework/CountryCodes.h src/framework/KafkaTopics.h src/framework/MicroService.h + src/framework/OpenWifiTypes.h src/framework/orm.h src/framework/StorageClass.h - src/framework/ow_constants.h src/framework/MicroServiceErrorHandler.h - src/framework/WebSocketClientNotifications.h + src/framework/UI_WebSocketClientServer.cpp + src/framework/UI_WebSocketClientServer.h + src/framework/UI_WebSocketClientNotifications.cpp + src/framework/UI_WebSocketClientNotifications.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/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/RESTAPI_SystemConfiguration.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/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp + 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/seclibs/qrcode/qrcodegen.hpp src/seclibs/qrcode/qrcodegen.cpp src/seclibs/cpptotp/bytes.cpp src/seclibs/cpptotp/bytes.h src/seclibs/cpptotp/otp.cpp src/seclibs/cpptotp/otp.h src/seclibs/cpptotp/sha1.cpp src/seclibs/cpptotp/sha1.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/RESTAPI/RESTAPI_oauth2_handler.h src/RESTAPI/RESTAPI_oauth2_handler.cpp src/RESTAPI/RESTAPI_users_handler.cpp src/RESTAPI/RESTAPI_users_handler.h src/RESTAPI/RESTAPI_user_handler.cpp src/RESTAPI/RESTAPI_user_handler.h @@ -119,14 +163,19 @@ add_executable( owsec src/SMS_provider_twilio.cpp src/SMS_provider_twilio.h src/ActionLinkManager.cpp src/ActionLinkManager.h src/ACLProcessor.h - src/framework/OpenWifiTypes.h src/storage/orm_users.cpp src/storage/orm_users.h src/storage/orm_tokens.cpp src/storage/orm_tokens.h src/storage/orm_preferences.cpp src/storage/orm_preferences.h src/storage/orm_actionLinks.cpp src/storage/orm_actionLinks.h src/storage/orm_avatar.cpp src/storage/orm_avatar.h src/SpecialUserHelpers.h - src/RESTAPI/RESTAPI_db_helpers.h src/storage/orm_logins.cpp src/storage/orm_logins.h src/RESTAPI/RESTAPI_totp_handler.cpp src/RESTAPI/RESTAPI_totp_handler.h src/TotpCache.h src/RESTAPI/RESTAPI_subtotp_handler.cpp src/RESTAPI/RESTAPI_subtotp_handler.h src/RESTAPI/RESTAPI_signup_handler.cpp src/RESTAPI/RESTAPI_signup_handler.h src/MessagingTemplates.cpp src/MessagingTemplates.h) + src/RESTAPI/RESTAPI_db_helpers.h src/storage/orm_logins.cpp src/storage/orm_logins.h + src/RESTAPI/RESTAPI_totp_handler.cpp + src/RESTAPI/RESTAPI_totp_handler.h + src/TotpCache.h + src/RESTAPI/RESTAPI_subtotp_handler.cpp src/RESTAPI/RESTAPI_subtotp_handler.h + src/RESTAPI/RESTAPI_signup_handler.cpp src/RESTAPI/RESTAPI_signup_handler.h + src/MessagingTemplates.cpp src/MessagingTemplates.h) if(NOT SMALL_BUILD) target_link_libraries(owsec PUBLIC diff --git a/Dockerfile b/Dockerfile index 68daf4c..e44f1b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -77,7 +77,7 @@ RUN mkdir -p "$OWSEC_ROOT" "$OWSEC_CONFIG" && \ RUN apt-get update && apt-get install --no-install-recommends -y \ librdkafka++1 gosu gettext ca-certificates bash jq curl wget \ - libmariadb-dev-compat libpq5 unixodbc postgresql-client libfmt7 + libmariadb-dev-compat libpq5 postgresql-client libfmt7 COPY readiness_check /readiness_check COPY test_scripts/curl/cli /cli diff --git a/build b/build index b393560..f11c82a 100644 --- a/build +++ b/build @@ -1 +1 @@ -23 \ No newline at end of file +9 \ No newline at end of file diff --git a/src/ActionLinkManager.cpp b/src/ActionLinkManager.cpp index 85b3feb..e1225bc 100644 --- a/src/ActionLinkManager.cpp +++ b/src/ActionLinkManager.cpp @@ -6,6 +6,8 @@ #include "StorageService.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "MessagingTemplates.h" +#include "framework/utils.h" +#include "fmt/format.h" namespace OpenWifi { diff --git a/src/ActionLinkManager.h b/src/ActionLinkManager.h index e01e048..479a96f 100644 --- a/src/ActionLinkManager.h +++ b/src/ActionLinkManager.h @@ -2,10 +2,9 @@ // Created by stephane bourque on 2021-11-08. // -#ifndef OWSEC_ACTIONLINKMANAGER_H -#define OWSEC_ACTIONLINKMANAGER_H +#pragma once -#include "framework/MicroService.h" +#include "framework/SubSystemServer.h" namespace OpenWifi { @@ -33,4 +32,3 @@ namespace OpenWifi { inline ActionLinkManager * ActionLinkManager() { return ActionLinkManager::instance(); } } -#endif //OWSEC_ACTIONLINKMANAGER_H diff --git a/src/AuthService.cpp b/src/AuthService.cpp index 14ae76b..5abb24f 100644 --- a/src/AuthService.cpp +++ b/src/AuthService.cpp @@ -8,7 +8,7 @@ #include -#include "framework/MicroService.h" +#include "framework/KafkaManager.h" #include "framework/KafkaTopics.h" #include "Poco/Net/OAuth20Credentials.h" @@ -18,7 +18,7 @@ #include "StorageService.h" #include "AuthService.h" - +#include "framework/MicroServiceFuncs.h" #include "SMTPMailerService.h" #include "MFAServer.h" @@ -49,17 +49,17 @@ namespace OpenWifi { int AuthService::Start() { poco_information(Logger(),"Starting..."); - TokenAging_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60); - RefreshTokenLifeSpan_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.refresh_token.lifespan", 90 * 24 * 60 * 600); - HowManyOldPassword_ = MicroService::instance().ConfigGetInt("authentication.oldpasswords", 5); + TokenAging_ = (uint64_t) MicroServiceConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60); + RefreshTokenLifeSpan_ = (uint64_t) MicroServiceConfigGetInt("authentication.refresh_token.lifespan", 90 * 24 * 60 * 600); + HowManyOldPassword_ = MicroServiceConfigGetInt("authentication.oldpasswords", 5); - AccessPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.access", "/wwwassets/access_policy.html"); - PasswordPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.password", "/wwwassets/password_policy.html"); - PasswordValidation_ = PasswordValidationStr_ = MicroService::instance().ConfigGetString("authentication.validation.expression",DefaultPassword_8_u_l_n_1); + AccessPolicy_ = MicroServiceConfigGetString("openwifi.document.policy.access", "/wwwassets/access_policy.html"); + PasswordPolicy_ = MicroServiceConfigGetString("openwifi.document.policy.password", "/wwwassets/password_policy.html"); + PasswordValidation_ = PasswordValidationStr_ = MicroServiceConfigGetString("authentication.validation.expression",DefaultPassword_8_u_l_n_1); - SubPasswordValidation_ = SubPasswordValidationStr_ = MicroService::instance().ConfigGetString("subscriber.validation.expression",DefaultPassword_8_u_l_n_1); - SubAccessPolicy_ = MicroService::instance().ConfigGetString("subscriber.policy.access", "/wwwassets/access_policy.html"); - SubPasswordPolicy_ = MicroService::instance().ConfigGetString("subscriber.policy.password", "/wwwassets/password_policy.html"); + SubPasswordValidation_ = SubPasswordValidationStr_ = MicroServiceConfigGetString("subscriber.validation.expression",DefaultPassword_8_u_l_n_1); + SubAccessPolicy_ = MicroServiceConfigGetString("subscriber.policy.access", "/wwwassets/access_policy.html"); + SubPasswordPolicy_ = MicroServiceConfigGetString("subscriber.policy.password", "/wwwassets/password_policy.html"); return 0; } @@ -260,11 +260,11 @@ namespace OpenWifi { if(KafkaManager()->Enabled()) { Poco::JSON::Object Obj; Obj.set("event", "remove-token"); - Obj.set("id", MicroService::instance().ID()); + Obj.set("id", MicroServiceID()); Obj.set("token", token); std::stringstream ResultText; Poco::JSON::Stringifier::stringify(Obj, ResultText); - KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroService::instance().PrivateEndPoint(), + KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroServicePrivateEndPoint(), ResultText.str(), false); } @@ -318,7 +318,7 @@ namespace OpenWifi { T.payload().set("identity", Identity); T.setIssuedAt(Poco::Timestamp()); T.setExpiration(Poco::Timestamp() + (long long)TokenAging_); - std::string JWT = MicroService::instance().Sign(T,Poco::JWT::Signer::ALGO_RS256); + std::string JWT = MicroServiceSign(T,Poco::JWT::Signer::ALGO_RS256); return JWT; } @@ -606,7 +606,7 @@ namespace OpenWifi { Attrs[RECIPIENT_EMAIL] = UInfo.email; Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "Password reset link"; - Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ; + Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=password_reset&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::FORGOT_PASSWORD), Attrs); } @@ -617,7 +617,7 @@ namespace OpenWifi { Attrs[RECIPIENT_EMAIL] = UInfo.email; Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "e-mail Address Verification"; - Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ; + Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=email_verification&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::EMAIL_VERIFICATION), Attrs); UInfo.waitingForEmailCheck = true; @@ -629,7 +629,7 @@ namespace OpenWifi { Attrs[RECIPIENT_EMAIL] = UInfo.email; Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "e-mail Invitation"; - Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_invitation&id=" + LinkId ; + Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() + "/actionLink?action=email_invitation&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=email_invitation&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::EMAIL_INVITATION), Attrs); UInfo.waitingForEmailCheck = true; @@ -655,7 +655,7 @@ namespace OpenWifi { Attrs[RECIPIENT_EMAIL] = UInfo.email; Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "Password reset link"; - Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=sub_password_reset&id=" + LinkId ; + Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() + "/actionLink?action=sub_password_reset&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=sub_password_reset&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SUB_FORGOT_PASSWORD, OperatorName), Attrs); } @@ -666,7 +666,7 @@ namespace OpenWifi { Attrs[RECIPIENT_EMAIL] = UInfo.email; Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "e-mail Address Verification"; - Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=sub_email_verification&id=" + LinkId ; + Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() + "/actionLink?action=sub_email_verification&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=sub_email_verification&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SUB_EMAIL_VERIFICATION, OperatorName), Attrs); UInfo.waitingForEmailCheck = true; @@ -678,7 +678,7 @@ namespace OpenWifi { Attrs[RECIPIENT_EMAIL] = UInfo.email; Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "Signup e-mail Address Verification"; - Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=signup_verification&id=" + LinkId ; + Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() + "/actionLink?action=signup_verification&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=signup_verification&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SIGNUP_VERIFICATION, OperatorName), Attrs); UInfo.waitingForEmailCheck = true; @@ -698,7 +698,7 @@ namespace OpenWifi { A.action = OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL; A.userId = UInfo.id; - A.id = MicroService::CreateUUID(); + A.id = MicroServiceCreateUUID(); A.created = OpenWifi::Now(); A.expires = A.created + 24*60*60; A.userAction = true; @@ -713,7 +713,7 @@ namespace OpenWifi { A.action = OpenWifi::SecurityObjects::LinkActions::SUB_VERIFY_EMAIL; A.userId = UInfo.id; - A.id = MicroService::CreateUUID(); + A.id = MicroServiceCreateUUID(); A.created = OpenWifi::Now(); A.expires = A.created + 24*60*60; A.userAction = false; diff --git a/src/AuthService.h b/src/AuthService.h index 0c3323c..813a850 100644 --- a/src/AuthService.h +++ b/src/AuthService.h @@ -6,13 +6,11 @@ // Arilia Wireless Inc. // -#ifndef UCENTRAL_UAUTHSERVICE_H -#define UCENTRAL_UAUTHSERVICE_H +#pragma once #include -#include "framework/MicroService.h" - +#include "framework/SubSystemServer.h" #include "Poco/JSON/Object.h" #include "Poco/Net/HTTPServerRequest.h" #include "Poco/Net/HTTPServerResponse.h" @@ -22,6 +20,9 @@ #include "Poco/HMACEngine.h" #include "Poco/ExpireLRUCache.h" +#include "framework/MicroServiceFuncs.h" +#include "framework/ow_constants.h" + #include "RESTObjects/RESTAPI_SecurityObjects.h" #include "MessagingTemplates.h" @@ -98,11 +99,11 @@ namespace OpenWifi{ void RevokeSubToken(std::string & Token); [[nodiscard]] static inline const std::string GetLogoAssetURI() { - return MicroService::instance().PublicEndPoint() + "/wwwassets/the_logo.png"; + return MicroServicePublicEndPoint() + "/wwwassets/the_logo.png"; } [[nodiscard]] static inline const std::string GetLogoAssetFileName() { - return MicroService::instance().WWWAssetsDir() + "/the_logo.png"; + return MicroServiceWWWAssetsDir() + "/the_logo.png"; } inline const std::string & GetPasswordPolicy() const { return PasswordPolicy_; } @@ -165,4 +166,3 @@ namespace OpenWifi{ } // end of namespace -#endif //UCENTRAL_UAUTHSERVICE_H diff --git a/src/Daemon.cpp b/src/Daemon.cpp index 122a94e..81b06f4 100644 --- a/src/Daemon.cpp +++ b/src/Daemon.cpp @@ -26,6 +26,8 @@ #include "SMSSender.h" #include "ActionLinkManager.h" #include "TotpCache.h" +#include "framework/RESTAPI_RateLimiter.h" +#include "framework/UI_WebSocketClientServer.h" namespace OpenWifi { class Daemon *Daemon::instance_ = nullptr; @@ -44,7 +46,8 @@ namespace OpenWifi { SMTPMailerService(), RESTAPI_RateLimiter(), TotpCache(), - AuthService() + AuthService(), + UI_WebSocketClientServer() }); } return instance_; @@ -53,6 +56,10 @@ namespace OpenWifi { void Daemon::PostInitialization([[maybe_unused]] Poco::Util::Application &self) { AssetDir_ = MicroService::instance().ConfigPath("openwifi.restapi.wwwassets"); } + + 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 4c3bd9a..ae6b788 100644 --- a/src/Daemon.h +++ b/src/Daemon.h @@ -2,14 +2,14 @@ // Created by stephane bourque on 2021-06-10. // -#ifndef UCENTRALSEC_DAEMON_H -#define UCENTRALSEC_DAEMON_H +#pragma once #include #include #include #include +#include "framework/MicroServiceNames.h" #include "framework/MicroService.h" #include "Poco/Util/Application.h" @@ -50,9 +50,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); } -#endif //UCENTRALSEC_DAEMON_H diff --git a/src/MFAServer.cpp b/src/MFAServer.cpp index 1b8ac93..72ba631 100644 --- a/src/MFAServer.cpp +++ b/src/MFAServer.cpp @@ -2,14 +2,15 @@ // Created by stephane bourque on 2021-10-11. // -#include "framework/MicroService.h" - #include "MFAServer.h" #include "SMSSender.h" #include "SMTPMailerService.h" #include "AuthService.h" #include "TotpCache.h" +#include "framework/MicroServiceFuncs.h" +#include "framework/utils.h" + namespace OpenWifi { int MFAServer::Start() { @@ -28,8 +29,8 @@ namespace OpenWifi { return false; std::string Challenge = MakeChallenge(); - std::string uuid = MicroService::CreateUUID(); - uint64_t Created = OpenWifi::Now(); + std::string uuid = MicroServiceCreateUUID(); + uint64_t Created = Utils::Now(); ChallengeStart.set("uuid",uuid); ChallengeStart.set("created", Created); @@ -103,7 +104,7 @@ namespace OpenWifi { void MFAServer::CleanCache() { // it is assumed that you have locked Cache_ at this point. - uint64_t Now = OpenWifi::Now(); + uint64_t Now = Utils::Now(); for(auto i=begin(Cache_);i!=end(Cache_);) { if((Now-i->second.Created)>300) { i = Cache_.erase(i); diff --git a/src/MFAServer.h b/src/MFAServer.h index eb853b0..1982b5f 100644 --- a/src/MFAServer.h +++ b/src/MFAServer.h @@ -4,9 +4,12 @@ #pragma once -#include "framework/MicroService.h" #include "Poco/JSON/Object.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" +#include "framework/SubSystemServer.h" +#include "framework/MicroServiceFuncs.h" + +#include "fmt/format.h" namespace OpenWifi { @@ -46,7 +49,7 @@ namespace OpenWifi { static bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge); static inline std::string MakeChallenge() { - return fmt::format("{0:06}" , MicroService::instance().Random(1,999999) ); + return fmt::format("{0:06}" , MicroServiceRandom(1,999999) ); } private: diff --git a/src/RESTAPI/RESTAPI_action_links.cpp b/src/RESTAPI/RESTAPI_action_links.cpp index d97169c..12d562f 100644 --- a/src/RESTAPI/RESTAPI_action_links.cpp +++ b/src/RESTAPI/RESTAPI_action_links.cpp @@ -7,7 +7,9 @@ #include "RESTAPI_action_links.h" #include "StorageService.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_PartHandler.h" +#include "framework/OpenAPIRequests.h" + #include "Daemon.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_action_links.h b/src/RESTAPI/RESTAPI_action_links.h index f05495c..e3a5f8a 100644 --- a/src/RESTAPI/RESTAPI_action_links.h +++ b/src/RESTAPI/RESTAPI_action_links.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_action_links : public RESTAPIHandler { public: - RESTAPI_action_links(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_action_links(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_asset_server.h b/src/RESTAPI/RESTAPI_asset_server.h index d6a0638..a1e0a87 100644 --- a/src/RESTAPI/RESTAPI_asset_server.h +++ b/src/RESTAPI/RESTAPI_asset_server.h @@ -4,12 +4,12 @@ #pragma once -#include "../framework/MicroService.h" +#include "framework/RESTAPI_Handler.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 {Poco::Net::HTTPRequest::HTTP_POST, diff --git a/src/RESTAPI/RESTAPI_avatar_handler.cpp b/src/RESTAPI/RESTAPI_avatar_handler.cpp index 5ad2535..40e59fc 100644 --- a/src/RESTAPI/RESTAPI_avatar_handler.cpp +++ b/src/RESTAPI/RESTAPI_avatar_handler.cpp @@ -8,7 +8,8 @@ #include "RESTAPI_avatar_handler.h" #include "StorageService.h" #include "Poco/Net/HTMLForm.h" -#include "framework/MicroService.h" +#include "Poco/CountingStream.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { @@ -34,7 +35,7 @@ namespace OpenWifi { Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler); Poco::JSON::Object Answer; - if (!partHandler.Name().empty() && partHandler.Length()< MicroService::instance().ConfigGetInt("openwifi.avatar.maxsize",2000000)) { + if (!partHandler.Name().empty() && partHandler.Length()< MicroServiceConfigGetInt("openwifi.avatar.maxsize",2000000)) { Answer.set(RESTAPI::Protocol::AVATARID, Id); Answer.set(RESTAPI::Protocol::ERRORCODE, 0); Logger_.information(fmt::format("Uploaded avatar: {} Type: {}", partHandler.Name(), partHandler.ContentType())); diff --git a/src/RESTAPI/RESTAPI_avatar_handler.h b/src/RESTAPI/RESTAPI_avatar_handler.h index 25b0d1f..51add4e 100644 --- a/src/RESTAPI/RESTAPI_avatar_handler.h +++ b/src/RESTAPI/RESTAPI_avatar_handler.h @@ -3,7 +3,8 @@ // #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" +#include "Poco/Net/PartHandler.h" namespace OpenWifi { @@ -32,7 +33,7 @@ namespace OpenWifi { class RESTAPI_avatar_handler : public RESTAPIHandler { public: - RESTAPI_avatar_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_avatar_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, @@ -48,6 +49,5 @@ namespace OpenWifi { void DoPost() final; void DoDelete() final; void DoPut() final {}; - }; } diff --git a/src/RESTAPI/RESTAPI_email_handler.cpp b/src/RESTAPI/RESTAPI_email_handler.cpp index b92b9f9..48c02e3 100644 --- a/src/RESTAPI/RESTAPI_email_handler.cpp +++ b/src/RESTAPI/RESTAPI_email_handler.cpp @@ -3,14 +3,10 @@ // #include "RESTAPI_email_handler.h" - - -#include "Poco/Exception.h" #include "Poco/JSON/Parser.h" #include "SMTPMailerService.h" #include "framework/ow_constants.h" -#include "framework/MicroService.h" namespace OpenWifi { void RESTAPI_email_handler::DoPost() { diff --git a/src/RESTAPI/RESTAPI_email_handler.h b/src/RESTAPI/RESTAPI_email_handler.h index b559bae..1662d91 100644 --- a/src/RESTAPI/RESTAPI_email_handler.h +++ b/src/RESTAPI/RESTAPI_email_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_email_handler : public RESTAPIHandler { public: - RESTAPI_email_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_email_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, Poco::Net::HTTPRequest::HTTP_OPTIONS}, diff --git a/src/RESTAPI/RESTAPI_oauth2_handler.cpp b/src/RESTAPI/RESTAPI_oauth2_handler.cpp index db956e8..886df1c 100644 --- a/src/RESTAPI/RESTAPI_oauth2_handler.cpp +++ b/src/RESTAPI/RESTAPI_oauth2_handler.cpp @@ -12,10 +12,11 @@ #include "RESTAPI_oauth2_handler.h" #include "MFAServer.h" #include "framework/ow_constants.h" -#include "framework/MicroService.h" #include "StorageService.h" #include "RESTAPI_db_helpers.h" +#include "framework/MicroServiceFuncs.h" + namespace OpenWifi { void RESTAPI_oauth2_handler::DoGet() { @@ -99,7 +100,7 @@ namespace OpenWifi { SecurityObjects::ActionLink NewLink; NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD; - NewLink.id = MicroService::CreateUUID(); + NewLink.id = MicroServiceCreateUUID(); NewLink.userId = UInfo1.id; NewLink.created = OpenWifi::Now(); NewLink.expires = NewLink.created + (24*60*60); diff --git a/src/RESTAPI/RESTAPI_oauth2_handler.h b/src/RESTAPI/RESTAPI_oauth2_handler.h index 77209a4..f7277d2 100644 --- a/src/RESTAPI/RESTAPI_oauth2_handler.h +++ b/src/RESTAPI/RESTAPI_oauth2_handler.h @@ -7,12 +7,12 @@ // #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_oauth2_handler : public RESTAPIHandler { public: - RESTAPI_oauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_oauth2_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, Poco::Net::HTTPRequest::HTTP_DELETE, diff --git a/src/RESTAPI/RESTAPI_preferences.h b/src/RESTAPI/RESTAPI_preferences.h index 16e9cfc..c336fcf 100644 --- a/src/RESTAPI/RESTAPI_preferences.h +++ b/src/RESTAPI/RESTAPI_preferences.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_preferences : public RESTAPIHandler { public: - RESTAPI_preferences(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_preferences(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 98315d6..78f8183 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_oauth2_handler.h" #include "RESTAPI/RESTAPI_user_handler.h" #include "RESTAPI/RESTAPI_users_handler.h" @@ -25,11 +23,13 @@ #include "RESTAPI/RESTAPI_totp_handler.h" #include "RESTAPI/RESTAPI_subtotp_handler.h" #include "RESTAPI/RESTAPI_signup_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, + Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t TransactionId) { return RESTAPI_Router< RESTAPI_oauth2_handler, @@ -53,12 +53,13 @@ namespace OpenWifi { RESTAPI_subtotp_handler, RESTAPI_signup_handler, RESTAPI_validate_sub_token_handler, - RESTAPI_validate_token_handler + RESTAPI_validate_token_handler, + RESTAPI_webSocketServer >(Path, Bindings, L, S,TransactionId); } 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_oauth2_handler, diff --git a/src/RESTAPI/RESTAPI_signup_handler.cpp b/src/RESTAPI/RESTAPI_signup_handler.cpp index 5eef231..a460450 100644 --- a/src/RESTAPI/RESTAPI_signup_handler.cpp +++ b/src/RESTAPI/RESTAPI_signup_handler.cpp @@ -5,6 +5,7 @@ #include "RESTAPI_signup_handler.h" #include "StorageService.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" +#include "framework/MicroServiceFuncs.h" #define __DBG__ std::cout << __LINE__ << std::endl; namespace OpenWifi { @@ -43,7 +44,7 @@ namespace OpenWifi { NewSub.name = UserName; NewSub.modified = OpenWifi::Now(); NewSub.creationDate = OpenWifi::Now(); - NewSub.id = MicroService::instance().CreateUUID(); + NewSub.id = MicroServiceCreateUUID(); NewSub.email = UserName; NewSub.userRole = SecurityObjects::SUBSCRIBER; NewSub.changePassword = true; @@ -55,7 +56,7 @@ namespace OpenWifi { SecurityObjects::ActionLink NewLink; NewLink.action = OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP; - NewLink.id = MicroService::CreateUUID(); + NewLink.id = MicroServiceCreateUUID(); NewLink.userId = NewSub.id; NewLink.created = OpenWifi::Now(); NewLink.expires = NewLink.created + (1*60*60); // 1 hour diff --git a/src/RESTAPI/RESTAPI_signup_handler.h b/src/RESTAPI/RESTAPI_signup_handler.h index 1389782..b163cf0 100644 --- a/src/RESTAPI/RESTAPI_signup_handler.h +++ b/src/RESTAPI/RESTAPI_signup_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.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_sms_handler.cpp b/src/RESTAPI/RESTAPI_sms_handler.cpp index 51f2fb9..dd1f2ec 100644 --- a/src/RESTAPI/RESTAPI_sms_handler.cpp +++ b/src/RESTAPI/RESTAPI_sms_handler.cpp @@ -5,7 +5,6 @@ #include "RESTAPI_sms_handler.h" #include "SMSSender.h" #include "framework/ow_constants.h" -#include "framework/MicroService.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_sms_handler.h b/src/RESTAPI/RESTAPI_sms_handler.h index 4e1a158..b24fd76 100644 --- a/src/RESTAPI/RESTAPI_sms_handler.h +++ b/src/RESTAPI/RESTAPI_sms_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_sms_handler : public RESTAPIHandler { public: - RESTAPI_sms_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_sms_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, Poco::Net::HTTPRequest::HTTP_OPTIONS}, diff --git a/src/RESTAPI/RESTAPI_subavatar_handler.cpp b/src/RESTAPI/RESTAPI_subavatar_handler.cpp index a0833b7..e9002ac 100644 --- a/src/RESTAPI/RESTAPI_subavatar_handler.cpp +++ b/src/RESTAPI/RESTAPI_subavatar_handler.cpp @@ -8,7 +8,8 @@ #include "RESTAPI_subavatar_handler.h" #include "StorageService.h" #include "Poco/Net/HTMLForm.h" -#include "framework/MicroService.h" +#include "Poco/CountingStream.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { @@ -34,7 +35,7 @@ namespace OpenWifi { Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler); Poco::JSON::Object Answer; - if (!partHandler.Name().empty() && partHandler.Length()< MicroService::instance().ConfigGetInt("openwifi.avatar.maxsize",2000000)) { + if (!partHandler.Name().empty() && partHandler.Length()< MicroServiceConfigGetInt("openwifi.avatar.maxsize",2000000)) { Answer.set(RESTAPI::Protocol::AVATARID, Id); Answer.set(RESTAPI::Protocol::ERRORCODE, 0); Logger_.information(fmt::format("Uploaded avatar: {} Type: {}", partHandler.Name(), partHandler.ContentType())); diff --git a/src/RESTAPI/RESTAPI_subavatar_handler.h b/src/RESTAPI/RESTAPI_subavatar_handler.h index 1d3081b..9b7697b 100644 --- a/src/RESTAPI/RESTAPI_subavatar_handler.h +++ b/src/RESTAPI/RESTAPI_subavatar_handler.h @@ -3,7 +3,8 @@ // #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" +#include "Poco/Net/PartHandler.h" namespace OpenWifi { @@ -32,7 +33,7 @@ namespace OpenWifi { class RESTAPI_subavatar_handler : public RESTAPIHandler { public: - RESTAPI_subavatar_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_subavatar_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_submfa_handler.cpp b/src/RESTAPI/RESTAPI_submfa_handler.cpp index 3826405..0ecd31e 100644 --- a/src/RESTAPI/RESTAPI_submfa_handler.cpp +++ b/src/RESTAPI/RESTAPI_submfa_handler.cpp @@ -5,6 +5,7 @@ #include "RESTAPI_submfa_handler.h" #include "StorageService.h" #include "SMSSender.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { @@ -64,7 +65,7 @@ namespace OpenWifi { MFC.sms = MFC.sms; MFC.type = "email"; MFC.email = UserInfo_.userinfo.email; - MFC.id = MicroService::instance().CreateUUID(); + MFC.id = MicroServiceCreateUUID(); Poco::JSON::Object Answer; MFC.to_json(Answer); @@ -116,7 +117,7 @@ namespace OpenWifi { MFC.sms = MFC.sms; MFC.type = "sms"; MFC.email = UserInfo_.userinfo.email; - MFC.id = MicroService::instance().CreateUUID(); + MFC.id = MicroServiceCreateUUID(); Poco::JSON::Object Answer; MFC.to_json(Answer); diff --git a/src/RESTAPI/RESTAPI_submfa_handler.h b/src/RESTAPI/RESTAPI_submfa_handler.h index 06aa4a6..0da1eb1 100644 --- a/src/RESTAPI/RESTAPI_submfa_handler.h +++ b/src/RESTAPI/RESTAPI_submfa_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_submfa_handler : public RESTAPIHandler { public: - RESTAPI_submfa_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_submfa_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_PUT, Poco::Net::HTTPRequest::HTTP_GET, diff --git a/src/RESTAPI/RESTAPI_suboauth2_handler.cpp b/src/RESTAPI/RESTAPI_suboauth2_handler.cpp index b392b9e..2781c98 100644 --- a/src/RESTAPI/RESTAPI_suboauth2_handler.cpp +++ b/src/RESTAPI/RESTAPI_suboauth2_handler.cpp @@ -5,7 +5,6 @@ #include "RESTAPI_suboauth2_handler.h" #include "AuthService.h" #include "MFAServer.h" -#include "framework/MicroService.h" #include "StorageService.h" #include "RESTAPI/RESTAPI_db_helpers.h" @@ -87,7 +86,7 @@ namespace OpenWifi { SecurityObjects::ActionLink NewLink; NewLink.action = OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD; - NewLink.id = MicroService::CreateUUID(); + NewLink.id = MicroServiceCreateUUID(); NewLink.userId = UInfo1.id; NewLink.created = OpenWifi::Now(); NewLink.expires = NewLink.created + (24*60*60); diff --git a/src/RESTAPI/RESTAPI_suboauth2_handler.h b/src/RESTAPI/RESTAPI_suboauth2_handler.h index 0da37ea..567d0d4 100644 --- a/src/RESTAPI/RESTAPI_suboauth2_handler.h +++ b/src/RESTAPI/RESTAPI_suboauth2_handler.h @@ -3,12 +3,12 @@ // #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_suboauth2_handler : public RESTAPIHandler { public: - RESTAPI_suboauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_suboauth2_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, Poco::Net::HTTPRequest::HTTP_DELETE, diff --git a/src/RESTAPI/RESTAPI_subpreferences.h b/src/RESTAPI/RESTAPI_subpreferences.h index 0814382..0a71616 100644 --- a/src/RESTAPI/RESTAPI_subpreferences.h +++ b/src/RESTAPI/RESTAPI_subpreferences.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_subpreferences : public RESTAPIHandler { public: - RESTAPI_subpreferences(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_subpreferences(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_subtotp_handler.cpp b/src/RESTAPI/RESTAPI_subtotp_handler.cpp index 9acc3a9..0772a59 100644 --- a/src/RESTAPI/RESTAPI_subtotp_handler.cpp +++ b/src/RESTAPI/RESTAPI_subtotp_handler.cpp @@ -5,6 +5,7 @@ #include "RESTAPI_subtotp_handler.h" #include "TotpCache.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_subtotp_handler.h b/src/RESTAPI/RESTAPI_subtotp_handler.h index e714686..cee4aaf 100644 --- a/src/RESTAPI/RESTAPI_subtotp_handler.h +++ b/src/RESTAPI/RESTAPI_subtotp_handler.h @@ -2,12 +2,12 @@ // Created by stephane bourque on 2022-01-31. // -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_subtotp_handler : public RESTAPIHandler { public: - RESTAPI_subtotp_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_subtotp_handler(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_subuser_handler.cpp b/src/RESTAPI/RESTAPI_subuser_handler.cpp index 98ceaa5..738a8cd 100644 --- a/src/RESTAPI/RESTAPI_subuser_handler.cpp +++ b/src/RESTAPI/RESTAPI_subuser_handler.cpp @@ -13,6 +13,8 @@ #include "MFAServer.h" #include "TotpCache.h" +#include "framework/MicroServiceFuncs.h" + namespace OpenWifi { void RESTAPI_subuser_handler::DoGet() { @@ -183,7 +185,7 @@ namespace OpenWifi { SecurityObjects::ActionLink NewLink; NewLink.action = OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD; - NewLink.id = MicroService::CreateUUID(); + NewLink.id = MicroServiceCreateUUID(); NewLink.userId = Existing.id; NewLink.created = OpenWifi::Now(); NewLink.expires = NewLink.created + (24*60*60); diff --git a/src/RESTAPI/RESTAPI_subuser_handler.h b/src/RESTAPI/RESTAPI_subuser_handler.h index d9e212f..f9432ad 100644 --- a/src/RESTAPI/RESTAPI_subuser_handler.h +++ b/src/RESTAPI/RESTAPI_subuser_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_subuser_handler : public RESTAPIHandler { public: - RESTAPI_subuser_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_subuser_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_subusers_handler.cpp b/src/RESTAPI/RESTAPI_subusers_handler.cpp index 4ea2df4..4cbe97a 100644 --- a/src/RESTAPI/RESTAPI_subusers_handler.cpp +++ b/src/RESTAPI/RESTAPI_subusers_handler.cpp @@ -4,7 +4,6 @@ #include "RESTAPI_subusers_handler.h" #include "StorageService.h" -#include "framework/MicroService.h" #include "RESTAPI/RESTAPI_db_helpers.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_subusers_handler.h b/src/RESTAPI/RESTAPI_subusers_handler.h index 6be6723..78fa1ba 100644 --- a/src/RESTAPI/RESTAPI_subusers_handler.h +++ b/src/RESTAPI/RESTAPI_subusers_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_subusers_handler : public RESTAPIHandler { public: - RESTAPI_subusers_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_subusers_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_system_endpoints_handler.cpp b/src/RESTAPI/RESTAPI_system_endpoints_handler.cpp index ddc777f..549c493 100644 --- a/src/RESTAPI/RESTAPI_system_endpoints_handler.cpp +++ b/src/RESTAPI/RESTAPI_system_endpoints_handler.cpp @@ -4,11 +4,12 @@ #include "RESTAPI_system_endpoints_handler.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { void RESTAPI_system_endpoints_handler::DoGet() { - auto Services = MicroService::instance().GetServices(); + auto Services = MicroServiceGetServices(); SecurityObjects::SystemEndpointList L; for(const auto &i:Services) { SecurityObjects::SystemEndpoint S{ diff --git a/src/RESTAPI/RESTAPI_system_endpoints_handler.h b/src/RESTAPI/RESTAPI_system_endpoints_handler.h index 2aade34..6caffe6 100644 --- a/src/RESTAPI/RESTAPI_system_endpoints_handler.h +++ b/src/RESTAPI/RESTAPI_system_endpoints_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "../framework/MicroService.h" +#include "../framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_system_endpoints_handler : public RESTAPIHandler { public: - RESTAPI_system_endpoints_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_system_endpoints_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_totp_handler.h b/src/RESTAPI/RESTAPI_totp_handler.h index d5534cb..1f6cde1 100644 --- a/src/RESTAPI/RESTAPI_totp_handler.h +++ b/src/RESTAPI/RESTAPI_totp_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_totp_handler : public RESTAPIHandler { public: - RESTAPI_totp_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_totp_handler(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_user_handler.cpp b/src/RESTAPI/RESTAPI_user_handler.cpp index b04980f..0a252f0 100644 --- a/src/RESTAPI/RESTAPI_user_handler.cpp +++ b/src/RESTAPI/RESTAPI_user_handler.cpp @@ -12,6 +12,7 @@ #include "RESTAPI/RESTAPI_db_helpers.h" #include "MFAServer.h" #include "TotpCache.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { @@ -191,7 +192,7 @@ namespace OpenWifi { SecurityObjects::ActionLink NewLink; NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD; - NewLink.id = MicroService::CreateUUID(); + NewLink.id = MicroServiceCreateUUID(); NewLink.userId = Existing.id; NewLink.created = OpenWifi::Now(); NewLink.expires = NewLink.created + (24*60*60); diff --git a/src/RESTAPI/RESTAPI_user_handler.h b/src/RESTAPI/RESTAPI_user_handler.h index 41d6ba1..29a42de 100644 --- a/src/RESTAPI/RESTAPI_user_handler.h +++ b/src/RESTAPI/RESTAPI_user_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_user_handler : public RESTAPIHandler { public: - RESTAPI_user_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_user_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_users_handler.cpp b/src/RESTAPI/RESTAPI_users_handler.cpp index 8de7c02..cc8253c 100644 --- a/src/RESTAPI/RESTAPI_users_handler.cpp +++ b/src/RESTAPI/RESTAPI_users_handler.cpp @@ -4,7 +4,6 @@ #include "RESTAPI_users_handler.h" #include "StorageService.h" -#include "framework/MicroService.h" #include "RESTAPI/RESTAPI_db_helpers.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_users_handler.h b/src/RESTAPI/RESTAPI_users_handler.h index 9074803..a1c5bda 100644 --- a/src/RESTAPI/RESTAPI_users_handler.h +++ b/src/RESTAPI/RESTAPI_users_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_users_handler : public RESTAPIHandler { public: - RESTAPI_users_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_users_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_validate_sub_token_handler.h b/src/RESTAPI/RESTAPI_validate_sub_token_handler.h index 1d2b9d4..907bdb9 100644 --- a/src/RESTAPI/RESTAPI_validate_sub_token_handler.h +++ b/src/RESTAPI/RESTAPI_validate_sub_token_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_validate_sub_token_handler : public RESTAPIHandler { public: - RESTAPI_validate_sub_token_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_validate_sub_token_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_validate_token_handler.h b/src/RESTAPI/RESTAPI_validate_token_handler.h index 068a542..c08d382 100644 --- a/src/RESTAPI/RESTAPI_validate_token_handler.h +++ b/src/RESTAPI/RESTAPI_validate_token_handler.h @@ -4,12 +4,12 @@ #pragma once -#include "framework/MicroService.h" +#include "framework/RESTAPI_Handler.h" namespace OpenWifi { class RESTAPI_validate_token_handler : public RESTAPIHandler { public: - RESTAPI_validate_token_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, uint64_t TransactionId, bool Internal) + RESTAPI_validate_token_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_AnalyticsObjects.h b/src/RESTObjects/RESTAPI_AnalyticsObjects.h index c1328f4..d5585f5 100644 --- a/src/RESTObjects/RESTAPI_AnalyticsObjects.h +++ b/src/RESTObjects/RESTAPI_AnalyticsObjects.h @@ -5,6 +5,7 @@ #pragma once #include "RESTAPI_ProvObjects.h" +#include "framework/utils.h" #include namespace OpenWifi { @@ -375,7 +376,7 @@ namespace OpenWifi { }; struct WifiClientHistory { - uint64_t timestamp=OpenWifi::Now(); + uint64_t timestamp=Utils::Now(); std::string station_id; std::string bssid; std::string ssid; diff --git a/src/RESTObjects/RESTAPI_CertObjects.cpp b/src/RESTObjects/RESTAPI_CertObjects.cpp index ea55bd4..71ed5af 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; @@ -154,6 +154,7 @@ namespace OpenWifi::CertObjects { field_to_json(Obj,"submitted", submitted); field_to_json(Obj,"started", started); field_to_json(Obj,"completed", completed); + field_to_json(Obj,"requesterUsername", requesterUsername); } bool JobEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { @@ -171,6 +172,7 @@ namespace OpenWifi::CertObjects { field_from_json(Obj,"submitted", submitted); field_from_json(Obj,"started", started); field_from_json(Obj,"completed", completed); + field_from_json(Obj,"requesterUsername", requesterUsername); return true; } catch (...) { } diff --git a/src/RESTObjects/RESTAPI_CertObjects.h b/src/RESTObjects/RESTAPI_CertObjects.h index 40b75a0..29b902f 100644 --- a/src/RESTObjects/RESTAPI_CertObjects.h +++ b/src/RESTObjects/RESTAPI_CertObjects.h @@ -91,6 +91,7 @@ namespace OpenWifi::CertObjects { uint64_t submitted=0; uint64_t started=0; uint64_t completed=0; + std::string requesterUsername; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); diff --git a/src/RESTObjects/RESTAPI_FMSObjects.cpp b/src/RESTObjects/RESTAPI_FMSObjects.cpp index ceb3619..5c42da9 100644 --- a/src/RESTObjects/RESTAPI_FMSObjects.cpp +++ b/src/RESTObjects/RESTAPI_FMSObjects.cpp @@ -3,7 +3,8 @@ // #include "RESTAPI_FMSObjects.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" +#include "framework/utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; @@ -233,7 +234,7 @@ namespace OpenWifi::FMSObjects { UnknownFirmwares_.clear(); totalSecondsOld_.clear(); numberOfDevices = 0 ; - snapshot = OpenWifi::Now(); + snapshot = Utils::Now(); } bool DeviceReport::from_json([[maybe_unused]] const Poco::JSON::Object::Ptr &Obj) { diff --git a/src/RESTObjects/RESTAPI_GWobjects.cpp b/src/RESTObjects/RESTAPI_GWobjects.cpp index d6b3f1d..07ed9e1 100644 --- a/src/RESTObjects/RESTAPI_GWobjects.cpp +++ b/src/RESTObjects/RESTAPI_GWobjects.cpp @@ -11,12 +11,13 @@ #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" +#include "framework/utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; @@ -49,6 +50,8 @@ namespace OpenWifi::GWObjects { field_to_json(Obj,"entity", entity); field_to_json(Obj,"modified", modified); field_to_json(Obj,"locale", locale); + field_to_json(Obj,"restrictedDevice", restrictedDevice); + } void Device::to_json_with_status(Poco::JSON::Object &Obj) const { @@ -57,7 +60,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", ""); @@ -69,6 +72,7 @@ namespace OpenWifi::GWObjects { 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); + field_to_json(Obj,"associations_6G", (uint64_t) 0); } #endif } @@ -88,6 +92,7 @@ namespace OpenWifi::GWObjects { field_from_json(Obj,"subscriber", subscriber); field_from_json(Obj,"entity", entity); field_from_json(Obj,"locale", locale); + field_from_json(Obj,"restrictedDevice", restrictedDevice); return true; } catch (const Poco::Exception &E) { } @@ -198,6 +203,7 @@ namespace OpenWifi::GWObjects { 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,"associations_6G", Associations_6G); field_to_json(Obj,"webSocketClients", webSocketClients); field_to_json(Obj,"websocketPackets", websocketPackets); field_to_json(Obj,"kafkaClients", kafkaClients); @@ -206,7 +212,8 @@ namespace OpenWifi::GWObjects { field_to_json(Obj,"started", started); field_to_json(Obj,"sessionId", sessionId); field_to_json(Obj,"connectionCompletionTime", connectionCompletionTime); - field_to_json(Obj,"totalConnectionTime", OpenWifi::Now() - started); + field_to_json(Obj,"totalConnectionTime", Utils::Now() - started); + field_to_json(Obj,"certificateExpiryDate", certificateExpiryDate); switch(VerifiedCertificate) { case NO_CERTIFICATE: @@ -225,12 +232,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) { } @@ -283,7 +292,7 @@ namespace OpenWifi::GWObjects { lastContact.clear(); associations.clear(); numberOfDevices = 0 ; - snapshot = OpenWifi::Now(); + snapshot = Utils::Now(); } void CapabilitiesModel::to_json(Poco::JSON::Object &Obj) const{ @@ -296,8 +305,10 @@ namespace OpenWifi::GWObjects { 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); + field_to_json(Obj,"signature", signature); + field_to_json(Obj,"deferred", deferred); + field_to_json(Obj,"uri", uri); } bool ScriptRequest::from_json(const Poco::JSON::Object::Ptr &Obj) { @@ -306,8 +317,10 @@ namespace OpenWifi::GWObjects { 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); + field_from_json(Obj,"signature", signature); + field_from_json(Obj,"deferred", deferred); + field_from_json(Obj,"uri", uri); return true; } catch (const Poco::Exception &E) { } diff --git a/src/RESTObjects/RESTAPI_GWobjects.h b/src/RESTObjects/RESTAPI_GWobjects.h index 2a2b2a9..8e53bd6 100644 --- a/src/RESTObjects/RESTAPI_GWobjects.h +++ b/src/RESTObjects/RESTAPI_GWobjects.h @@ -28,19 +28,21 @@ namespace OpenWifi::GWObjects { uint64_t TX = 0, RX = 0; uint64_t Associations_2G=0; uint64_t Associations_5G=0; + uint64_t Associations_6G=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; - uint64_t started=0; - uint64_t sessionId=0; - double connectionCompletionTime=0.0; + std::string Compatible; + uint64_t kafkaClients=0; + uint64_t webSocketClients=0; + uint64_t kafkaPackets=0; + uint64_t websocketPackets=0; + std::string locale; + uint64_t started=0; + uint64_t sessionId=0; + double connectionCompletionTime=0.0; + std::uint64_t certificateExpiryDate=0; void to_json(Poco::JSON::Object &Obj) const; }; @@ -68,6 +70,7 @@ namespace OpenWifi::GWObjects { std::string entity; uint64_t modified=0; std::string locale; + bool restrictedDevice=false; void to_json(Poco::JSON::Object &Obj) const; void to_json_with_status(Poco::JSON::Object &Obj) const; @@ -78,6 +81,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); }; @@ -212,12 +217,15 @@ namespace OpenWifi::GWObjects { }; struct ScriptRequest { - uint64_t timeout=30; std::string serialNumber; + uint64_t timeout=30; std::string type; std::string script; - std::string scriptId; - uint64_t when=0; + std::uint64_t when; + std::string signature; + bool deferred; + std::string uri; + 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 948c787..c5387a8 100644 --- a/src/RESTObjects/RESTAPI_ProvObjects.cpp +++ b/src/RESTObjects/RESTAPI_ProvObjects.cpp @@ -8,7 +8,9 @@ #include "RESTAPI_ProvObjects.h" -#include "framework/MicroService.h" +#include "framework/RESTAPI_utils.h" +#include "framework/MicroServiceFuncs.h" +#include "framework/utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; @@ -600,6 +602,7 @@ namespace OpenWifi::ProvObjects { field_to_json( Obj, "devClass",devClass); field_to_json( Obj, "locale",locale); field_to_json( Obj, "realMacAddress",realMacAddress); + field_to_json( Obj, "doNotAllowOverrides",doNotAllowOverrides); } bool InventoryTag::from_json(const Poco::JSON::Object::Ptr &Obj) { @@ -621,6 +624,7 @@ namespace OpenWifi::ProvObjects { field_from_json( Obj,"devClass",devClass); field_from_json( Obj,"locale",locale); field_from_json( Obj,"realMacAddress",realMacAddress); + field_from_json( Obj, "doNotAllowOverrides",doNotAllowOverrides); return true; } catch(...) { @@ -1091,7 +1095,7 @@ namespace OpenWifi::ProvObjects { } bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) { - uint64_t Now = OpenWifi::Now(); + uint64_t Now = Utils::Now(); if(O->has("name")) I.name = O->get("name").toString(); @@ -1112,7 +1116,7 @@ namespace OpenWifi::ProvObjects { } bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) { - uint64_t Now = OpenWifi::Now(); + uint64_t Now = Utils::Now(); if(O->has("name")) I.name = O->get("name").toString(); @@ -1130,14 +1134,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.modified = I.created = Utils::Now(); + I.id = MicroServiceCreateUUID(); return true; } @@ -1159,5 +1163,82 @@ namespace OpenWifi::ProvObjects { return false; } + void RRMAlgorithmDetails::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"name",name); + field_to_json(Obj,"parameters",parameters); + } + + bool RRMAlgorithmDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"name",name); + field_from_json(Obj,"parameters",parameters); + return true; + } catch(...) { + + } + return false; + } + + void RRMDetails::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"vendor",vendor); + field_to_json(Obj,"schedule",schedule); + field_to_json(Obj,"algorithms",algorithms); + } + + bool RRMDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"vendor",vendor); + field_from_json(Obj,"schedule",schedule); + field_from_json(Obj,"algorithms",algorithms); + return true; + } catch(...) { + + } + return false; + } + + void ConfigurationOverride::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"source",source); + field_to_json(Obj,"reason",reason); + field_to_json(Obj,"parameterName",parameterName); + field_to_json(Obj,"parameterType",parameterType); + field_to_json(Obj,"parameterValue",parameterValue); + field_to_json(Obj,"modified",modified); + } + + bool ConfigurationOverride::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"source",source); + field_from_json(Obj,"reason",reason); + field_from_json(Obj,"parameterName",parameterName); + field_from_json(Obj,"parameterType",parameterType); + field_from_json(Obj,"parameterValue",parameterValue); + field_from_json(Obj,"modified",modified); + return true; + } catch(...) { + + } + return false; + } + + void ConfigurationOverrideList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"serialNumber",serialNumber); + field_to_json(Obj,"managementPolicy",managementPolicy); + field_to_json(Obj,"overrides",overrides); + } + + bool ConfigurationOverrideList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"serialNumber",serialNumber); + field_from_json(Obj,"managementPolicy",managementPolicy); + field_from_json(Obj,"overrides",overrides); + return true; + } catch(...) { + + } + return false; + } + } + diff --git a/src/RESTObjects/RESTAPI_ProvObjects.h b/src/RESTObjects/RESTAPI_ProvObjects.h index 4452af9..3c0ed3e 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 { @@ -62,6 +61,21 @@ namespace OpenWifi::ProvObjects { }; typedef std::vector ManagementPolicyVec; + struct RRMAlgorithmDetails { + std::string name; + std::string parameters; + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct RRMDetails { + std::string vendor; + std::string schedule; + std::vector algorithms; + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + struct DeviceRules { std::string rcOnly{"inherit"}; std::string rrm{"inherit"}; @@ -414,6 +428,7 @@ namespace OpenWifi::ProvObjects { std::string devClass; std::string locale; std::string realMacAddress; + bool doNotAllowOverrides=false; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); @@ -679,6 +694,27 @@ namespace OpenWifi::ProvObjects { bool from_json(const Poco::JSON::Object::Ptr &Obj); }; + struct ConfigurationOverride { + std::string source; + std::string reason; + std::string parameterName; + std::string parameterType; + std::string parameterValue; + std::uint64_t modified; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct ConfigurationOverrideList { + std::string serialNumber; + Types::UUID_t managementPolicy; + std::vector overrides; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I); bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I); bool CreateObjectInfo(const SecurityObjects::UserInfo &U, ObjectInfo &I); diff --git a/src/RESTObjects/RESTAPI_SecurityObjects.cpp b/src/RESTObjects/RESTAPI_SecurityObjects.cpp index a077e40..9b84be3 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; @@ -433,7 +433,7 @@ namespace OpenWifi::SecurityObjects { SecurityObjects::NoteInfoVec NIV; NIV = RESTAPI_utils::to_object_array(Obj->get("notes").toString()); for(auto const &i:NIV) { - SecurityObjects::NoteInfo ii{.created=(uint64_t)OpenWifi::Now(), .createdBy=UInfo.email, .note=i.note}; + SecurityObjects::NoteInfo ii{.created=(uint64_t)Utils::Now(), .createdBy=UInfo.email, .note=i.note}; Notes.push_back(ii); } } @@ -446,7 +446,7 @@ namespace OpenWifi::SecurityObjects { bool MergeNotes(const NoteInfoVec & NewNotes, const UserInfo &UInfo, NoteInfoVec & ExistingNotes) { for(auto const &i:NewNotes) { - SecurityObjects::NoteInfo ii{.created=(uint64_t)OpenWifi::Now(), .createdBy=UInfo.email, .note=i.note}; + SecurityObjects::NoteInfo ii{.created=(uint64_t)Utils::Now(), .createdBy=UInfo.email, .note=i.note}; ExistingNotes.push_back(ii); } return true; diff --git a/src/RESTObjects/RESTAPI_SecurityObjects.h b/src/RESTObjects/RESTAPI_SecurityObjects.h index ef74bc4..3cee8cc 100644 --- a/src/RESTObjects/RESTAPI_SecurityObjects.h +++ b/src/RESTObjects/RESTAPI_SecurityObjects.h @@ -14,6 +14,7 @@ #include "Poco/JSON/Object.h" #include "Poco/Data/LOB.h" #include "Poco/Data/LOBStream.h" +#include "framework/utils.h" namespace OpenWifi { uint64_t Now(); @@ -62,7 +63,7 @@ namespace OpenWifi { std::string UserTypeToString(USER_ROLE U); struct NoteInfo { - uint64_t created=0; // = OpenWifi::Now(); + uint64_t created=0; // = Utils::Now(); std::string createdBy; std::string note; @@ -101,7 +102,7 @@ namespace OpenWifi { std::string uuid; std::string question; std::string method; - uint64_t created = OpenWifi::Now(); + uint64_t created = Utils::Now(); void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); @@ -264,7 +265,7 @@ namespace OpenWifi { std::string locale; std::string message; uint64_t sent=0; - uint64_t created=OpenWifi::Now(); + uint64_t created=Utils::Now(); uint64_t expires=0; uint64_t completed=0; uint64_t canceled=0; 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/SMSSender.cpp b/src/SMSSender.cpp index e52b66b..46f764f 100644 --- a/src/SMSSender.cpp +++ b/src/SMSSender.cpp @@ -2,21 +2,18 @@ // Created by stephane bourque on 2021-10-09. // -#include - -#include "framework/MicroService.h" - #include "MFAServer.h" #include "SMS_provider_aws.h" #include "SMS_provider_twilio.h" #include "SMSSender.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { int SMSSender::Start() { - Enabled_ = MicroService::instance().ConfigGetBool("smssender.enabled",false); + Enabled_ = MicroServiceConfigGetBool("smssender.enabled",false); if(Enabled_) { - Provider_ = MicroService::instance().ConfigGetString("smssender.provider","aws"); + Provider_ = MicroServiceConfigGetString("smssender.provider","aws"); if(Provider_=="aws") { ProviderImpl_ = std::make_unique(Logger()); } else if(Provider_=="twilio") { diff --git a/src/SMSSender.h b/src/SMSSender.h index fcb422e..ea647e3 100644 --- a/src/SMSSender.h +++ b/src/SMSSender.h @@ -2,14 +2,13 @@ // Created by stephane bourque on 2021-10-09. // -#ifndef OWSEC_SMSSENDER_H -#define OWSEC_SMSSENDER_H +#pragma once #include #include #include -#include "framework/MicroService.h" +#include "framework/SubSystemServer.h" #include "SMS_provider.h" namespace OpenWifi { @@ -54,6 +53,3 @@ namespace OpenWifi { inline SMSSender * SMSSender() { return SMSSender::instance(); } } - - -#endif //OWSEC_SMSSENDER_H diff --git a/src/SMS_provider_aws.cpp b/src/SMS_provider_aws.cpp index 20023d8..9e4a6d3 100644 --- a/src/SMS_provider_aws.cpp +++ b/src/SMS_provider_aws.cpp @@ -2,19 +2,20 @@ // Created by stephane bourque on 2021-10-15. // +#include "SMS_provider_aws.h" #include #include #include -#include "framework/MicroService.h" -#include "SMS_provider_aws.h" +#include "framework/MicroServiceFuncs.h" +#include "fmt/format.h" namespace OpenWifi { bool SMS_provider_aws::Initialize() { - SecretKey_ = MicroService::instance().ConfigGetString("smssender.aws.secretkey",""); - AccessKey_ = MicroService::instance().ConfigGetString("smssender.aws.accesskey",""); - Region_ = MicroService::instance().ConfigGetString("smssender.aws.region",""); + SecretKey_ = MicroServiceConfigGetString("smssender.aws.secretkey",""); + AccessKey_ = MicroServiceConfigGetString("smssender.aws.accesskey",""); + Region_ = MicroServiceConfigGetString("smssender.aws.region",""); if(SecretKey_.empty() || AccessKey_.empty() || Region_.empty()) { poco_debug(Logger(),"SMSSender is disabled. Please provide key, secret, and region."); diff --git a/src/SMS_provider_aws.h b/src/SMS_provider_aws.h index fbc2a92..2d6377f 100644 --- a/src/SMS_provider_aws.h +++ b/src/SMS_provider_aws.h @@ -2,8 +2,7 @@ // Created by stephane bourque on 2021-10-15. // -#ifndef OWSEC_SMS_PROVIDER_AWS_H -#define OWSEC_SMS_PROVIDER_AWS_H +#pragma once #include #include @@ -32,5 +31,3 @@ namespace OpenWifi { Aws::Auth::AWSCredentials AwsCreds_; }; } - -#endif //OWSEC_SMS_PROVIDER_AWS_H diff --git a/src/SMS_provider_twilio.cpp b/src/SMS_provider_twilio.cpp index 7709753..f893b4b 100644 --- a/src/SMS_provider_twilio.cpp +++ b/src/SMS_provider_twilio.cpp @@ -4,19 +4,20 @@ #include "SMS_provider_twilio.h" -#include "framework/MicroService.h" - #include "Poco/Net/HTTPBasicCredentials.h" #include "Poco/URI.h" #include "Poco/Net/HTMLForm.h" #include "Poco/Net/HTTPSClientSession.h" #include "Poco/Net/HTTPResponse.h" +#include "framework/MicroServiceFuncs.h" +#include "fmt/format.h" + namespace OpenWifi { bool SMS_provider_twilio::Initialize() { - Sid_ = MicroService::instance().ConfigGetString("smssender.twilio.sid",""); - Token_ = MicroService::instance().ConfigGetString("smssender.twilio.token",""); - PhoneNumber_ = MicroService::instance().ConfigGetString("smssender.twilio.phonenumber",""); + Sid_ = MicroServiceConfigGetString("smssender.twilio.sid",""); + Token_ = MicroServiceConfigGetString("smssender.twilio.token",""); + PhoneNumber_ = MicroServiceConfigGetString("smssender.twilio.phonenumber",""); if(Sid_.empty() || Token_.empty() || PhoneNumber_.empty()) { poco_debug(Logger(),"SMSSender is disabled. Please provide SID, TOKEN, and PHONE NUMBER."); diff --git a/src/SMTPMailerService.cpp b/src/SMTPMailerService.cpp index 56080c6..9f69ee6 100644 --- a/src/SMTPMailerService.cpp +++ b/src/SMTPMailerService.cpp @@ -1,9 +1,6 @@ // // Created by stephane bourque on 2021-06-17. // -#include - -#include "framework/MicroService.h" #include "Poco/Net/MailMessage.h" #include "Poco/Net/MailRecipient.h" @@ -18,23 +15,27 @@ #include "SMTPMailerService.h" #include "AuthService.h" +#include "framework/MicroServiceFuncs.h" +#include "framework/utils.h" +#include "fmt/format.h" + namespace OpenWifi { void SMTPMailerService::LoadMyConfig() { - Enabled_ = MicroService::instance().ConfigGetBool("mailer.enabled",false); + Enabled_ = MicroServiceConfigGetBool("mailer.enabled",false); if(Enabled_) { - MailHost_ = MicroService::instance().ConfigGetString("mailer.hostname"); - SenderLoginUserName_ = MicroService::instance().ConfigGetString("mailer.username"); - SenderLoginPassword_ = MicroService::instance().ConfigGetString("mailer.password"); - Sender_ = MicroService::instance().ConfigGetString("mailer.sender"); - LoginMethod_ = MicroService::instance().ConfigGetString("mailer.loginmethod"); - MailHostPort_ = MicroService::instance().ConfigGetInt("mailer.port"); - TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir()); - MailRetry_ = MicroService::instance().ConfigGetInt("mailer.retry",2*60); - MailAbandon_ = MicroService::instance().ConfigGetInt("mailer.abandon",2*60*60); - UseHTML_ = MicroService::instance().ConfigGetBool("mailer.html",false); + MailHost_ = MicroServiceConfigGetString("mailer.hostname",""); + SenderLoginUserName_ = MicroServiceConfigGetString("mailer.username",""); + SenderLoginPassword_ = MicroServiceConfigGetString("mailer.password",""); + Sender_ = MicroServiceConfigGetString("mailer.sender",""); + LoginMethod_ = MicroServiceConfigGetString("mailer.loginmethod",""); + MailHostPort_ = MicroServiceConfigGetInt("mailer.port", 587); + TemplateDir_ = MicroServiceConfigPath("mailer.templates", MicroServiceDataDirectory()); + MailRetry_ = MicroServiceConfigGetInt("mailer.retry",2*60); + MailAbandon_ = MicroServiceConfigGetInt("mailer.abandon",2*60*60); + UseHTML_ = MicroServiceConfigGetBool("mailer.html",false); Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty()); - EmailLogo_ = TemplateDir_ + "/" + MicroService::instance().ConfigGetString("mailer.logo","logo.jpg"); + EmailLogo_ = TemplateDir_ + "/" + MicroServiceConfigGetString("mailer.logo","logo.jpg"); } } @@ -51,7 +52,7 @@ namespace OpenWifi { } void SMTPMailerService::reinitialize([[maybe_unused]] Poco::Util::Application &self) { - MicroService::instance().LoadConfigurationFile(); + MicroServiceLoadConfigurationFile(); poco_information(Logger(),"Reinitializing."); LoadMyConfig(); } diff --git a/src/SMTPMailerService.h b/src/SMTPMailerService.h index e1739b1..a048e8f 100644 --- a/src/SMTPMailerService.h +++ b/src/SMTPMailerService.h @@ -2,15 +2,14 @@ // Created by stephane bourque on 2021-06-17. // -#ifndef UCENTRALSEC_SMTPMAILERSERVICE_H -#define UCENTRALSEC_SMTPMAILERSERVICE_H - -#include "framework/MicroService.h" +#pragma once #include "Poco/File.h" #include "Poco/Net/InvalidCertificateHandler.h" #include "Poco/Net/AcceptCertificateHandler.h" +#include "framework/SubSystemServer.h" + namespace OpenWifi { enum MESSAGE_ATTRIBUTES { @@ -116,4 +115,3 @@ namespace OpenWifi { inline SMTPMailerService * SMTPMailerService() { return SMTPMailerService::instance(); } } -#endif //UCENTRALSEC_SMTPMAILERSERVICE_H diff --git a/src/SpecialUserHelpers.h b/src/SpecialUserHelpers.h index 136f6a2..82c612c 100644 --- a/src/SpecialUserHelpers.h +++ b/src/SpecialUserHelpers.h @@ -5,6 +5,8 @@ #pragma once #include "StorageService.h" +#include "framework/AppServiceRegistry.h" +#include "framework/MicroServiceFuncs.h" namespace OpenWifi { @@ -17,9 +19,9 @@ namespace OpenWifi { AppServiceRegistry().Get("defaultusercreated", DefaultUserCreated); if (!StorageService()->UserDB().GetUserById(NewDefaultUseridStockUUID, U) && !DefaultUserCreated) { - U.currentPassword = MicroService::instance().ConfigGetString("authentication.default.password", ""); + U.currentPassword = MicroServiceConfigGetString("authentication.default.password", ""); U.lastPasswords.push_back(U.currentPassword); - U.email = MicroService::instance().ConfigGetString("authentication.default.username", ""); + U.email = MicroServiceConfigGetString("authentication.default.username", ""); U.id = NewDefaultUseridStockUUID; U.userRole = SecurityObjects::ROOT; U.creationDate = OpenWifi::Now(); diff --git a/src/StorageService.cpp b/src/StorageService.cpp index c408374..44e6582 100644 --- a/src/StorageService.cpp +++ b/src/StorageService.cpp @@ -8,6 +8,8 @@ #include "StorageService.h" #include "SpecialUserHelpers.h" +#include "framework/MicroServiceFuncs.h" +#include "framework/utils.h" namespace OpenWifi { @@ -52,7 +54,7 @@ namespace OpenWifi { Archivercallback_ = std::make_unique>(Archiver_,&Archiver::onTimer); Timer_.setStartInterval( 5 * 60 * 1000); // first run in 5 minutes Timer_.setPeriodicInterval(1 * 60 * 60 * 1000); // 1 hours - Timer_.start(*Archivercallback_, MicroService::instance().TimerPool()); + Timer_.start(*Archivercallback_, MicroServiceTimerPool()); return 0; } diff --git a/src/TotpCache.h b/src/TotpCache.h index 52ae1e3..524ee62 100644 --- a/src/TotpCache.h +++ b/src/TotpCache.h @@ -2,15 +2,14 @@ // Created by stephane bourque on 2022-01-31. // -#ifndef OWSEC_TOTPCACHE_H -#define OWSEC_TOTPCACHE_H - -#include "framework/MicroService.h" +#pragma once #include "seclibs/cpptotp/bytes.h" #include "seclibs/qrcode/qrcodegen.hpp" #include "seclibs/cpptotp/otp.h" +#include "framework/MicroServiceFuncs.h" + namespace OpenWifi { class TotpCache : public SubSystemServer { @@ -35,7 +34,7 @@ namespace OpenWifi { std::string R; for(;Size;Size--) { - R += (char) MicroService::instance().Random(33,127); + R += (char) MicroServiceRandom(33,127); } Base32Secret = CppTotp::Bytes::toBase32( CppTotp::Bytes::ByteString{ (const u_char *)R.c_str()}); return R; @@ -56,13 +55,13 @@ namespace OpenWifi { uint64_t Now = OpenWifi::Now(); uint32_t p = CppTotp::totp(CppTotp::Bytes::ByteString{ (const u_char *)Secret.c_str()}, Now, 0, 30, 6); char buffer[16]{0}; - sprintf(buffer,"%06u",p); + snprintf(buffer,7,"%06u",p); Expecting = std::string(buffer); return Code == Expecting; } int Start() override { - Issuer_ = MicroService::instance().ConfigGetString("totp.issuer","OpenWiFi"); + Issuer_ = MicroServiceConfigGetString("totp.issuer","OpenWiFi"); return 0; }; @@ -164,5 +163,3 @@ namespace OpenWifi { inline auto TotpCache() { return TotpCache::instance(); } } - -#endif //OWSEC_TOTPCACHE_H diff --git a/src/framework/ALBserver.cpp b/src/framework/ALBserver.cpp new file mode 100644 index 0000000..4f3e1b1 --- /dev/null +++ b/src/framework/ALBserver.cpp @@ -0,0 +1,71 @@ +// +// 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 { + + void ALBRequestHandler::handleRequest([[maybe_unused]] Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) { + Utils::SetThreadName("alb-request"); + try { + if((id_ % 100) == 0) { + Logger_.debug(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)) { + poco_information(Logger(),"Starting..."); + 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..6316818 --- /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 { + 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; + + 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..70aa0d9 --- /dev/null +++ b/src/framework/AuthClient.h @@ -0,0 +1,60 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "framework/SubSystemServer.h" +#include "RESTObjects/RESTAPI_SecurityObjects.h" +#include "Poco/ExpireLRUCache.h" +#include "framework/utils.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_) < Utils::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..28b88f6 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; @@ -32,7 +32,7 @@ namespace OpenWifi { nlohmann::json RootSchema_; ConfigurationValidator(): - SubSystemServer("configvalidator", "CFG-VALIDATOR", "config.validator") { + SubSystemServer("ConfigValidator", "CFG-VALIDATOR", "config.validator") { } }; diff --git a/src/framework/EventBusManager.cpp b/src/framework/EventBusManager.cpp new file mode 100644 index 0000000..967ca58 --- /dev/null +++ b/src/framework/EventBusManager.cpp @@ -0,0 +1,49 @@ +// +// 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() { + poco_information(Logger(),"Starting..."); + 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..5733c93 --- /dev/null +++ b/src/framework/KafkaManager.cpp @@ -0,0 +1,365 @@ +// +// 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() { + Poco::Logger &Logger_ = Poco::Logger::create("KAFKA-PRODUCER", KafkaManager()->Logger().getChannel()); + poco_information(Logger_,"Starting..."); + + 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(Logger_,fmt::format("Caught a Kafka exception (producer): {}", E.what())); + } catch( const Poco::Exception &E) { + Logger_.log(E); + } catch (...) { + poco_error(Logger_,"std::exception"); + } + Note = Queue_.waitDequeueNotification(); + } + poco_information(Logger_,"Stopped..."); + } + + inline void KafkaConsumer::run() { + Utils::SetThreadName("Kafka:Cons"); + + Poco::Logger &Logger_ = Poco::Logger::create("KAFKA-CONSUMER", KafkaManager()->Logger().getChannel()); + + poco_information(Logger_,"Starting..."); + + 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()) { + poco_information(Logger_,fmt::format("Partition assigned: {}...", + partitions.front().get_partition())); + } + }); + Consumer.set_revocation_callback([&](const cppkafka::TopicPartitionList& partitions) { + if(!partitions.empty()) { + poco_information(Logger_,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(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(Logger_,fmt::format("Caught a Kafka exception (consumer): {}", E.what())); + } catch (const Poco::Exception &E) { + Logger_.log(E); + } catch (...) { + poco_error(Logger_,"std::exception"); + } + } + Consumer.unsubscribe(); + poco_information(Logger_,"Stopped..."); + } + + 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::Logger &Logger_ = Poco::Logger::create("KAFKA-DISPATCHER", KafkaManager()->Logger().getChannel()); + poco_information(Logger_,"Starting..."); + 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(); + } + poco_information(Logger_,"Stopped..."); + } + + 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) { + poco_information(Logger(),fmt::format("Partition assigned: {}...", partitions.front().get_partition())); + } + + void KafkaManager::PartitionRevocation(const cppkafka::TopicPartitionList& partitions) { + poco_information(Logger(),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..e96f25c --- /dev/null +++ b/src/framework/KafkaManager.h @@ -0,0 +1,122 @@ +// +// 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..2e293d6 --- /dev/null +++ b/src/framework/MicroService.cpp @@ -0,0 +1,684 @@ +// +// 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/SplitterChannel.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_GenericServerAccounting.h" +#include "framework/RESTAPI_ExtServer.h" +#include "framework/RESTAPI_IntServer.h" +#include "framework/utils.h" +#include "framework/WebSocketLogger.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 = Utils::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 = Utils::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 = Utils::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); + auto DisableWebSocketLogging = MicroService::instance().ConfigGetBool("logging.websocket",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("purgeCount", "10"); + FileChannel->setProperty("path", LoggingLocation); + if(UseAsyncLogs_) { + std::cout << __LINE__ << std::endl; + Poco::AutoPtr Async_File( + new Poco::AsyncChannel(FileChannel)); + std::cout << __LINE__ << std::endl; + Poco::AutoPtr Formatter(new Poco::PatternFormatter); + std::cout << __LINE__ << std::endl; + Formatter->setProperty("pattern", LoggingFormat); + std::cout << __LINE__ << std::endl; + Poco::AutoPtr FormattingChannel( + new Poco::FormattingChannel(Formatter, Async_File)); + std::cout << __LINE__ << std::endl; + if(DisableWebSocketLogging) { + std::cout << __LINE__ << std::endl; + Poco::Logger::root().setChannel(FormattingChannel); + } else { + std::cout << __LINE__ << std::endl; + Poco::AutoPtr WSLogger(new WebSocketLogger); + Poco::AutoPtr Splitter(new Poco::SplitterChannel); + Splitter->addChannel(WSLogger); + Splitter->addChannel(FormattingChannel); + std::cout << __LINE__ << std::endl; + Poco::Logger::root().setChannel(Splitter); + } + + } else { + Poco::AutoPtr Formatter(new Poco::PatternFormatter); + Formatter->setProperty("pattern", LoggingFormat); + Poco::AutoPtr FormattingChannel( + new Poco::FormattingChannel(Formatter, FileChannel)); + if(DisableWebSocketLogging) { + std::cout << __LINE__ << std::endl; + Poco::Logger::root().setChannel(FormattingChannel); + } else { + std::cout << __LINE__ << std::endl; + Poco::AutoPtr Splitter(new Poco::SplitterChannel); + Poco::AutoPtr WSLogger(new WebSocketLogger); + Splitter->addChannel(WSLogger); + Splitter->addChannel(FormattingChannel); + std::cout << __LINE__ << std::endl; + Poco::Logger::root().setChannel(Splitter); + } + std::cout << __LINE__ << std::endl; + } + } + auto Level = Poco::Logger::parseLevel(MicroService::instance().ConfigGetString("logging.level", "debug")); + Poco::Logger::root().setLevel(Level); + + if(!DisableWebSocketLogging) { + static const UI_WebSocketClientServer::NotificationTypeIdVec Notifications = { + {1, "log"}}; + + UI_WebSocketClientServer()->RegisterNotifications(Notifications); + } + } + } + + 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); + } + } + + void MicroService::DeleteOverrideConfiguration() { + Poco::File F(DataDir_ + ExtraConfigurationFilename); + + try { + if(F.exists()) + F.remove(); + } catch (...) { + + } + } + +} \ No newline at end of file diff --git a/src/framework/MicroService.h b/src/framework/MicroService.h index 1bba1e6..45b17bf 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,12 @@ namespace OpenWifi { // Logger_ = Poco::Logger::root().get("BASE-SVC"); } + inline static const char * ExtraConfigurationFilename = "/configuration_override.json"; + + 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 +102,67 @@ 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_; } + void DeleteOverrideConfiguration(); + + [[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 +170,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 +190,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/MicroServiceErrorHandler.h b/src/framework/MicroServiceErrorHandler.h index 05d75d1..3a78aba 100644 --- a/src/framework/MicroServiceErrorHandler.h +++ b/src/framework/MicroServiceErrorHandler.h @@ -10,6 +10,7 @@ #include "Poco/Net/NetException.h" #include "Poco/Net/SSLException.h" #include "Poco/JSON/Template.h" +#include "Poco/JSON/JSONException.h" #include "Poco/Thread.h" namespace OpenWifi { 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..590cc9c --- /dev/null +++ b/src/framework/MicroServiceFuncs.cpp @@ -0,0 +1,121 @@ +// +// 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::uint64_t MicroServiceRandom(std::uint64_t Range) { + return MicroService::instance().Random(Range); + } + + std::string MicroServiceSign(Poco::JWT::Token &T, const std::string &Algo) { + return MicroService::instance().Sign(T, Algo); + } + + std::string MicroServiceGetPublicAPIEndPoint() { + return MicroService::instance().GetPublicAPIEndPoint(); + } + + void MicroServiceDeleteOverrideConfiguration() { + return MicroService::instance().DeleteOverrideConfiguration(); + } +} diff --git a/src/framework/MicroServiceFuncs.h b/src/framework/MicroServiceFuncs.h new file mode 100644 index 0000000..b246b92 --- /dev/null +++ b/src/framework/MicroServiceFuncs.h @@ -0,0 +1,56 @@ +// +// 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::uint64_t MicroServiceRandom(std::uint64_t Range); + std::string MicroServiceSign(Poco::JWT::Token &T, const std::string &Algo); + std::string MicroServiceGetPublicAPIEndPoint(); + void MicroServiceDeleteOverrideConfiguration(); +} 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_SystemConfiguration.h b/src/framework/RESTAPI_SystemConfiguration.h new file mode 100644 index 0000000..c75be97 --- /dev/null +++ b/src/framework/RESTAPI_SystemConfiguration.h @@ -0,0 +1,52 @@ +// +// Created by stephane bourque on 2022-10-31. +// + +#pragma once + +#include "framework/RESTAPI_Handler.h" +#include "framework/MicroServiceFuncs.h" + +using namespace std::chrono_literals; + +namespace OpenWifi { + + class RESTAPI_system_configuration : public RESTAPIHandler { + public: + RESTAPI_system_configuration(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, + RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, + bool Internal) + : RESTAPIHandler(bindings, L, + std::vector{Poco::Net::HTTPRequest::HTTP_PUT, + Poco::Net::HTTPRequest::HTTP_GET, + Poco::Net::HTTPRequest::HTTP_DELETE, + Poco::Net::HTTPRequest::HTTP_OPTIONS}, + Server, TransactionId, Internal) {} + + static auto PathName() { return std::list{"/api/v1/systemConfiguration"}; } + + inline void DoPost() final {} + + inline void DoGet() final { + + return OK(); + } + + inline void DoPut() final{ + if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) { + return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); + } + + return OK(); + }; + + inline void DoDelete() final{ + if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) { + return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); + } + MicroServiceDeleteOverrideConfiguration(); + return OK(); + }; + }; + +} diff --git a/src/framework/RESTAPI_WebSocketServer.h b/src/framework/RESTAPI_WebSocketServer.h new file mode 100644 index 0000000..923de74 --- /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_WebSocketClientNotifications.cpp b/src/framework/UI_WebSocketClientNotifications.cpp new file mode 100644 index 0000000..c8fcfda --- /dev/null +++ b/src/framework/UI_WebSocketClientNotifications.cpp @@ -0,0 +1,11 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#include "framework/UI_WebSocketClientNotifications.h" +#include "framework/UI_WebSocketClientServer.h" + +namespace OpenWifi { + +} + diff --git a/src/framework/UI_WebSocketClientNotifications.h b/src/framework/UI_WebSocketClientNotifications.h new file mode 100644 index 0000000..d534a5c --- /dev/null +++ b/src/framework/UI_WebSocketClientNotifications.h @@ -0,0 +1,45 @@ +// +// Created by stephane bourque on 2022-05-05. +// + +#pragma once + +#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::uint64_t type_id=0; + 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_id", type_id); + 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, "type_id", type_id); + RESTAPI_utils::field_from_json(Obj, "content", content); + return true; + } catch (...) { + + } + return false; + } +} + diff --git a/src/framework/UI_WebSocketClientServer.cpp b/src/framework/UI_WebSocketClientServer.cpp new file mode 100644 index 0000000..10b89ec --- /dev/null +++ b/src/framework/UI_WebSocketClientServer.cpp @@ -0,0 +1,292 @@ +// +// 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" + +#define DBG { std::cout << __LINE__ << std::endl; } + +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); + auto ClientSocket = Client->WS_->impl()->sockfd(); + + Client->WS_->setNoDelay(true); + Client->WS_->setKeepAlive(true); + Client->WS_->setBlocking(false); + Reactor_.addEventHandler(*Client->WS_, + Poco::NObserver( + *this, &UI_WebSocketClientServer::OnSocketReadable)); + Reactor_.addEventHandler(*Client->WS_, + Poco::NObserver( + *this, &UI_WebSocketClientServer::OnSocketShutdown)); + Reactor_.addEventHandler(*Client->WS_, + Poco::NObserver( + *this, &UI_WebSocketClientServer::OnSocketError)); + Client->SocketRegistered_ = true; + Clients_[ClientSocket] = std::move(Client); + UsersConnected_ = Clients_.size(); + } + + void UI_WebSocketClientServer::SetProcessor( UI_WebSocketClientProcessor * F) { + Processor_ = F; + } + + UI_WebSocketClientServer::UI_WebSocketClientServer() noexcept: + SubSystemServer("WebSocketClientServer", "UI-WSCLNT-SVR", "websocketclients") + { + } + + void UI_WebSocketClientServer::EndConnection([[maybe_unused]] std::lock_guard &G, ClientList::iterator &Client) { + if(Client->second->SocketRegistered_) { + Client->second->SocketRegistered_ = false; + (*Client->second->WS_).shutdown(); + Reactor_.removeEventHandler(*Client->second->WS_, + Poco::NObserver(*this,&UI_WebSocketClientServer::OnSocketReadable)); + Reactor_.removeEventHandler(*Client->second->WS_, + Poco::NObserver(*this,&UI_WebSocketClientServer::OnSocketShutdown)); + Reactor_.removeEventHandler(*Client->second->WS_, + Poco::NObserver(*this,&UI_WebSocketClientServer::OnSocketError)); + } + Clients_.erase(Client); + UsersConnected_ = Clients_.size(); + std::cout << "How many clients: " << Clients_.size() << std::endl; + } + + void UI_WebSocketClientServer::run() { + Running_ = true ; + Utils::SetThreadName("ws:uiclnt-svr"); + while(Running_) { + Poco::Thread::trySleep(2000); + + if(!Running_) + break; + } + }; + + int UI_WebSocketClientServer::Start() { + poco_information(Logger(),"Starting..."); + GoogleApiKey_ = MicroServiceConfigGetString("google.apikey",""); + GeoCodeEnabled_ = !GoogleApiKey_.empty(); + ReactorThread_.start(Reactor_); + Thr_.start(*this); + return 0; + }; + + void UI_WebSocketClientServer::Stop() { + if(Running_) { + poco_information(Logger(),"Stopping..."); + Clients_.clear(); + Reactor_.stop(); + ReactorThread_.join(); + Running_ = false; + Thr_.wakeUp(); + Thr_.join(); + poco_information(Logger(),"Stopped..."); + } + }; + + bool UI_WebSocketClientServer::IsFiltered(std::uint64_t id, const OpenWifi::UI_WebSocketClientInfo &Client) { + return std::find(Client.Filter_.begin(), Client.Filter_.end(),id)!=end(Client.Filter_); + } + + bool UI_WebSocketClientServer::SendToUser(const std::string &UserName, std::uint64_t id, const std::string &Payload) { + std::lock_guard G(Mutex_); + + for(const auto &Client:Clients_) { + if(Client.second->UserName_ == UserName) { + try { + if(!IsFiltered(id,*Client.second) && Client.second->Authenticated_) { + return Client.second->WS_->sendFrame( + Payload.c_str(), (int)Payload.size()) == (int)Payload.size(); + } else { + return false; + } + } catch (...) { + return false; + } + } + } + return false; + } + + void UI_WebSocketClientServer::SendToAll(std::uint64_t id, const std::string &Payload) { + std::lock_guard G(Mutex_); + + for(const auto &Client:Clients_) { + try { + if(!IsFiltered(id,*Client.second) && Client.second->Authenticated_) + Client.second->WS_->sendFrame(Payload.c_str(),(int)Payload.size()); + } catch (...) { + + } + } + } + + UI_WebSocketClientServer::ClientList::iterator UI_WebSocketClientServer::FindWSClient( [[maybe_unused]] std::lock_guard &G, int ClientSocket) { + return Clients_.find(ClientSocket); + } + + void UI_WebSocketClientServer::SortNotifications() { + struct { + bool operator()(const NotificationEntry &A, const NotificationEntry & B) const { + return A.id < B.id; }; + } CompareNotifications; + std::sort(NotificationTypes_.begin(), NotificationTypes_.end(), CompareNotifications); + + NotificationTypesJSON_.clear(); + Poco::JSON::Array AllNotifications; + for(const auto ¬ification:NotificationTypes_) { + Poco::JSON::Object Notification; + Notification.set("id", notification.id); + Notification.set("helper", notification.helper); + AllNotifications.add(Notification); + } + NotificationTypesJSON_.set("notificationTypes", AllNotifications); + } + + void UI_WebSocketClientServer::RegisterNotifications(const OpenWifi::UI_WebSocketClientServer::NotificationTypeIdVec &Notifications) { + std::copy(Notifications.begin(), Notifications.end(), std::back_inserter(NotificationTypes_)); + SortNotifications(); + } + + void UI_WebSocketClientServer::OnSocketError([[maybe_unused]] const Poco::AutoPtr &pNf) { + std::lock_guard G(LocalMutex_); + auto Client = FindWSClient(G,pNf->socket().impl()->sockfd()); + if(Client==end(Clients_)) + return; + EndConnection(G,Client); + } + + void UI_WebSocketClientServer::OnSocketReadable([[maybe_unused]] const Poco::AutoPtr &pNf) { + + UI_WebSocketClientServer::ClientList::iterator Client; + + std::lock_guard G(LocalMutex_); + + try { + + Client = FindWSClient(G,pNf->socket().impl()->sockfd()); + if( Client == end(Clients_)) + return; + + Poco::Buffer IncomingFrame(0); + int flags; + int n; + n = Client->second->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.", Client->second->Id_, Client->second->UserName_)); + return EndConnection(G, Client); + } + + switch (Op) { + case Poco::Net::WebSocket::FRAME_OP_PING: { + Client->second->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.", Client->second->Id_, Client->second->UserName_)); + return EndConnection(G, Client); + } break; + case Poco::Net::WebSocket::FRAME_OP_TEXT: { + constexpr const char *DropMessagesCommand = "drop-notifications"; + IncomingFrame.append(0); + if (!Client->second->Authenticated_) { + std::string Frame{IncomingFrame.begin()}; + auto Tokens = Utils::Split(Frame, ':'); + bool Expired = false, Contacted = false; + if (Tokens.size() == 2 && + AuthClient()->IsAuthorized(Tokens[1], Client->second->UserInfo_, 0, Expired, Contacted)) { + Client->second->Authenticated_ = true; + Client->second->UserName_ = Client->second->UserInfo_.userinfo.email; + poco_debug(Logger(),fmt::format("START({}): {} UI Client is starting WS connection.", Client->second->Id_, Client->second->UserName_)); + auto WelcomeMessage = NotificationTypesJSON_; + WelcomeMessage.set("success", "Welcome! Bienvenue! Bienvenidos!"); + std::ostringstream OS; + WelcomeMessage.stringify(OS); + Client->second->WS_->sendFrame(OS.str().c_str(), (int) OS.str().size()); + Client->second->UserName_ = Client->second->UserInfo_.userinfo.email; + } else { + Poco::JSON::Object WelcomeMessage; + WelcomeMessage.set("error", "Invalid token. Closing connection."); + std::ostringstream OS; + WelcomeMessage.stringify(OS); + Client->second->WS_->sendFrame(OS.str().c_str(), (int) OS.str().size()); + Client->second->WS_->sendFrame(OS.str().c_str(), (int) OS.str().size()); + return EndConnection(G, Client); + } + } else { + Poco::JSON::Parser P; + auto Obj = + P.parse(IncomingFrame.begin()).extract(); + + if(Obj->has(DropMessagesCommand) && Obj->isArray(DropMessagesCommand)) { + auto Filters = Obj->getArray(DropMessagesCommand); + Client->second->Filter_.clear(); + for(const auto &Filter:*Filters) { + Client->second->Filter_.emplace_back( (std::uint64_t) Filter); + } + std::sort(begin(Client->second->Filter_),end(Client->second->Filter_)); + return; + } + + std::string Answer; + bool CloseConnection=false; + if (Processor_ != nullptr) { + Processor_->Processor(Obj, Answer, CloseConnection); + } + if (!Answer.empty()) + Client->second->WS_->sendFrame(Answer.c_str(), (int)Answer.size()); + else { + Client->second->WS_->sendFrame("{}", 2); + } + + if(CloseConnection) { + return EndConnection(G, Client); + } + } + } break; + default: { + } + } + } catch (...) { + return EndConnection(G, Client); + } + } + + void UI_WebSocketClientServer::OnSocketShutdown([[maybe_unused]] const Poco::AutoPtr &pNf) { + ClientList::iterator Client; + std::lock_guard G(LocalMutex_); + try { + Client = FindWSClient(G, pNf->socket().impl()->sockfd()); + if (Client == end(Clients_)) + return; + EndConnection(G, Client); + } catch (...) { + + } + } + +} // 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..95e4a33 --- /dev/null +++ b/src/framework/UI_WebSocketClientServer.h @@ -0,0 +1,131 @@ +// +// 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/UI_WebSocketClientNotifications.h" + +namespace OpenWifi { + + class UI_WebSocketClientProcessor { + public: + virtual void Processor(const Poco::JSON::Object::Ptr &O, std::string &Answer, bool &Done ) = 0; + private: + }; + + struct UI_WebSocketClientInfo { + std::unique_ptr WS_ = nullptr; + std::string Id_; + std::string UserName_; + bool Authenticated_ = false; + bool SocketRegistered_=false; + std::vector Filter_; + SecurityObjects::UserInfoAndPolicy UserInfo_; + + UI_WebSocketClientInfo(Poco::Net::WebSocket &WS, const std::string &Id, const std::string &username) { + WS_ = std::make_unique(WS); + Id_ = Id; + UserName_ = username; + } + }; + + class UI_WebSocketClientServer : public SubSystemServer, Poco::Runnable { + + public: + static auto instance() { + static auto instance_ = new UI_WebSocketClientServer; + return instance_; + } + + bool IsAnyoneConnected() { + return UsersConnected_; + } + + 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); + [[nodiscard]] inline bool GeoCodeEnabled() const { return GeoCodeEnabled_; } + [[nodiscard]] inline std::string GoogleApiKey() const { return GoogleApiKey_; } + + 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, Notification.type_id, 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(Notification.type_id, OO.str()); + } + + [[nodiscard]] bool SendToUser(const std::string &userName, std::uint64_t id, const std::string &Payload); + void SendToAll(std::uint64_t id, const std::string &Payload); + + struct NotificationEntry { + std::uint64_t id=0; + std::string helper; + }; + + using ClientList = std::map>; + using NotificationTypeIdVec = std::vector; + + void RegisterNotifications(const NotificationTypeIdVec & Notifications); + bool IsFiltered(std::uint64_t id, const UI_WebSocketClientInfo &Client); + + private: + mutable std::atomic_bool Running_ = false; + std::atomic_uint64_t UsersConnected_=0; + Poco::Thread Thr_; + Poco::Net::SocketReactor Reactor_; + Poco::Thread ReactorThread_; + std::recursive_mutex LocalMutex_; + bool GeoCodeEnabled_ = false; + std::string GoogleApiKey_; + ClientList Clients_; + UI_WebSocketClientProcessor *Processor_ = nullptr; + NotificationTypeIdVec NotificationTypes_; + Poco::JSON::Object NotificationTypesJSON_; + + UI_WebSocketClientServer() noexcept; + void EndConnection(std::lock_guard &G, ClientList::iterator & Client); + + void OnSocketReadable(const Poco::AutoPtr &pNf); + void OnSocketShutdown(const Poco::AutoPtr &pNf); + void OnSocketError(const Poco::AutoPtr &pNf); + + ClientList::iterator FindWSClient( std::lock_guard &G, int ClientSocket); + + void SortNotifications(); + }; + + inline auto UI_WebSocketClientServer() { return UI_WebSocketClientServer::instance(); } + +}; + diff --git a/src/framework/WebSocketClientNotifications.h b/src/framework/WebSocketClientNotifications.h deleted file mode 100644 index 435732c..0000000 --- a/src/framework/WebSocketClientNotifications.h +++ /dev/null @@ -1,250 +0,0 @@ -// -// Created by stephane bourque on 2022-05-05. -// - -#pragma once - -#include "framework/MicroService.h" - -namespace OpenWifi { - - struct WebNotificationSingleDevice { - std::string serialNumber; - inline void to_json(Poco::JSON::Object &Obj) const { - RESTAPI_utils::field_to_json(Obj,"serialNumber", serialNumber); - } - - inline bool from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - RESTAPI_utils::field_from_json(Obj,"serialNumber", serialNumber); - return true; - } catch (...) { - - } - return false; - } - }; - - struct WebNotificationSingleDeviceConfigurationChange { - std::string serialNumber; - uint64_t oldUUID; - uint64_t newUUID; - - inline void to_json(Poco::JSON::Object &Obj) const { - RESTAPI_utils::field_to_json(Obj,"serialNumber", serialNumber); - RESTAPI_utils::field_to_json(Obj,"oldUUID", oldUUID); - RESTAPI_utils::field_to_json(Obj,"newUUID", newUUID); - } - - inline bool from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - RESTAPI_utils::field_from_json(Obj,"serialNumber", serialNumber); - RESTAPI_utils::field_from_json(Obj,"oldUUID", oldUUID); - RESTAPI_utils::field_from_json(Obj,"newUUID", newUUID); - return true; - } catch (...) { - - } - return false; - } - }; - - struct WebNotificationSingleDeviceFirmwareChange { - std::string serialNumber; - std::string newFirmware; - inline void to_json(Poco::JSON::Object &Obj) const { - RESTAPI_utils::field_to_json(Obj,"serialNumber", serialNumber); - RESTAPI_utils::field_to_json(Obj,"newFirmware", newFirmware); - } - - inline bool from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - RESTAPI_utils::field_from_json(Obj,"serialNumber", serialNumber); - RESTAPI_utils::field_from_json(Obj,"newFirmware", newFirmware); - return true; - } catch (...) { - - } - return false; - } - }; - - 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); - } - - 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 WebSocketClientNotificationDeviceConnected(const std::string &SerialNumber) { - WebSocketNotification N; - N.content.serialNumber = SerialNumber; - N.type = "device_connection"; - WebSocketClientServer()->SendNotification(N); - } - - inline void WebSocketClientNotificationDeviceDisconnected(const std::string & SerialNumber) { - WebSocketNotification N; - N.content.serialNumber = SerialNumber; - N.type = "device_disconnection"; - WebSocketClientServer()->SendNotification(N); - } - - struct WebSocketNotificationJobContent { - std::string title, - details, - jobId; - std::vector success, - error, - warning; - uint64_t timeStamp=OpenWifi::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); - } - - ///// - ///// - ///// - - struct WebSocketNotificationRebootList { - std::string title, - details, - jobId; - std::vector success, - warning; - uint64_t timeStamp=OpenWifi::Now(); - - void to_json(Poco::JSON::Object &Obj) const; - bool from_json(const Poco::JSON::Object::Ptr &Obj); - }; - - 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); - } - - ///// - ///// - ///// - - struct WebSocketNotificationUpgradeList { - std::string title, - details, - jobId; - std::vector success, - skipped, - no_firmware, - not_connected; - uint64_t timeStamp=OpenWifi::Now(); - - void to_json(Poco::JSON::Object &Obj) const; - bool from_json(const Poco::JSON::Object::Ptr &Obj); - }; - - 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); - } - -} // namespace OpenWifi - diff --git a/src/framework/WebSocketLogger.h b/src/framework/WebSocketLogger.h new file mode 100644 index 0000000..7c0553e --- /dev/null +++ b/src/framework/WebSocketLogger.h @@ -0,0 +1,107 @@ +// +// Created by stephane bourque on 2022-10-25. +// + +#pragma once + +#include "framework/SubSystemServer.h" +#include "framework/UI_WebSocketClientServer.h" +#include "framework/UI_WebSocketClientNotifications.h" + + +namespace OpenWifi { + + class WebSocketLogger : public Poco::Channel { + public: + + WebSocketLogger() { + } + + ~WebSocketLogger() { + } + + std::string getProperty( [[maybe_unused]] const std::string &p ) const { + std::cout << "WS getProperty" << std::endl; + return ""; + } + + void close() final { + } + + void open() final { + } + + 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 "fatal"; + case Poco::Message::PRIO_NOTICE: return "notice"; + case Poco::Message::PRIO_TRACE: return "trace"; + case Poco::Message::PRIO_WARNING: return "warning"; + default: return "none"; + } + } + + struct NotificationLogMessage { + std::string msg; + std::string level; + std::uint64_t timestamp; + std::string source; + std::string thread_name; + std::uint64_t thread_id=0; + + inline void to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj,"msg", msg); + RESTAPI_utils::field_to_json(Obj,"level", level); + RESTAPI_utils::field_to_json(Obj,"timestamp", timestamp); + RESTAPI_utils::field_to_json(Obj,"source", source); + RESTAPI_utils::field_to_json(Obj,"thread_name", thread_name); + RESTAPI_utils::field_to_json(Obj,"thread_id", thread_id); + } + + inline bool from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json(Obj, "msg", msg); + RESTAPI_utils::field_from_json(Obj, "level", level); + RESTAPI_utils::field_from_json(Obj, "timestamp", timestamp); + RESTAPI_utils::field_from_json(Obj, "source", source); + RESTAPI_utils::field_from_json(Obj, "thread_name", thread_name); + RESTAPI_utils::field_from_json(Obj, "thread_id", thread_id); + return true; + } catch(...) { + + } + return false; + } + }; + + typedef WebSocketNotification WebSocketClientNotificationLogMessage_t; + + void log(const Poco::Message &m) final { + if(UI_WebSocketClientServer()->IsAnyoneConnected()) { + WebSocketClientNotificationLogMessage_t Msg; + Msg.content.msg = m.getText(); + Msg.content.level = WebSocketLogger::to_string(m.getPriority()); + Msg.content.timestamp = m.getTime().epochTime(); + Msg.content.source = m.getSource(); + Msg.content.thread_name = m.getThread(); + Msg.content.thread_id = m.getTid(); + Msg.type_id = 1; + UI_WebSocketClientServer()->SendNotification(Msg); + } + } + + void setProperty([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) { + std::cout << "WS setProperty" << std::endl; + } + + private: + std::recursive_mutex Mutex_; + }; + +// 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..cc6c5ae 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."}; @@ -198,6 +219,9 @@ namespace OpenWifi::RESTAPI::Errors { static const struct msg MaximumRTTYSessionsReached{1144,"Too many RTTY sessions currently active"}; static const struct msg DeviceIsAlreadyBusy{1145,"Device is already executing a command. Please try later."}; + + static const struct msg DeviceRequiresSignature{1146,"Device requires device signature to be provided."}; + } @@ -412,6 +436,8 @@ namespace OpenWifi::uCentralProtocol { static const char *CHANNELS = "channels"; static const char *PASSWORD = "password"; static const char *DEVICEUPDATE = "deviceupdate"; + static const char *FWSIGNATURE = "FWsignature"; + static const char *SIGNATURE = "signature"; static const char *SERIALNUMBER = "serialNumber"; static const char *COMPATIBLE = "compatible"; 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/storage/orm_actionLinks.cpp b/src/storage/orm_actionLinks.cpp index 444eab6..e1e170c 100644 --- a/src/storage/orm_actionLinks.cpp +++ b/src/storage/orm_actionLinks.cpp @@ -3,6 +3,7 @@ // #include "orm_actionLinks.h" +#include "framework/RESTAPI_utils.h" /* "Id varchar(36)," diff --git a/src/storage/orm_logins.cpp b/src/storage/orm_logins.cpp index 9ef4361..b58281c 100644 --- a/src/storage/orm_logins.cpp +++ b/src/storage/orm_logins.cpp @@ -3,6 +3,7 @@ // #include "orm_logins.h" +#include "framework/utils.h" namespace OpenWifi { diff --git a/src/storage/orm_preferences.cpp b/src/storage/orm_preferences.cpp index a9181b9..bba0b99 100644 --- a/src/storage/orm_preferences.cpp +++ b/src/storage/orm_preferences.cpp @@ -3,6 +3,7 @@ // #include "orm_preferences.h" +#include "framework/RESTAPI_utils.h" /* "Id varchar(36) UNIQUE PRIMARY KEY," diff --git a/src/storage/orm_tokens.cpp b/src/storage/orm_tokens.cpp index 314b803..23d878c 100644 --- a/src/storage/orm_tokens.cpp +++ b/src/storage/orm_tokens.cpp @@ -5,6 +5,7 @@ #include "orm_tokens.h" #include "AuthService.h" #include "StorageService.h" +#include "framework/RESTAPI_utils.h" /* "Token TEXT PRIMARY KEY, " diff --git a/src/storage/orm_tokens.h b/src/storage/orm_tokens.h index 7c2d3e9..68febf1 100644 --- a/src/storage/orm_tokens.h +++ b/src/storage/orm_tokens.h @@ -6,6 +6,7 @@ #include "framework/orm.h" #include "RESTObjects/RESTAPI_SecurityObjects.h" +#include "Poco/ExpireLRUCache.h" namespace OpenWifi { diff --git a/src/storage/orm_users.cpp b/src/storage/orm_users.cpp index 499e851..9124990 100644 --- a/src/storage/orm_users.cpp +++ b/src/storage/orm_users.cpp @@ -6,6 +6,8 @@ #include "AuthService.h" #include "RESTObjects/RESTAPI_CertObjects.h" #include "StorageService.h" +#include "framework/RESTAPI_utils.h" +#include "framework/MicroServiceFuncs.h" /* std::string, // Id = 0; @@ -117,7 +119,7 @@ namespace OpenWifi { Poco::Data::Session Sess = Pool_.get(); if(!PasswordHashedAlready) { - NewUser.id = MicroService::CreateUUID(); + NewUser.id = MicroServiceCreateUUID(); NewUser.creationDate = OpenWifi::Now(); } diff --git a/src/storage/orm_users.h b/src/storage/orm_users.h index 29dd7a1..8cbf019 100644 --- a/src/storage/orm_users.h +++ b/src/storage/orm_users.h @@ -5,6 +5,8 @@ #pragma once #include "framework/orm.h" +#include "Poco/ExpireLRUCache.h" + #include "RESTObjects/RESTAPI_SecurityObjects.h" namespace OpenWifi {