diff --git a/CMakeLists.txt b/CMakeLists.txt index e35f5f5..a9f4f86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,17 +53,12 @@ include_directories(/usr/local/include /usr/local/opt/openssl/include src inclu add_executable( owsec build src/Daemon.h src/Daemon.cpp - src/framework/MicroService.cpp src/framework/MicroService.h - src/framework/SubSystemServer.cpp src/framework/SubSystemServer.h + src/framework/MicroService.h src/RESTAPI/RESTAPI_oauth2Handler.h src/RESTAPI/RESTAPI_oauth2Handler.cpp - src/framework/RESTAPI_handler.h src/framework/RESTAPI_handler.cpp src/RESTAPI/RESTAPI_server.cpp src/RESTAPI/RESTAPI_server.h - src/RESTAPI/RESTAPI_SecurityObjects.cpp src/RESTAPI/RESTAPI_SecurityObjects.h - src/framework/RESTAPI_system_command.h src/framework/RESTAPI_system_command.cpp + src/RESTObjects/RESTAPI_SecurityObjects.cpp src/RESTObjects/RESTAPI_SecurityObjects.h src/AuthService.h src/AuthService.cpp - src/framework/KafkaManager.h src/framework/KafkaManager.cpp src/StorageService.cpp src/StorageService.h - src/framework/Utils.cpp src/framework/Utils.h src/SMTPMailerService.cpp src/SMTPMailerService.h src/RESTAPI/RESTAPI_users_handler.cpp src/RESTAPI/RESTAPI_users_handler.h src/RESTAPI/RESTAPI_user_handler.cpp src/RESTAPI/RESTAPI_user_handler.h @@ -75,15 +70,12 @@ add_executable( owsec src/RESTAPI/RESTAPI_avatarHandler.cpp src/RESTAPI/RESTAPI_avatarHandler.h src/storage/storage_avatar.cpp src/storage/storage_avatar.h src/storage/storage_users.h src/RESTAPI/RESTAPI_email_handler.cpp src/RESTAPI/RESTAPI_email_handler.h - src/framework/RESTAPI_GenericServer.h src/framework/RESTAPI_GenericServer.cpp - src/framework/storage_setup.cpp src/storage/storage_tables.cpp src/framework/RESTAPI_protocol.h src/framework/OpenWifiTypes.h src/storage/storage_users.cpp src/storage/storage_tokens.cpp src/framework/RESTAPI_errors.h - src/framework/Storage.h src/SMSSender.cpp src/SMSSender.h src/RESTAPI/RESTAPI_sms_handler.cpp src/RESTAPI/RESTAPI_sms_handler.h src/MFAServer.cpp src/MFAServer.h diff --git a/build b/build index 2b82dfe..b44fe09 100644 --- a/build +++ b/build @@ -1 +1 @@ -60 \ No newline at end of file +65 \ No newline at end of file diff --git a/src/AuthService.cpp b/src/AuthService.cpp index 2ad66a9..cc5e6f6 100644 --- a/src/AuthService.cpp +++ b/src/AuthService.cpp @@ -12,13 +12,10 @@ #include "Poco/JWT/Token.h" #include "Poco/JWT/Signer.h" -#include "Daemon.h" -#include "framework/RESTAPI_handler.h" +#include "framework/MicroService.h" #include "StorageService.h" #include "AuthService.h" -#include "framework/Utils.h" -#include "framework/KafkaManager.h" -#include "framework/Kafka_topics.h" +#include "framework/KafkaTopics.h" #include "SMTPMailerService.h" #include "MFAServer.h" @@ -46,16 +43,16 @@ namespace OpenWifi { } int AuthService::Start() { - Signer_.setRSAKey(Daemon()->Key()); + Signer_.setRSAKey(MicroService::instance().Key()); Signer_.addAllAlgorithms(); Logger_.notice("Starting..."); - Secure_ = Daemon()->ConfigGetBool("authentication.enabled",true); - DefaultPassword_ = Daemon()->ConfigGetString("authentication.default.password",""); - DefaultUserName_ = Daemon()->ConfigGetString("authentication.default.username",""); - Mechanism_ = Daemon()->ConfigGetString("authentication.service.type","internal"); - PasswordValidation_ = PasswordValidationStr_ = Daemon()->ConfigGetString("authentication.validation.expression","^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$"); - TokenAging_ = (uint64_t) Daemon()->ConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60); - HowManyOldPassword_ = Daemon()->ConfigGetInt("authentication.oldpasswords", 5); + Secure_ = MicroService::instance().ConfigGetBool("authentication.enabled",true); + DefaultPassword_ = MicroService::instance().ConfigGetString("authentication.default.password",""); + DefaultUserName_ = MicroService::instance().ConfigGetString("authentication.default.username",""); + Mechanism_ = MicroService::instance().ConfigGetString("authentication.service.type","internal"); + PasswordValidation_ = PasswordValidationStr_ = MicroService::instance().ConfigGetString("authentication.validation.expression","^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$"); + TokenAging_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60); + HowManyOldPassword_ = MicroService::instance().ConfigGetInt("authentication.oldpasswords", 5); return 0; } @@ -82,7 +79,7 @@ namespace OpenWifi { } if(!CallToken.empty()) { - if(Storage()->IsTokenRevoked(CallToken)) + if(StorageService()->IsTokenRevoked(CallToken)) return false; auto Client = UserCache_.find(CallToken); if( Client == UserCache_.end() ) @@ -94,7 +91,7 @@ namespace OpenWifi { return true; } UserCache_.erase(CallToken); - Storage()->RevokeToken(CallToken); + StorageService()->RevokeToken(CallToken); return false; } @@ -131,13 +128,13 @@ namespace OpenWifi { try { Poco::JSON::Object Obj; Obj.set("event", "remove-token"); - Obj.set("id", Daemon()->ID()); + Obj.set("id", MicroService::instance().ID()); Obj.set("token", token); std::stringstream ResultText; Poco::JSON::Stringifier::stringify(Obj, ResultText); std::string Tmp{token}; - Storage()->RevokeToken(Tmp); - KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, Daemon()->PrivateEndPoint(), ResultText.str(), + StorageService()->RevokeToken(Tmp); + KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroService::instance().PrivateEndPoint(), ResultText.str(), false); } catch (const Poco::Exception &E) { Logger_.log(E); @@ -171,8 +168,8 @@ namespace OpenWifi { try { auto E = UserCache_.find(SessionToken); if(E == UserCache_.end()) { - if(Storage()->GetToken(SessionToken,UInfo)) { - if(Storage()->GetUserById(UInfo.userinfo.email,UInfo.userinfo)) { + if(StorageService()->GetToken(SessionToken,UInfo)) { + if(StorageService()->GetUserById(UInfo.userinfo.email,UInfo.userinfo)) { UserCache_[UInfo.webtoken.access_token_] = UInfo; return true; } @@ -205,8 +202,8 @@ namespace OpenWifi { UInfo.webtoken.errorCode = 0; UInfo.webtoken.userMustChangePassword = false; UserCache_[UInfo.webtoken.access_token_] = UInfo; - Storage()->SetLastLogin(UInfo.userinfo.Id); - Storage()->AddToken(UInfo.webtoken.username_, UInfo.webtoken.access_token_, + StorageService()->SetLastLogin(UInfo.userinfo.Id); + StorageService()->AddToken(UInfo.webtoken.username_, UInfo.webtoken.access_token_, UInfo.webtoken.refresh_token_, UInfo.webtoken.token_type_, UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_); } @@ -235,7 +232,7 @@ namespace OpenWifi { Poco::toLowerInPlace(UserName); auto PasswordHash = ComputePasswordHash(UserName, Password); - if(Storage()->GetUserByEmail(UserName,UInfo.userinfo)) { + if(StorageService()->GetUserByEmail(UserName,UInfo.userinfo)) { if(UInfo.userinfo.waitingForEmailCheck) { return USERNAME_PENDING_VERIFICATION; } @@ -260,12 +257,12 @@ namespace OpenWifi { } UInfo.userinfo.lastPasswordChange = std::time(nullptr); UInfo.userinfo.changePassword = false; - Storage()->UpdateUserInfo(AUTHENTICATION_SYSTEM, UInfo.userinfo.Id,UInfo.userinfo); + StorageService()->UpdateUserInfo(AUTHENTICATION_SYSTEM, UInfo.userinfo.Id,UInfo.userinfo); } // so we have a good password, password up date has taken place if need be, now generate the token. UInfo.userinfo.lastLogin=std::time(nullptr); - Storage()->SetLastLogin(UInfo.userinfo.Id); + StorageService()->SetLastLogin(UInfo.userinfo.Id); CreateToken(UserName, UInfo ); return SUCCESS; } @@ -292,7 +289,7 @@ namespace OpenWifi { bool AuthService::SendEmailToUser(std::string &Email, EMAIL_REASON Reason) { SecurityObjects::UserInfo UInfo; - if(Storage()->GetUserByEmail(Email,UInfo)) { + if(StorageService()->GetUserByEmail(Email,UInfo)) { switch (Reason) { case FORGOT_PASSWORD: { MessageAttributes Attrs; @@ -301,7 +298,7 @@ namespace OpenWifi { Attrs[LOGO] = "logo.jpg"; Attrs[SUBJECT] = "Password reset link"; Attrs[ACTION_LINK] = - Daemon()->GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + UInfo.Id ; + MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + UInfo.Id ; SMTPMailerService()->SendMessage(UInfo.email, "password_reset.txt", Attrs); } break; @@ -313,7 +310,7 @@ namespace OpenWifi { Attrs[LOGO] = "logo.jpg"; Attrs[SUBJECT] = "EMail Address Verification"; Attrs[ACTION_LINK] = - Daemon()->GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; + MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); UInfo.waitingForEmailCheck = true; } @@ -333,7 +330,7 @@ namespace OpenWifi { Attrs[LOGO] = "logo.jpg"; Attrs[SUBJECT] = "EMail Address Verification"; Attrs[ACTION_LINK] = - Daemon()->GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; + MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); UInfo.waitingForEmailCheck = true; return true; diff --git a/src/AuthService.h b/src/AuthService.h index afb68f0..da3e11b 100644 --- a/src/AuthService.h +++ b/src/AuthService.h @@ -11,15 +11,14 @@ #include -#include "framework/SubSystemServer.h" - #include "Poco/JSON/Object.h" #include "Poco/Net/HTTPServerRequest.h" #include "Poco/Net/HTTPServerResponse.h" #include "Poco/JWT/Signer.h" #include "Poco/SHA2Engine.h" -#include "RESTAPI/RESTAPI_SecurityObjects.h" +#include "framework/MicroService.h" +#include "RESTObjects/RESTAPI_SecurityObjects.h" namespace OpenWifi{ @@ -106,6 +105,10 @@ namespace OpenWifi{ inline AuthService * AuthService() { return AuthService::instance(); } + [[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) { + return AuthService()->IsAuthorized(Request, SessionToken, UInfo ); + } + } // end of namespace #endif //UCENTRAL_UAUTHSERVICE_H diff --git a/src/Daemon.cpp b/src/Daemon.cpp index 170ef5a..6e2080d 100644 --- a/src/Daemon.cpp +++ b/src/Daemon.cpp @@ -26,8 +26,6 @@ #include #include -#include "framework/ALBHealthCheckServer.h" -#include "framework/KafkaManager.h" #include "StorageService.h" #include "RESTAPI/RESTAPI_server.h" #include "SMTPMailerService.h" @@ -45,8 +43,8 @@ namespace OpenWifi { vDAEMON_CONFIG_ENV_VAR, vDAEMON_APP_NAME, vDAEMON_BUS_TIMER, - Types::SubSystemVec{ - Storage(), + SubSystemVec{ + StorageService(), SMSSender(), RESTAPI_Server(), RESTAPI_InternalServer(), diff --git a/src/Daemon.h b/src/Daemon.h index 66dddb9..1274b4f 100644 --- a/src/Daemon.h +++ b/src/Daemon.h @@ -20,7 +20,6 @@ #include "Poco/Crypto/CipherFactory.h" #include "Poco/Crypto/Cipher.h" - #include "framework/OpenWifiTypes.h" #include "framework/MicroService.h" @@ -39,7 +38,7 @@ namespace OpenWifi { const std::string & ConfigEnv, const std::string & AppName, uint64_t BusTimer, - const Types::SubSystemVec & SubSystems) : + const SubSystemVec & SubSystems) : MicroService( PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems) {}; void initialize(Poco::Util::Application &self) override; diff --git a/src/MFAServer.cpp b/src/MFAServer.cpp index 6a34e00..877bc60 100644 --- a/src/MFAServer.cpp +++ b/src/MFAServer.cpp @@ -3,9 +3,9 @@ // #include "MFAServer.h" -#include "Daemon.h" #include "SMSSender.h" #include "SMTPMailerService.h" +#include "framework/MicroService.h" namespace OpenWifi { @@ -27,7 +27,7 @@ namespace OpenWifi { return false; std::string Challenge = MakeChallenge(); - std::string uuid = Daemon()->CreateUUID(); + std::string uuid = MicroService::instance().CreateUUID(); uint64_t Created = std::time(nullptr); ChallengeStart.set("uuid",uuid); diff --git a/src/MFAServer.h b/src/MFAServer.h index 156efc5..ba5d094 100644 --- a/src/MFAServer.h +++ b/src/MFAServer.h @@ -5,9 +5,9 @@ #ifndef OWSEC_MFASERVER_H #define OWSEC_MFASERVER_H -#include "framework/SubSystemServer.h" +#include "framework/MicroService.h" #include "Poco/JSON/Object.h" -#include "RESTAPI/RESTAPI_SecurityObjects.h" +#include "RESTObjects/RESTAPI_SecurityObjects.h" namespace OpenWifi { struct MFACacheEntry { diff --git a/src/RESTAPI/RESTAPI_AssetServer.cpp b/src/RESTAPI/RESTAPI_AssetServer.cpp index 10abd7e..56e985b 100644 --- a/src/RESTAPI/RESTAPI_AssetServer.cpp +++ b/src/RESTAPI/RESTAPI_AssetServer.cpp @@ -4,10 +4,9 @@ #include "RESTAPI_AssetServer.h" #include "Poco/File.h" -#include "../Daemon.h" +#include "framework/RESTAPI_protocol.h" +#include "framework/MicroService.h" #include "RESTAPI_server.h" -#include "../framework/Utils.h" -#include "../framework/RESTAPI_protocol.h" namespace OpenWifi { void RESTAPI_AssetServer::DoGet() { diff --git a/src/RESTAPI/RESTAPI_AssetServer.h b/src/RESTAPI/RESTAPI_AssetServer.h index 11e9743..87bdce4 100644 --- a/src/RESTAPI/RESTAPI_AssetServer.h +++ b/src/RESTAPI/RESTAPI_AssetServer.h @@ -5,7 +5,7 @@ #ifndef UCENTRALSEC_RESTAPI_ASSETSERVER_H #define UCENTRALSEC_RESTAPI_ASSETSERVER_H -#include "../framework/RESTAPI_handler.h" +#include "../framework/MicroService.h" namespace OpenWifi { class RESTAPI_AssetServer : public RESTAPIHandler { diff --git a/src/RESTAPI/RESTAPI_InternalServer.cpp b/src/RESTAPI/RESTAPI_InternalServer.cpp index 10e0522..c05f69a 100644 --- a/src/RESTAPI/RESTAPI_InternalServer.cpp +++ b/src/RESTAPI/RESTAPI_InternalServer.cpp @@ -5,7 +5,6 @@ #include "Poco/URI.h" -#include "../framework/RESTAPI_system_command.h" #include "RESTAPI_user_handler.h" #include "RESTAPI_users_handler.h" #include "RESTAPI_action_links.h" @@ -13,7 +12,7 @@ #include "RESTAPI_InternalServer.h" #include "RESTAPI_sms_handler.h" -#include "../framework/Utils.h" +#include "framework/MicroService.h" namespace OpenWifi { @@ -59,7 +58,7 @@ namespace OpenWifi { } void RESTAPI_InternalServer::reinitialize(Poco::Util::Application &self) { - Daemon()->LoadConfigurationFile(); + MicroService::instance().LoadConfigurationFile(); Logger_.information("Reinitializing."); Stop(); Start(); diff --git a/src/RESTAPI/RESTAPI_InternalServer.h b/src/RESTAPI/RESTAPI_InternalServer.h index dfa2e83..c7e5a2e 100644 --- a/src/RESTAPI/RESTAPI_InternalServer.h +++ b/src/RESTAPI/RESTAPI_InternalServer.h @@ -5,13 +5,13 @@ #ifndef UCENTRALSEC_RESTAPI_INTERNALSERVER_H #define UCENTRALSEC_RESTAPI_INTERNALSERVER_H -#include "../framework/SubSystemServer.h" #include "Poco/Net/HTTPServer.h" #include "Poco/Net/HTTPRequestHandler.h" #include "Poco/Net/HTTPRequestHandlerFactory.h" #include "Poco/Net/HTTPServerRequest.h" #include "Poco/Net/NetException.h" -#include "../framework/RESTAPI_GenericServer.h" + +#include "framework/MicroService.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_action_links.cpp b/src/RESTAPI/RESTAPI_action_links.cpp index 9d7c7da..432119a 100644 --- a/src/RESTAPI/RESTAPI_action_links.cpp +++ b/src/RESTAPI/RESTAPI_action_links.cpp @@ -3,14 +3,13 @@ // #include "RESTAPI_action_links.h" -#include "../StorageService.h" -#include "../framework/Utils.h" -#include "../framework/RESTAPI_utils.h" +#include "StorageService.h" #include "Poco/JSON/Parser.h" #include "Poco/Net/HTMLForm.h" #include "RESTAPI_server.h" -#include "../Daemon.h" + +#include "framework/MicroService.h" namespace OpenWifi { void RESTAPI_action_links::DoGet() { @@ -64,7 +63,7 @@ namespace OpenWifi { } SecurityObjects::UserInfo UInfo; - if(!Storage()->GetUserById(Id,UInfo)) { + if(!StorageService()->GetUserById(Id,UInfo)) { Poco::File FormFile{ RESTAPI_Server()->AssetDir() + "/password_reset_error.html"}; Types::StringPairVec FormVars{ {"UUID", Id}, {"ERROR_TEXT", "This request does not contain a valid user ID. Please contact your system administrator."}}; @@ -84,11 +83,11 @@ namespace OpenWifi { {"ERROR_TEXT", "You cannot reuse one of your recent passwords."}}; return SendHTMLFileBack(FormFile,FormVars); } - Storage()->UpdateUserInfo(UInfo.email,Id,UInfo); + StorageService()->UpdateUserInfo(UInfo.email,Id,UInfo); Poco::File FormFile{ RESTAPI_Server()->AssetDir() + "/password_reset_success.html"}; Types::StringPairVec FormVars{ {"UUID", Id}, {"USERNAME", UInfo.email}, - {"ACTION_LINK",Daemon()->GetUIURI()}}; + {"ACTION_LINK",MicroService::instance().GetUIURI()}}; SendHTMLFileBack(FormFile,FormVars); } else { DoReturnA404(); @@ -99,7 +98,7 @@ namespace OpenWifi { SecurityObjects::UserInfo UInfo; Logger_.information(Poco::format("EMAIL-VERIFICATION(%s): For ID=%s", Request->clientAddress().toString(), Id)); - if (!Storage()->GetUserById(Id, UInfo)) { + if (!StorageService()->GetUserById(Id, UInfo)) { Types::StringPairVec FormVars{{"UUID", Id}, {"ERROR_TEXT", "This does not appear to be a valid email verification link.."}}; Poco::File FormFile{RESTAPI_Server()->AssetDir() + "/email_verification_error.html"}; @@ -110,10 +109,10 @@ namespace OpenWifi { UInfo.validated = true; UInfo.lastEmailCheck = std::time(nullptr); UInfo.validationDate = std::time(nullptr); - Storage()->UpdateUserInfo(UInfo.email, Id, UInfo); + StorageService()->UpdateUserInfo(UInfo.email, Id, UInfo); Types::StringPairVec FormVars{{"UUID", Id}, {"USERNAME", UInfo.email}, - {"ACTION_LINK",Daemon()->GetUIURI()}}; + {"ACTION_LINK",MicroService::instance().GetUIURI()}}; Poco::File FormFile{RESTAPI_Server()->AssetDir() + "/email_verification_success.html"}; SendHTMLFileBack(FormFile, FormVars); } diff --git a/src/RESTAPI/RESTAPI_action_links.h b/src/RESTAPI/RESTAPI_action_links.h index 9848170..fe7138f 100644 --- a/src/RESTAPI/RESTAPI_action_links.h +++ b/src/RESTAPI/RESTAPI_action_links.h @@ -6,14 +6,7 @@ #define UCENTRALSEC_RESTAPI_ACTION_LINKS_H -#include "../framework/RESTAPI_handler.h" -#include "Poco/Net/PartHandler.h" -#include "Poco/Message.h" -#include "Poco/Net/MessageHeader.h" -#include "Poco/Net/NameValueCollection.h" -#include "Poco/NullStream.h" -#include "Poco/StreamCopier.h" -#include "Poco/CountingStream.h" +#include "framework/MicroService.h" namespace OpenWifi { class RESTAPI_action_links : public RESTAPIHandler { diff --git a/src/RESTAPI/RESTAPI_avatarHandler.cpp b/src/RESTAPI/RESTAPI_avatarHandler.cpp index 728d6d5..e66112d 100644 --- a/src/RESTAPI/RESTAPI_avatarHandler.cpp +++ b/src/RESTAPI/RESTAPI_avatarHandler.cpp @@ -6,11 +6,10 @@ #include #include "RESTAPI_avatarHandler.h" -#include "../StorageService.h" -#include "../Daemon.h" +#include "StorageService.h" #include "Poco/Net/HTMLForm.h" -#include "../framework/Utils.h" -#include "../framework/RESTAPI_protocol.h" +#include "framework/RESTAPI_protocol.h" +#include "framework/MicroService.h" namespace OpenWifi { @@ -32,23 +31,23 @@ namespace OpenWifi { std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); SecurityObjects::UserInfo UInfo; - if (Id.empty() || !Storage()->GetUserById(Id, UInfo)) { + if (Id.empty() || !StorageService()->GetUserById(Id, UInfo)) { return NotFound(); } // if there is an avatar, just remove it... - Storage()->DeleteAvatar(UserInfo_.userinfo.email,Id); + StorageService()->DeleteAvatar(UserInfo_.userinfo.email,Id); Poco::TemporaryFile TmpFile; AvatarPartHandler partHandler(Id, Logger_, TmpFile); Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler); Poco::JSON::Object Answer; - if (!partHandler.Name().empty() && partHandler.Length()ConfigGetInt("openwifi.avatar.maxsize",2000000)) { + if (!partHandler.Name().empty() && partHandler.Length()< MicroService::instance().ConfigGetInt("openwifi.avatar.maxsize",2000000)) { Answer.set(RESTAPI::Protocol::AVATARID, Id); Answer.set(RESTAPI::Protocol::ERRORCODE, 0); Logger_.information(Poco::format("Uploaded avatar: %s Type: %s", partHandler.Name(), partHandler.ContentType())); - Storage()->SetAvatar(UserInfo_.userinfo.email, + StorageService()->SetAvatar(UserInfo_.userinfo.email, Id, TmpFile, partHandler.ContentType(), partHandler.Name()); } else { Answer.set(RESTAPI::Protocol::AVATARID, Id); @@ -65,7 +64,7 @@ namespace OpenWifi { } Poco::TemporaryFile TempAvatar; std::string Type, Name; - if (!Storage()->GetAvatar(UserInfo_.userinfo.email, Id, TempAvatar, Type, Name)) { + if (!StorageService()->GetAvatar(UserInfo_.userinfo.email, Id, TempAvatar, Type, Name)) { return NotFound(); } SendFile(TempAvatar, Type, Name); @@ -76,7 +75,7 @@ namespace OpenWifi { if (Id.empty()) { return NotFound(); } - if (!Storage()->DeleteAvatar(UserInfo_.userinfo.email, Id)) { + if (!StorageService()->DeleteAvatar(UserInfo_.userinfo.email, Id)) { return NotFound(); } OK(); diff --git a/src/RESTAPI/RESTAPI_avatarHandler.h b/src/RESTAPI/RESTAPI_avatarHandler.h index 0b34456..18d9c67 100644 --- a/src/RESTAPI/RESTAPI_avatarHandler.h +++ b/src/RESTAPI/RESTAPI_avatarHandler.h @@ -6,7 +6,7 @@ #define UCENTRALSEC_RESTAPI_AVATARHANDLER_H -#include "framework/RESTAPI_handler.h" +#include "framework/MicroService.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_email_handler.cpp b/src/RESTAPI/RESTAPI_email_handler.cpp index 72bb8a7..f21c7eb 100644 --- a/src/RESTAPI/RESTAPI_email_handler.cpp +++ b/src/RESTAPI/RESTAPI_email_handler.cpp @@ -8,9 +8,9 @@ #include "Poco/Exception.h" #include "Poco/JSON/Parser.h" -#include "../Daemon.h" -#include "../SMTPMailerService.h" -#include "../framework/RESTAPI_errors.h" +#include "SMTPMailerService.h" +#include "framework/RESTAPI_errors.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 762fb86..8ec3139 100644 --- a/src/RESTAPI/RESTAPI_email_handler.h +++ b/src/RESTAPI/RESTAPI_email_handler.h @@ -6,7 +6,7 @@ #define OWSEC_RESTAPI_EMAIL_HANDLER_H -#include "framework/RESTAPI_handler.h" +#include "framework/MicroService.h" namespace OpenWifi { class RESTAPI_email_handler : public RESTAPIHandler { diff --git a/src/RESTAPI/RESTAPI_oauth2Handler.cpp b/src/RESTAPI/RESTAPI_oauth2Handler.cpp index 7c2e76a..ce62841 100644 --- a/src/RESTAPI/RESTAPI_oauth2Handler.cpp +++ b/src/RESTAPI/RESTAPI_oauth2Handler.cpp @@ -8,13 +8,12 @@ #include "Poco/JSON/Parser.h" -#include "../AuthService.h" +#include "AuthService.h" #include "RESTAPI_oauth2Handler.h" -#include "../framework/RESTAPI_protocol.h" +#include "framework/RESTAPI_protocol.h" #include "RESTAPI_server.h" -#include "../MFAServer.h" - -#include "../framework/Utils.h" +#include "MFAServer.h" +#include "framework/MicroService.h" namespace OpenWifi { void RESTAPI_oauth2Handler::DoGet() { diff --git a/src/RESTAPI/RESTAPI_oauth2Handler.h b/src/RESTAPI/RESTAPI_oauth2Handler.h index 8b900fa..38e694c 100644 --- a/src/RESTAPI/RESTAPI_oauth2Handler.h +++ b/src/RESTAPI/RESTAPI_oauth2Handler.h @@ -9,7 +9,7 @@ #ifndef UCENTRAL_RESTAPI_OAUTH2HANDLER_H #define UCENTRAL_RESTAPI_OAUTH2HANDLER_H -#include "../framework/RESTAPI_handler.h" +#include "framework/MicroService.h" namespace OpenWifi { class RESTAPI_oauth2Handler : public RESTAPIHandler { diff --git a/src/RESTAPI/RESTAPI_server.cpp b/src/RESTAPI/RESTAPI_server.cpp index d0f9955..3c36b6d 100644 --- a/src/RESTAPI/RESTAPI_server.cpp +++ b/src/RESTAPI/RESTAPI_server.cpp @@ -12,7 +12,6 @@ #include "RESTAPI_server.h" #include "RESTAPI_oauth2Handler.h" -#include "../framework/RESTAPI_system_command.h" #include "RESTAPI_user_handler.h" #include "RESTAPI_users_handler.h" #include "RESTAPI_action_links.h" @@ -22,8 +21,7 @@ #include "RESTAPI_email_handler.h" #include "RESTAPI_sms_handler.h" -#include "../Daemon.h" -#include "../framework/Utils.h" +#include "framework/MicroService.h" namespace OpenWifi { @@ -33,9 +31,9 @@ namespace OpenWifi { Logger_.information("Starting."); Server_.InitLogging(); - AsserDir_ = Daemon()->ConfigPath("openwifi.restapi.wwwassets"); - AccessPolicy_ = Daemon()->ConfigGetString("openwifi.document.policy.access", "/wwwassets/access_policy.html"); - PasswordPolicy_ = Daemon()->ConfigGetString("openwifi.document.policy.password", "/wwwassets/password_policy.html"); + AsserDir_ = MicroService::instance().ConfigPath("openwifi.restapi.wwwassets"); + AccessPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.access", "/wwwassets/access_policy.html"); + PasswordPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.password", "/wwwassets/password_policy.html"); for(const auto & Svr: ConfigServersList_) { Logger_.information(Poco::format("Starting: %s:%s Keyfile:%s CertFile: %s", Svr.Address(), std::to_string(Svr.Port()), @@ -85,7 +83,7 @@ namespace OpenWifi { } void RESTAPI_Server::reinitialize(Poco::Util::Application &self) { - Daemon()->LoadConfigurationFile(); + MicroService::instance().LoadConfigurationFile(); Logger_.information("Reinitializing."); Stop(); Start(); diff --git a/src/RESTAPI/RESTAPI_server.h b/src/RESTAPI/RESTAPI_server.h index 9e08a68..696c8d9 100644 --- a/src/RESTAPI/RESTAPI_server.h +++ b/src/RESTAPI/RESTAPI_server.h @@ -9,13 +9,13 @@ #ifndef UCENTRAL_UCENTRALRESTAPISERVER_H #define UCENTRAL_UCENTRALRESTAPISERVER_H -#include "../framework/SubSystemServer.h" #include "Poco/Net/HTTPServer.h" #include "Poco/Net/HTTPRequestHandler.h" #include "Poco/Net/HTTPRequestHandlerFactory.h" #include "Poco/Net/HTTPServerRequest.h" #include "Poco/Net/NetException.h" -#include "../framework/RESTAPI_GenericServer.h" + +#include "framework/MicroService.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_sms_handler.cpp b/src/RESTAPI/RESTAPI_sms_handler.cpp index f0acdf1..25bc0b5 100644 --- a/src/RESTAPI/RESTAPI_sms_handler.cpp +++ b/src/RESTAPI/RESTAPI_sms_handler.cpp @@ -3,9 +3,9 @@ // #include "RESTAPI_sms_handler.h" -#include "../SMSSender.h" -#include "../framework/Utils.h" -#include "../framework/RESTAPI_errors.h" +#include "SMSSender.h" +#include "framework/RESTAPI_errors.h" +#include "framework/MicroService.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_sms_handler.h b/src/RESTAPI/RESTAPI_sms_handler.h index b6509cc..163c21a 100644 --- a/src/RESTAPI/RESTAPI_sms_handler.h +++ b/src/RESTAPI/RESTAPI_sms_handler.h @@ -6,7 +6,7 @@ #define OWSEC_RESTAPI_SMS_HANDLER_H -#include "../framework/RESTAPI_handler.h" +#include "framework/MicroService.h" namespace OpenWifi { class RESTAPI_sms_handler : public RESTAPIHandler { diff --git a/src/RESTAPI/RESTAPI_systemEndpoints_handler.cpp b/src/RESTAPI/RESTAPI_systemEndpoints_handler.cpp index bcd19f7..6bf0e6a 100644 --- a/src/RESTAPI/RESTAPI_systemEndpoints_handler.cpp +++ b/src/RESTAPI/RESTAPI_systemEndpoints_handler.cpp @@ -4,7 +4,7 @@ #include "RESTAPI_systemEndpoints_handler.h" #include "../Daemon.h" -#include "RESTAPI_SecurityObjects.h" +#include "RESTObjects/RESTAPI_SecurityObjects.h" namespace OpenWifi { diff --git a/src/RESTAPI/RESTAPI_systemEndpoints_handler.h b/src/RESTAPI/RESTAPI_systemEndpoints_handler.h index f8e6bf0..24339dd 100644 --- a/src/RESTAPI/RESTAPI_systemEndpoints_handler.h +++ b/src/RESTAPI/RESTAPI_systemEndpoints_handler.h @@ -5,7 +5,7 @@ #ifndef UCENTRALSEC_RESTAPI_SYSTEMENDPOINTS_HANDLER_H #define UCENTRALSEC_RESTAPI_SYSTEMENDPOINTS_HANDLER_H -#include "../framework/RESTAPI_handler.h" +#include "../framework/MicroService.h" namespace OpenWifi { class RESTAPI_systemEndpoints_handler : public RESTAPIHandler { public: diff --git a/src/RESTAPI/RESTAPI_user_handler.cpp b/src/RESTAPI/RESTAPI_user_handler.cpp index 4396a88..e912037 100644 --- a/src/RESTAPI/RESTAPI_user_handler.cpp +++ b/src/RESTAPI/RESTAPI_user_handler.cpp @@ -3,12 +3,10 @@ // #include "RESTAPI_user_handler.h" -#include "../StorageService.h" +#include "StorageService.h" #include "Poco/JSON/Parser.h" -#include "../framework/Utils.h" -#include "../framework/RESTAPI_utils.h" -#include "../framework/RESTAPI_errors.h" -#include "../SMSSender.h" +#include "framework/RESTAPI_errors.h" +#include "SMSSender.h" namespace OpenWifi { void RESTAPI_user_handler::DoGet() { @@ -21,10 +19,10 @@ namespace OpenWifi { std::string Arg; SecurityObjects::UserInfo UInfo; if(HasParameter("byEmail",Arg) && Arg=="true") { - if(!Storage()->GetUserByEmail(Id,UInfo)) { + if(!StorageService()->GetUserByEmail(Id,UInfo)) { return NotFound(); } - } else if(!Storage()->GetUserById(Id,UInfo)) { + } else if(!StorageService()->GetUserById(Id,UInfo)) { return NotFound(); } Poco::JSON::Object UserInfoObject; @@ -39,18 +37,18 @@ namespace OpenWifi { } SecurityObjects::UserInfo UInfo; - if(!Storage()->GetUserById(Id,UInfo)) { + if(!StorageService()->GetUserById(Id,UInfo)) { return NotFound(); } - if(!Storage()->DeleteUser(UserInfo_.userinfo.email,Id)) { + if(!StorageService()->DeleteUser(UserInfo_.userinfo.email,Id)) { return NotFound(); } if(AuthService()->DeleteUserFromCache(UInfo.email)) ; Logger_.information(Poco::format("Remove all tokens for '%s'", UserInfo_.userinfo.email)); - Storage()->RevokeAllTokens(UInfo.email); + StorageService()->RevokeAllTokens(UInfo.email); Logger_.information(Poco::format("User '%s' deleted by '%s'.",Id,UserInfo_.userinfo.email)); OK(); } @@ -82,7 +80,7 @@ namespace OpenWifi { if(UInfo.name.empty()) UInfo.name = UInfo.email; - if(!Storage()->CreateUser(UInfo.email,UInfo)) { + if(!StorageService()->CreateUser(UInfo.email,UInfo)) { Logger_.information(Poco::format("Could not add user '%s'.",UInfo.email)); return BadRequest(RESTAPI::Errors::RecordNotCreated); } @@ -90,10 +88,10 @@ namespace OpenWifi { if(GetParameter("email_verification","false")=="true") { if(AuthService::VerifyEmail(UInfo)) Logger_.information(Poco::format("Verification e-mail requested for %s",UInfo.email)); - Storage()->UpdateUserInfo(UserInfo_.userinfo.email,UInfo.Id,UInfo); + StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,UInfo.Id,UInfo); } - if(!Storage()->GetUserByEmail(UInfo.email, UInfo)) { + if(!StorageService()->GetUserByEmail(UInfo.email, UInfo)) { Logger_.information(Poco::format("User '%s' but not retrieved.",UInfo.email)); return NotFound(); } @@ -113,7 +111,7 @@ namespace OpenWifi { } SecurityObjects::UserInfo Existing; - if(!Storage()->GetUserById(Id,Existing)) { + if(!StorageService()->GetUserById(Id,Existing)) { return NotFound(); } @@ -186,9 +184,9 @@ namespace OpenWifi { } } - if(Storage()->UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) { + if(StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) { SecurityObjects::UserInfo NewUserInfo; - Storage()->GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo); + StorageService()->GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo); Poco::JSON::Object ModifiedObject; NewUserInfo.to_json(ModifiedObject); return ReturnObject(ModifiedObject); diff --git a/src/RESTAPI/RESTAPI_user_handler.h b/src/RESTAPI/RESTAPI_user_handler.h index 92eaf35..517d5c5 100644 --- a/src/RESTAPI/RESTAPI_user_handler.h +++ b/src/RESTAPI/RESTAPI_user_handler.h @@ -5,7 +5,7 @@ #ifndef UCENTRALSEC_RESTAPI_USER_HANDLER_H #define UCENTRALSEC_RESTAPI_USER_HANDLER_H -#include "framework/RESTAPI_handler.h" +#include "framework/MicroService.h" namespace OpenWifi { class RESTAPI_user_handler : public RESTAPIHandler { diff --git a/src/RESTAPI/RESTAPI_users_handler.cpp b/src/RESTAPI/RESTAPI_users_handler.cpp index 3b7ece6..9bfe636 100644 --- a/src/RESTAPI/RESTAPI_users_handler.cpp +++ b/src/RESTAPI/RESTAPI_users_handler.cpp @@ -3,9 +3,9 @@ // #include "RESTAPI_users_handler.h" -#include "../StorageService.h" -#include "../framework/RESTAPI_protocol.h" -#include "../framework/Utils.h" +#include "StorageService.h" +#include "framework/RESTAPI_protocol.h" +#include "framework/MicroService.h" namespace OpenWifi { void RESTAPI_users_handler::DoGet() { @@ -15,7 +15,7 @@ namespace OpenWifi { if(QB_.Select.empty()) { Poco::JSON::Array ArrayObj; Poco::JSON::Object Answer; - if (Storage()->GetUsers(QB_.Offset, QB_.Limit, Users)) { + if (StorageService()->GetUsers(QB_.Offset, QB_.Limit, Users)) { for (const auto &i : Users) { Poco::JSON::Object Obj; if (IdOnly) { @@ -33,7 +33,7 @@ namespace OpenWifi { Poco::JSON::Array ArrayObj; for(auto &i:IDs) { SecurityObjects::UserInfo UInfo; - if(Storage()->GetUserById(i,UInfo)) { + if(StorageService()->GetUserById(i,UInfo)) { Poco::JSON::Object Obj; if (IdOnly) { ArrayObj.add(UInfo.Id); diff --git a/src/RESTAPI/RESTAPI_users_handler.h b/src/RESTAPI/RESTAPI_users_handler.h index 7e88c1b..201c39b 100644 --- a/src/RESTAPI/RESTAPI_users_handler.h +++ b/src/RESTAPI/RESTAPI_users_handler.h @@ -5,7 +5,7 @@ #ifndef UCENTRALSEC_RESTAPI_USERS_HANDLER_H #define UCENTRALSEC_RESTAPI_USERS_HANDLER_H -#include "framework/RESTAPI_handler.h" +#include "framework/MicroService.h" namespace OpenWifi { class RESTAPI_users_handler : public RESTAPIHandler { diff --git a/src/RESTAPI/RESTAPI_validateToken_handler.cpp b/src/RESTAPI/RESTAPI_validateToken_handler.cpp index 9e76bec..8f38cf6 100644 --- a/src/RESTAPI/RESTAPI_validateToken_handler.cpp +++ b/src/RESTAPI/RESTAPI_validateToken_handler.cpp @@ -3,9 +3,7 @@ // #include "RESTAPI_validateToken_handler.h" -#include "../Daemon.h" -#include "../AuthService.h" -#include "../framework/Utils.h" +#include "AuthService.h" namespace OpenWifi { void RESTAPI_validateToken_handler::DoGet() { diff --git a/src/RESTAPI/RESTAPI_validateToken_handler.h b/src/RESTAPI/RESTAPI_validateToken_handler.h index eca26a7..6475d8e 100644 --- a/src/RESTAPI/RESTAPI_validateToken_handler.h +++ b/src/RESTAPI/RESTAPI_validateToken_handler.h @@ -5,7 +5,7 @@ #ifndef UCENTRALSEC_RESTAPI_VALIDATETOKEN_HANDLER_H #define UCENTRALSEC_RESTAPI_VALIDATETOKEN_HANDLER_H -#include "../framework/RESTAPI_handler.h" +#include "framework/MicroService.h" namespace OpenWifi { class RESTAPI_validateToken_handler : public RESTAPIHandler { diff --git a/src/RESTObjects/RESTAPI_FMSObjects.cpp b/src/RESTObjects/RESTAPI_FMSObjects.cpp new file mode 100644 index 0000000..78d0062 --- /dev/null +++ b/src/RESTObjects/RESTAPI_FMSObjects.cpp @@ -0,0 +1,248 @@ +// +// Created by stephane bourque on 2021-07-12. +// + +#include "RESTAPI_FMSObjects.h" +#include "framework/MicroService.h" + +using OpenWifi::RESTAPI_utils::field_to_json; +using OpenWifi::RESTAPI_utils::field_from_json; + +namespace OpenWifi::FMSObjects { + + void Firmware::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "id", id); + field_to_json(Obj, "release", release); + field_to_json(Obj, "deviceType", deviceType); + field_to_json(Obj, "description", description); + field_to_json(Obj, "revision", revision); + field_to_json(Obj, "uri", uri); + field_to_json(Obj, "image", image); + field_to_json(Obj, "imageDate", imageDate); + field_to_json(Obj, "size", size); + field_to_json(Obj, "downloadCount", downloadCount); + field_to_json(Obj, "firmwareHash", firmwareHash); + field_to_json(Obj, "owner", owner); + field_to_json(Obj, "location", location); + field_to_json(Obj, "uploader", uploader); + field_to_json(Obj, "digest", digest); + field_to_json(Obj, "latest", latest); + field_to_json(Obj, "notes", notes); + field_to_json(Obj, "created", created); + }; + + bool Firmware::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "id", id); + field_from_json(Obj, "release", release); + field_from_json(Obj, "deviceType", deviceType); + field_from_json(Obj, "description", description); + field_from_json(Obj, "revision", revision); + field_from_json(Obj, "uri", uri); + field_from_json(Obj, "image", image); + field_from_json(Obj, "imageDate", imageDate); + field_from_json(Obj, "size", size); + field_from_json(Obj, "downloadCount", downloadCount); + field_from_json(Obj, "firmwareHash", firmwareHash); + field_from_json(Obj, "owner", owner); + field_from_json(Obj, "location", location); + field_from_json(Obj, "uploader", uploader); + field_from_json(Obj, "digest", digest); + field_from_json(Obj, "latest", latest); + field_from_json(Obj, "notes", notes); + field_from_json(Obj, "created", created); + return true; + } catch (...) { + + } + return true; + } + + void FirmwareList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"firmwares",firmwares); + } + + bool FirmwareList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "firmwares", firmwares); + return true; + } catch (...) { + + } + return false; + } + + void DeviceType::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "id", id); + field_to_json(Obj, "deviceType", deviceType); + field_to_json(Obj, "manufacturer", manufacturer); + field_to_json(Obj, "model", model); + field_to_json(Obj, "policy", policy); + field_to_json(Obj, "notes", notes); + field_to_json(Obj, "lastUpdate", lastUpdate); + field_to_json(Obj, "created", created); + field_to_json(Obj, "id", id); + field_to_json(Obj, "id", id); + field_to_json(Obj, "id", id); + } + + bool DeviceType::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "id", id); + field_from_json(Obj, "deviceType", deviceType); + field_from_json(Obj, "manufacturer", manufacturer); + field_from_json(Obj, "model", model); + field_from_json(Obj, "policy", policy); + field_from_json(Obj, "notes", notes); + field_from_json(Obj, "lastUpdate", lastUpdate); + field_from_json(Obj, "created", created); + field_from_json(Obj, "id", id); + field_from_json(Obj, "id", id); + field_from_json(Obj, "id", id); + return true; + } catch (...) { + + } + return false; + } + + void DeviceTypeList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"deviceTypes", deviceTypes); + } + + bool DeviceTypeList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"deviceTypes", deviceTypes); + return true; + } catch(...) { + + } + return false; + } + + void RevisionHistoryEntry::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "id", id); + field_to_json(Obj, "serialNumber", serialNumber); + field_to_json(Obj, "fromRelease", fromRelease); + field_to_json(Obj, "toRelease", toRelease); + field_to_json(Obj, "commandUUID", commandUUID); + field_to_json(Obj, "revisionId", revisionId); + field_to_json(Obj, "upgraded", upgraded); + } + + bool RevisionHistoryEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "id", id); + field_from_json(Obj, "serialNumber", serialNumber); + field_from_json(Obj, "fromRelease", fromRelease); + field_from_json(Obj, "toRelease", toRelease); + field_from_json(Obj, "commandUUID", commandUUID); + field_from_json(Obj, "revisionId", revisionId); + field_from_json(Obj, "upgraded", upgraded); + return true; + } catch(...) { + + } + return false; + } + + void RevisionHistoryEntryList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"deviceTypes", history); + } + + bool RevisionHistoryEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"deviceTypes", history); + return true; + } catch(...) { + + } + return false; + } + + void FirmwareAgeDetails::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"latestId", latestId); + field_to_json(Obj,"image", image); + field_to_json(Obj,"imageDate", imageDate); + field_to_json(Obj,"revision", revision); + field_to_json(Obj,"uri", uri); + field_to_json(Obj,"age", age); + field_to_json(Obj,"latest",latest); + } + + bool FirmwareAgeDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"latestId", latestId); + field_from_json(Obj,"image", image); + field_from_json(Obj,"imageDate", imageDate); + field_from_json(Obj,"revision", revision); + field_from_json(Obj,"uri", uri); + field_from_json(Obj,"age", age); + field_from_json(Obj,"latest", latest); + return true; + } catch(...) { + + } + return false; + } + + void DeviceConnectionInformation::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "serialNumber", serialNumber); + field_to_json(Obj, "revision", revision); + field_to_json(Obj, "deviceType", deviceType); + field_to_json(Obj, "endPoint", endPoint); + field_to_json(Obj, "lastUpdate", lastUpdate); + field_to_json(Obj, "status", status); + } + + bool DeviceConnectionInformation::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj, "serialNumber", serialNumber); + field_from_json(Obj, "revision", revision); + field_from_json(Obj, "deviceType", deviceType); + field_from_json(Obj, "endPoint", endPoint); + field_from_json(Obj, "lastUpdate", lastUpdate); + field_from_json(Obj, "status", status); + return true; + } catch(...) { + + } + return false; + } + + void DeviceReport::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "ouis",OUI_); + field_to_json(Obj, "revisions", Revisions_); + field_to_json(Obj, "deviceTypes", DeviceTypes_); + field_to_json(Obj, "status", Status_); + field_to_json(Obj, "endPoints", EndPoints_); + field_to_json(Obj, "usingLatest", UsingLatest_); + field_to_json(Obj, "unknownFirmwares", UnknownFirmwares_); + field_to_json(Obj,"snapshot",snapshot); + field_to_json(Obj,"numberOfDevices",numberOfDevices); + field_to_json(Obj, "totalSecondsOld", totalSecondsOld_); + } + + void DeviceReport::reset() { + OUI_.clear(); + Revisions_.clear(); + DeviceTypes_.clear(); + Status_.clear(); + EndPoints_.clear(); + UsingLatest_.clear(); + UnknownFirmwares_.clear(); + totalSecondsOld_.clear(); + numberOfDevices = 0 ; + snapshot = std::time(nullptr); + } + + bool DeviceReport::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + + return true; + } catch (...) { + + } + return false; + } +} diff --git a/src/RESTObjects/RESTAPI_FMSObjects.h b/src/RESTObjects/RESTAPI_FMSObjects.h new file mode 100644 index 0000000..86dfb70 --- /dev/null +++ b/src/RESTObjects/RESTAPI_FMSObjects.h @@ -0,0 +1,133 @@ +// +// Created by stephane bourque on 2021-07-12. +// + +#include + +#ifndef UCENTRALFMS_RESTAPI_FMSOBJECTS_H +#define UCENTRALFMS_RESTAPI_FMSOBJECTS_H + + +#include "RESTAPI_SecurityObjects.h" +#include "framework/OpenWifiTypes.h" + +namespace OpenWifi::FMSObjects { + + struct Firmware { + std::string id; + std::string release; + std::string deviceType; + std::string description; + std::string revision; + std::string uri; + std::string image; + uint64_t imageDate=0; + uint64_t size=0; + uint64_t downloadCount=0; + std::string firmwareHash; + std::string owner; + std::string location; + std::string uploader; + std::string digest; + bool latest=false; + SecurityObjects::NoteInfoVec notes; + uint64_t created=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector FirmwareVec; + + struct FirmwareList { + FirmwareVec firmwares; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct DeviceType { + std::string id; + std::string deviceType; + std::string manufacturer; + std::string model; + std::string policy; + SecurityObjects::NoteInfoVec notes; + uint64_t lastUpdate=0; + uint64_t created=0; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector DeviceTypeVec; + + struct DeviceTypeList { + DeviceTypeVec deviceTypes; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct RevisionHistoryEntry { + std::string id; + std::string serialNumber; + std::string fromRelease; + std::string toRelease; + std::string commandUUID; + std::string revisionId; + uint64_t upgraded; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector RevisionHistoryEntryVec; + + struct RevisionHistoryEntryList { + RevisionHistoryEntryVec history; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct FirmwareAgeDetails { + std::string latestId; + std::string image; + uint64_t imageDate; + std::string revision; + std::string uri; + uint64_t age=0; + bool latest=true; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct DeviceConnectionInformation { + std::string serialNumber; + std::string revision; + std::string deviceType; + std::string endPoint; + uint64_t lastUpdate; + std::string status; + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct DeviceReport { + uint64_t snapshot=0; + uint64_t numberOfDevices=0; + Types::CountedMap OUI_; + Types::CountedMap Revisions_; + Types::CountedMap DeviceTypes_; + Types::CountedMap Status_; + Types::CountedMap EndPoints_; + Types::CountedMap UsingLatest_; + Types::CountedMap UnknownFirmwares_; + Types::CountedMap totalSecondsOld_; + void to_json(Poco::JSON::Object &Obj) const; + void reset(); + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; +} + + +#endif //UCENTRALFMS_RESTAPI_FMSOBJECTS_H diff --git a/src/RESTObjects/RESTAPI_GWobjects.cpp b/src/RESTObjects/RESTAPI_GWobjects.cpp new file mode 100644 index 0000000..bb14f8e --- /dev/null +++ b/src/RESTObjects/RESTAPI_GWobjects.cpp @@ -0,0 +1,263 @@ +// +// 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" +#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", Daemon::instance()->IdentifyDevice(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); + } + + 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(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); + 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); + } + + bool DefaultConfiguration::from_json(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(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,"serialNumber", SerialNumber); + 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); + + 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 = std::time(nullptr); + } + + void CapabilitiesModel::to_json(Poco::JSON::Object &Obj) const{ + field_to_json(Obj,"deviceType", deviceType); + field_to_json(Obj,"capabilities", capabilities); + }; + +} + diff --git a/src/RESTObjects/RESTAPI_GWobjects.h b/src/RESTObjects/RESTAPI_GWobjects.h new file mode 100644 index 0000000..698e399 --- /dev/null +++ b/src/RESTObjects/RESTAPI_GWobjects.h @@ -0,0 +1,195 @@ +// +// 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. +// + +#ifndef UCENTRAL_RESTAPI_OBJECTS_H +#define UCENTRAL_RESTAPI_OBJECTS_H + +#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 SerialNumber; + 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; + 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; + uint64_t CreationTimestamp; + uint64_t LastConfigurationChange; + uint64_t LastConfigurationDownload; + uint64_t LastFWUpdate; + std::string Venue; + std::string DevicePassword; + void to_json(Poco::JSON::Object &Obj) const; + void to_json_with_status(Poco::JSON::Object &Obj) const; + bool from_json(Poco::JSON::Object::Ptr Obj); + void Print() const; + }; + + struct Statistics { + std::string SerialNumber; + uint64_t UUID; + std::string Data; + uint64_t Recorded; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct HealthCheck { + std::string SerialNumber; + uint64_t UUID; + std::string Data; + uint64_t Recorded; + uint64_t Sanity; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct Capabilities { + std::string Capabilities; + uint64_t FirstUpdate; + uint64_t LastUpdate; + 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; + uint64_t Recorded; + uint64_t LogType; + uint64_t UUID; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct DefaultConfiguration { + std::string Name; + std::string Configuration; + std::string Models; + std::string Description; + uint64_t Created; + uint64_t LastModified; + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(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; + 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(Poco::JSON::Object::Ptr Obj); + }; + + struct RttySessionDetails { + std::string SerialNumber; + std::string Server; + uint64_t Port; + std::string Token; + uint64_t TimeOut; + std::string ConnectionId; + uint64_t Started; + std::string CommandUUID; + uint64_t ViewPort; + std::string DevicePassword; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct Dashboard { + uint64_t snapshot; + uint64_t numberOfDevices; + 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; + }; +} + +#endif //UCENTRAL_RESTAPI_OBJECTS_H diff --git a/src/RESTObjects/RESTAPI_ProvObjects.cpp b/src/RESTObjects/RESTAPI_ProvObjects.cpp new file mode 100644 index 0000000..e81c798 --- /dev/null +++ b/src/RESTObjects/RESTAPI_ProvObjects.cpp @@ -0,0 +1,440 @@ +// +// 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 "RESTAPI_ProvObjects.h" +#include "framework/MicroService.h" + +namespace OpenWifi::ProvObjects { + + void ObjectInfo::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj,"id",id); + RESTAPI_utils::field_to_json(Obj,"name",name); + RESTAPI_utils::field_to_json(Obj,"description",description); + RESTAPI_utils::field_to_json(Obj,"created",created); + RESTAPI_utils::field_to_json(Obj,"modified",modified); + RESTAPI_utils::field_to_json(Obj,"notes",notes); + RESTAPI_utils::field_to_json(Obj,"tags",tags); + } + + bool ObjectInfo::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json(Obj,"id",id); + RESTAPI_utils::field_from_json(Obj,"name",name); + RESTAPI_utils::field_from_json(Obj,"description",description); + RESTAPI_utils::field_from_json(Obj,"created",created); + RESTAPI_utils::field_from_json(Obj,"modified",modified); + RESTAPI_utils::field_from_json(Obj,"notes",notes); + RESTAPI_utils::field_from_json(Obj,"tags",tags); + return true; + } catch(...) { + + } + return false; + } + + void ManagementPolicyEntry::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json( Obj,"users",users); + RESTAPI_utils::field_to_json( Obj,"resources",resources); + RESTAPI_utils::field_to_json( Obj,"access",access); + RESTAPI_utils::field_to_json( Obj,"policy",policy); + } + + bool ManagementPolicyEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json( Obj,"users",users); + RESTAPI_utils::field_from_json( Obj,"resources",resources); + RESTAPI_utils::field_from_json( Obj,"access",access); + RESTAPI_utils::field_from_json( Obj,"policy",policy); + return true; + } catch(...) { + + } + return false; + } + + void ManagementPolicy::to_json(Poco::JSON::Object &Obj) const { + info.to_json(Obj); + RESTAPI_utils::field_to_json(Obj, "entries", entries); + RESTAPI_utils::field_to_json(Obj, "inUse", inUse); + RESTAPI_utils::field_to_json(Obj, "entity", entity); + } + + bool ManagementPolicy::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + info.from_json(Obj); + RESTAPI_utils::field_from_json(Obj, "entries", entries); + RESTAPI_utils::field_from_json(Obj, "inUse", inUse); + RESTAPI_utils::field_from_json(Obj, "entity", entity); + return true; + } catch(...) { + + } + return false; + } + + void Entity::to_json(Poco::JSON::Object &Obj) const { + info.to_json(Obj); + RESTAPI_utils::field_to_json( Obj,"parent",parent); + RESTAPI_utils::field_to_json( Obj,"venues",venues); + RESTAPI_utils::field_to_json( Obj,"children",children); + RESTAPI_utils::field_to_json( Obj,"contacts",contacts); + RESTAPI_utils::field_to_json( Obj,"locations",locations); + RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); + RESTAPI_utils::field_to_json( Obj,"deviceConfiguration",deviceConfiguration); + RESTAPI_utils::field_to_json( Obj,"devices",devices); + RESTAPI_utils::field_to_json( Obj,"rrm",rrm); + RESTAPI_utils::field_to_json( Obj,"sourceIP",sourceIP); + } + + bool Entity::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + info.from_json(Obj); + RESTAPI_utils::field_from_json( Obj,"parent",parent); + RESTAPI_utils::field_from_json( Obj,"venues",venues); + RESTAPI_utils::field_from_json( Obj,"children",children); + RESTAPI_utils::field_from_json( Obj,"contacts",contacts); + RESTAPI_utils::field_from_json( Obj,"locations",locations); + RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); + RESTAPI_utils::field_from_json( Obj,"deviceConfiguration",deviceConfiguration); + RESTAPI_utils::field_from_json( Obj,"devices",devices); + RESTAPI_utils::field_from_json( Obj,"rrm",rrm); + RESTAPI_utils::field_from_json( Obj,"sourceIP",sourceIP); + return true; + } catch(...) { + + } + return false; + } + + void DiGraphEntry::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json( Obj,"parent",parent); + RESTAPI_utils::field_to_json( Obj,"child",child); + } + + bool DiGraphEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json( Obj,"parent",parent); + RESTAPI_utils::field_from_json( Obj,"child",child); + return true; + } catch (...) { + + } + return false; + } + + void Venue::to_json(Poco::JSON::Object &Obj) const { + info.to_json(Obj); + RESTAPI_utils::field_to_json( Obj,"parent",parent); + RESTAPI_utils::field_to_json( Obj,"entity",entity); + RESTAPI_utils::field_to_json( Obj,"children",children); + RESTAPI_utils::field_to_json( Obj,"devices",devices); + RESTAPI_utils::field_to_json( Obj,"topology",topology); + RESTAPI_utils::field_to_json( Obj,"parent",parent); + RESTAPI_utils::field_to_json( Obj,"design",design); + RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); + RESTAPI_utils::field_to_json( Obj,"deviceConfiguration",deviceConfiguration); + RESTAPI_utils::field_to_json( Obj,"contact",contact); + RESTAPI_utils::field_to_json( Obj,"location",location); + RESTAPI_utils::field_to_json( Obj,"rrm",rrm); + RESTAPI_utils::field_to_json( Obj,"sourceIP",sourceIP); + } + + bool Venue::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + info.from_json(Obj); + RESTAPI_utils::field_from_json( Obj,"parent",parent); + RESTAPI_utils::field_from_json( Obj,"entity",entity); + RESTAPI_utils::field_from_json( Obj,"children",children); + RESTAPI_utils::field_from_json( Obj,"devices",devices); + RESTAPI_utils::field_from_json( Obj,"topology",topology); + RESTAPI_utils::field_from_json( Obj,"parent",parent); + RESTAPI_utils::field_from_json( Obj,"design",design); + RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); + RESTAPI_utils::field_from_json( Obj,"deviceConfiguration",deviceConfiguration); + RESTAPI_utils::field_from_json( Obj,"contact",contact); + RESTAPI_utils::field_from_json( Obj,"location",location); + RESTAPI_utils::field_from_json( Obj,"rrm",rrm); + RESTAPI_utils::field_from_json( Obj,"sourceIP",sourceIP); + return true; + } catch (...) { + + } + return false; + } + + void UserInfoDigest::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json( Obj,"id",id); + RESTAPI_utils::field_to_json( Obj,"entity",loginId); + RESTAPI_utils::field_to_json( Obj,"children",userType); + } + + bool UserInfoDigest::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json( Obj,"id",id); + RESTAPI_utils::field_from_json( Obj,"entity",loginId); + RESTAPI_utils::field_from_json( Obj,"children",userType); + return true; + } catch(...) { + } + return false; + } + + void ManagementRole::to_json(Poco::JSON::Object &Obj) const { + info.to_json(Obj); + RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); + RESTAPI_utils::field_to_json( Obj,"users",users); + RESTAPI_utils::field_to_json( Obj,"entity",entity); + } + + bool ManagementRole::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + info.from_json(Obj); + RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); + RESTAPI_utils::field_from_json( Obj,"users",users); + RESTAPI_utils::field_from_json( Obj,"entity",entity); + return true; + } catch(...) { + } + return false; + } + + void Location::to_json(Poco::JSON::Object &Obj) const { + info.to_json(Obj); + RESTAPI_utils::field_to_json( Obj,"type",OpenWifi::ProvObjects::to_string(type)); + RESTAPI_utils::field_to_json( Obj,"buildingName",buildingName); + RESTAPI_utils::field_to_json( Obj,"addressLines",addressLines); + RESTAPI_utils::field_to_json( Obj,"city",city); + RESTAPI_utils::field_to_json( Obj,"state",state); + RESTAPI_utils::field_to_json( Obj,"postal",postal); + RESTAPI_utils::field_to_json( Obj,"country",country); + RESTAPI_utils::field_to_json( Obj,"phones",phones); + RESTAPI_utils::field_to_json( Obj,"mobiles",mobiles); + RESTAPI_utils::field_to_json( Obj,"geoCode",geoCode); + RESTAPI_utils::field_to_json( Obj,"inUse",inUse); + RESTAPI_utils::field_to_json( Obj,"entity",entity); + RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); + } + + bool Location::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + info.from_json(Obj); + std::string tmp_type; + RESTAPI_utils::field_from_json( Obj,"type", tmp_type); + type = location_from_string(tmp_type); + RESTAPI_utils::field_from_json( Obj,"buildingName",buildingName); + RESTAPI_utils::field_from_json( Obj,"addressLines",addressLines); + RESTAPI_utils::field_from_json( Obj,"city",city); + RESTAPI_utils::field_from_json( Obj,"state",state); + RESTAPI_utils::field_from_json( Obj,"postal",postal); + RESTAPI_utils::field_from_json( Obj,"country",country); + RESTAPI_utils::field_from_json( Obj,"phones",phones); + RESTAPI_utils::field_from_json( Obj,"mobiles",mobiles); + RESTAPI_utils::field_from_json( Obj,"geoCode",geoCode); + RESTAPI_utils::field_from_json( Obj,"inUse",inUse); + RESTAPI_utils::field_from_json( Obj,"entity",entity); + RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); + return true; + } catch (...) { + + } + return false; + } + + void Contact::to_json(Poco::JSON::Object &Obj) const { + info.to_json(Obj); + RESTAPI_utils::field_to_json( Obj,"type", to_string(type)); + RESTAPI_utils::field_to_json( Obj,"title",title); + RESTAPI_utils::field_to_json( Obj,"salutation",salutation); + RESTAPI_utils::field_to_json( Obj,"firstname",firstname); + RESTAPI_utils::field_to_json( Obj,"lastname",lastname); + RESTAPI_utils::field_to_json( Obj,"initials",initials); + RESTAPI_utils::field_to_json( Obj,"visual",visual); + RESTAPI_utils::field_to_json( Obj,"mobiles",mobiles); + RESTAPI_utils::field_to_json( Obj,"phones",phones); + RESTAPI_utils::field_to_json( Obj,"primaryEmail",primaryEmail); + RESTAPI_utils::field_to_json( Obj,"secondaryEmail",secondaryEmail); + RESTAPI_utils::field_to_json( Obj,"accessPIN",accessPIN); + RESTAPI_utils::field_to_json( Obj,"inUse",inUse); + RESTAPI_utils::field_to_json( Obj,"entity",entity); + RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); + } + + bool Contact::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + info.from_json(Obj); + std::string tmp_type; + RESTAPI_utils::field_from_json( Obj,"type", tmp_type); + type = contact_from_string(tmp_type); + RESTAPI_utils::field_from_json( Obj,"title",title); + RESTAPI_utils::field_from_json( Obj,"salutation",salutation); + RESTAPI_utils::field_from_json( Obj,"firstname",firstname); + RESTAPI_utils::field_from_json( Obj,"lastname",lastname); + RESTAPI_utils::field_from_json( Obj,"initials",initials); + RESTAPI_utils::field_from_json( Obj,"visual",visual); + RESTAPI_utils::field_from_json( Obj,"mobiles",mobiles); + RESTAPI_utils::field_from_json( Obj,"phones",phones); + RESTAPI_utils::field_from_json( Obj,"primaryEmail",primaryEmail); + RESTAPI_utils::field_from_json( Obj,"secondaryEmail",secondaryEmail); + RESTAPI_utils::field_from_json( Obj,"accessPIN",accessPIN); + RESTAPI_utils::field_from_json( Obj,"inUse",inUse); + RESTAPI_utils::field_from_json( Obj,"entity",entity); + RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); + return true; + } catch (...) { + + } + return false; + } + + void InventoryTag::to_json(Poco::JSON::Object &Obj) const { + info.to_json(Obj); + RESTAPI_utils::field_to_json(Obj, "serialNumber", serialNumber); + RESTAPI_utils::field_to_json(Obj, "venue", venue); + RESTAPI_utils::field_to_json(Obj, "entity", entity); + RESTAPI_utils::field_to_json(Obj, "subscriber", subscriber); + RESTAPI_utils::field_to_json(Obj, "deviceType", deviceType); + RESTAPI_utils::field_to_json(Obj, "qrCode", qrCode); + RESTAPI_utils::field_to_json(Obj, "geoCode", geoCode); + RESTAPI_utils::field_to_json(Obj, "location", location); + RESTAPI_utils::field_to_json(Obj, "contact", contact); + RESTAPI_utils::field_to_json( Obj,"deviceConfiguration",deviceConfiguration); + RESTAPI_utils::field_to_json( Obj,"rrm",rrm); + RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); + } + + bool InventoryTag::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + info.from_json(Obj); + RESTAPI_utils::field_from_json( Obj,"serialNumber",serialNumber); + RESTAPI_utils::field_from_json( Obj,"venue",venue); + RESTAPI_utils::field_from_json( Obj,"entity",entity); + RESTAPI_utils::field_from_json( Obj,"subscriber",subscriber); + RESTAPI_utils::field_from_json( Obj,"deviceType",deviceType); + RESTAPI_utils::field_from_json(Obj, "qrCode", qrCode); + RESTAPI_utils::field_from_json( Obj,"geoCode",geoCode); + RESTAPI_utils::field_from_json( Obj,"location",location); + RESTAPI_utils::field_from_json( Obj,"contact",contact); + RESTAPI_utils::field_from_json( Obj,"deviceConfiguration",deviceConfiguration); + RESTAPI_utils::field_from_json( Obj,"rrm",rrm); + RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); + return true; + } catch(...) { + + } + return false; + } + + void DeviceConfigurationElement::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json( Obj,"name", name); + RESTAPI_utils::field_to_json( Obj,"description", description); + RESTAPI_utils::field_to_json( Obj,"weight", weight); + RESTAPI_utils::field_to_json( Obj,"configuration", configuration); + } + + bool DeviceConfigurationElement::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json( Obj,"name",name); + RESTAPI_utils::field_from_json( Obj,"description",description); + RESTAPI_utils::field_from_json( Obj,"weight",weight); + RESTAPI_utils::field_from_json( Obj,"configuration",configuration); + return true; + } catch(...) { + + } + return false; + } + + void DeviceConfiguration::to_json(Poco::JSON::Object &Obj) const { + info.to_json(Obj); + RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); + RESTAPI_utils::field_to_json( Obj,"deviceTypes",deviceTypes); + RESTAPI_utils::field_to_json( Obj,"configuration",configuration); + RESTAPI_utils::field_to_json( Obj,"inUse",inUse); + RESTAPI_utils::field_to_json( Obj,"variables",variables); + RESTAPI_utils::field_to_json( Obj,"rrm",rrm); + RESTAPI_utils::field_to_json( Obj,"firmwareUpgrade",firmwareUpgrade); + RESTAPI_utils::field_to_json( Obj,"firmwareRCOnly",firmwareRCOnly); + } + + bool DeviceConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + info.from_json(Obj); + RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); + RESTAPI_utils::field_from_json( Obj,"deviceTypes",deviceTypes); + RESTAPI_utils::field_from_json( Obj,"configuration",configuration); + RESTAPI_utils::field_from_json( Obj,"inUse",inUse); + RESTAPI_utils::field_from_json( Obj,"variables",variables); + RESTAPI_utils::field_from_json( Obj,"rrm",rrm); + RESTAPI_utils::field_from_json( Obj,"firmwareUpgrade",firmwareUpgrade); + RESTAPI_utils::field_from_json( Obj,"firmwareRCOnly",firmwareRCOnly); + return true; + } catch(...) { + + } + return false; + } + + void Report::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj, "snapshot", snapShot); + RESTAPI_utils::field_to_json(Obj, "devices", tenants); + }; + + void Report::reset() { + tenants.clear(); + } + + void ExpandedUseEntry::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj, "uuid", uuid); + RESTAPI_utils::field_to_json(Obj, "name", name); + RESTAPI_utils::field_to_json(Obj, "description", description); + } + + bool ExpandedUseEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json( Obj,"uuid",uuid); + RESTAPI_utils::field_from_json( Obj,"name",name); + RESTAPI_utils::field_from_json( Obj,"description",description); + return true; + } catch(...) { + + } + return false; + } + + void ExpandedUseEntryList::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj, "type", type); + RESTAPI_utils::field_to_json(Obj, "entries", entries); + } + + bool ExpandedUseEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json( Obj,"type",type); + RESTAPI_utils::field_from_json( Obj,"entries",entries); + return true; + } catch(...) { + + } + return false; + } + + void ExpandedUseEntryMapList::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json(Obj, "entries", entries); + } + + bool ExpandedUseEntryMapList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json( Obj,"entries",entries); + return true; + } catch(...) { + + } + return false; + } + +}; diff --git a/src/RESTObjects/RESTAPI_ProvObjects.h b/src/RESTObjects/RESTAPI_ProvObjects.h new file mode 100644 index 0000000..3aeff47 --- /dev/null +++ b/src/RESTObjects/RESTAPI_ProvObjects.h @@ -0,0 +1,322 @@ +// +// 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. +// + + +#ifndef OWPROV_RESTAPI_PROVOBJECTS_H +#define OWPROV_RESTAPI_PROVOBJECTS_H + +#include +#include "RESTAPI_SecurityObjects.h" + +namespace OpenWifi::ProvObjects { + + struct ObjectInfo { + Types::UUID_t id; + std::string name; + std::string description; + SecurityObjects::NoteInfoVec notes; + uint64_t created=0; + uint64_t modified=0; + Types::TagList tags; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct ManagementPolicyEntry { + Types::UUIDvec_t users; + Types::UUIDvec_t resources; + Types::StringVec access; + std::string policy; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct ManagementPolicy { + ObjectInfo info; + std::vector entries; + Types::StringVec inUse; + Types::UUID_t entity; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector ManagementPolicyVec; + + struct Entity { + ObjectInfo info; + Types::UUID_t parent; + Types::UUIDvec_t children; + Types::UUIDvec_t venues; + Types::UUIDvec_t contacts; // all contacts associated in this entity + Types::UUIDvec_t locations; // all locations associated in this entity + Types::UUID_t managementPolicy; + std::string deviceConfiguration; + Types::UUIDvec_t devices; + std::string rrm; + Types::StringVec sourceIP; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector EntityVec; + + struct DiGraphEntry { + Types::UUID_t parent; + Types::UUID_t child; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + typedef std::vector DiGraph; + + struct Venue { + ObjectInfo info; + Types::UUID_t entity; + Types::UUID_t parent; + Types::UUIDvec_t children; + Types::UUID_t managementPolicy; + Types::UUIDvec_t devices; + DiGraph topology; + std::string design; + std::string deviceConfiguration; + std::string contact; + std::string location; + std::string rrm; + Types::StringVec sourceIP; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector VenueVec; + + struct UserInfoDigest { + std::string id; + std::string loginId; + std::string userType; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct ManagementRole { + ObjectInfo info; + Types::UUID_t managementPolicy; + Types::UUIDvec_t users; + Types::StringVec inUse; + Types::UUID_t entity; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector ManagementRoleVec; + + enum LocationType { + LT_SERVICE, LT_EQUIPMENT, LT_AUTO, LT_MANUAL, + LT_SPECIAL, LT_UNKNOWN, LT_CORPORATE + }; + + inline std::string to_string(LocationType L) { + switch(L) { + case LT_SERVICE: return "SERVICE"; + case LT_EQUIPMENT: return "EQUIPMENT"; + case LT_AUTO: return "AUTO"; + case LT_MANUAL: return "MANUAL"; + case LT_SPECIAL: return "SPECIAL"; + case LT_UNKNOWN: return "UNKNOWN"; + case LT_CORPORATE: return "CORPORATE"; + default: return "UNKNOWN"; + } + } + + inline LocationType location_from_string(const std::string &S) { + if(!Poco::icompare(S,"SERVICE")) + return LT_SERVICE; + else if(!Poco::icompare(S,"EQUIPMENT")) + return LT_EQUIPMENT; + else if(!Poco::icompare(S,"AUTO")) + return LT_AUTO; + else if(!Poco::icompare(S,"MANUAL")) + return LT_MANUAL; + else if(!Poco::icompare(S,"SPECIAL")) + return LT_SPECIAL; + else if(!Poco::icompare(S,"UNKNOWN")) + return LT_UNKNOWN; + else if(!Poco::icompare(S,"CORPORATE")) + return LT_CORPORATE; + return LT_UNKNOWN; + } + + struct Location { + ObjectInfo info; + LocationType type; + std::string buildingName; + Types::StringVec addressLines; + std::string city; + std::string state; + std::string postal; + std::string country; + Types::StringVec phones; + Types::StringVec mobiles; + std::string geoCode; + Types::StringVec inUse; + Types::UUID_t entity; + Types::UUID_t managementPolicy; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector LocationVec; + + enum ContactType { + CT_SUBSCRIBER, CT_USER, CT_INSTALLER, CT_CSR, CT_MANAGER, + CT_BUSINESSOWNER, CT_TECHNICIAN, CT_CORPORATE, CT_UNKNOWN + }; + + inline std::string to_string(ContactType L) { + switch(L) { + case CT_SUBSCRIBER: return "SUBSCRIBER"; + case CT_USER: return "USER"; + case CT_INSTALLER: return "INSTALLER"; + case CT_CSR: return "CSR"; + case CT_MANAGER: return "MANAGER"; + case CT_BUSINESSOWNER: return "BUSINESSOWNER"; + case CT_TECHNICIAN: return "TECHNICIAN"; + case CT_CORPORATE: return "CORPORATE"; + case CT_UNKNOWN: return "UNKNOWN"; + default: return "UNKNOWN"; + } + } + + inline ContactType contact_from_string(const std::string &S) { + if(!Poco::icompare(S,"SUBSCRIBER")) + return CT_SUBSCRIBER; + else if(!Poco::icompare(S,"USER")) + return CT_USER; + else if(!Poco::icompare(S,"INSTALLER")) + return CT_INSTALLER; + else if(!Poco::icompare(S,"CSR")) + return CT_CSR; + else if(!Poco::icompare(S,"BUSINESSOWNER")) + return CT_BUSINESSOWNER; + else if(!Poco::icompare(S,"TECHNICIAN")) + return CT_TECHNICIAN; + else if(!Poco::icompare(S,"CORPORATE")) + return CT_CORPORATE; + else if(!Poco::icompare(S,"UNKNOWN")) + return CT_UNKNOWN; + return CT_UNKNOWN; + } + + struct Contact { + ObjectInfo info; + ContactType type=CT_USER; + std::string title; + std::string salutation; + std::string firstname; + std::string lastname; + std::string initials; + std::string visual; + Types::StringVec mobiles; + Types::StringVec phones; + std::string primaryEmail; + std::string secondaryEmail; + std::string accessPIN; + Types::StringVec inUse; + Types::UUID_t entity; + Types::UUID_t managementPolicy; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector ContactVec; + + struct DeviceConfigurationElement { + std::string name; + std::string description; + uint64_t weight; + std::string configuration; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector DeviceConfigurationElementVec; + + struct DeviceConfiguration { + ObjectInfo info; + Types::UUID_t managementPolicy; + Types::StringVec deviceTypes; + DeviceConfigurationElementVec configuration; + Types::StringVec inUse; + Types::StringPairVec variables; + std::string rrm; + std::string firmwareUpgrade; + bool firmwareRCOnly=false; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector DeviceConfigurationVec; + + struct InventoryTag { + ObjectInfo info; + std::string serialNumber; + std::string venue; + std::string entity; + std::string subscriber; + std::string deviceType; + std::string qrCode; + std::string geoCode; + std::string location; + std::string contact; + std::string deviceConfiguration; + std::string rrm; + Types::UUID_t managementPolicy; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + typedef std::vector InventoryTagVec; + + struct Report { + uint64_t snapShot=0; + Types::CountedMap tenants; + + void reset(); + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct ExpandedUseEntry { + std::string uuid; + std::string name; + std::string description; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct ExpandedUseEntryList { + std::string type; + std::vector entries; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct ExpandedUseEntryMapList { + std::vector entries; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; +}; + + +#endif //OWPROV_RESTAPI_PROVOBJECTS_H diff --git a/src/RESTAPI/RESTAPI_SecurityObjects.cpp b/src/RESTObjects/RESTAPI_SecurityObjects.cpp similarity index 99% rename from src/RESTAPI/RESTAPI_SecurityObjects.cpp rename to src/RESTObjects/RESTAPI_SecurityObjects.cpp index 285c127..2ad5a9e 100644 --- a/src/RESTAPI/RESTAPI_SecurityObjects.cpp +++ b/src/RESTObjects/RESTAPI_SecurityObjects.cpp @@ -9,8 +9,8 @@ #include "Poco/JSON/Parser.h" #include "Poco/JSON/Stringifier.h" +#include "framework/MicroService.h" #include "RESTAPI_SecurityObjects.h" -#include "../framework/RESTAPI_utils.h" using OpenWifi::RESTAPI_utils::field_to_json; using OpenWifi::RESTAPI_utils::field_from_json; diff --git a/src/RESTAPI/RESTAPI_SecurityObjects.h b/src/RESTObjects/RESTAPI_SecurityObjects.h similarity index 100% rename from src/RESTAPI/RESTAPI_SecurityObjects.h rename to src/RESTObjects/RESTAPI_SecurityObjects.h diff --git a/src/SMSSender.cpp b/src/SMSSender.cpp index b3462fc..ee51374 100644 --- a/src/SMSSender.cpp +++ b/src/SMSSender.cpp @@ -2,22 +2,22 @@ // Created by stephane bourque on 2021-10-09. // -#include "SMSSender.h" -#include "Daemon.h" #include #include #include #include -#include "MFAServer.h" +#include "MFAServer.h" #include "SMS_provider_aws.h" #include "SMS_provider_twilio.h" +#include "SMSSender.h" +#include "framework/MicroService.h" namespace OpenWifi { class SMSSender * SMSSender::instance_ = nullptr; int SMSSender::Start() { - Provider_ = Daemon()->ConfigGetString("sms.provider","aws"); + Provider_ = MicroService::instance().ConfigGetString("sms.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 e8bcbe6..29dec7d 100644 --- a/src/SMSSender.h +++ b/src/SMSSender.h @@ -5,10 +5,11 @@ #ifndef OWSEC_SMSSENDER_H #define OWSEC_SMSSENDER_H -#include "framework/SubSystemServer.h" #include #include #include + +#include "framework/MicroService.h" #include "SMS_provider.h" namespace OpenWifi { diff --git a/src/SMS_provider_aws.cpp b/src/SMS_provider_aws.cpp index 11176e6..121d2a6 100644 --- a/src/SMS_provider_aws.cpp +++ b/src/SMS_provider_aws.cpp @@ -2,18 +2,19 @@ // Created by stephane bourque on 2021-10-15. // -#include "SMS_provider_aws.h" -#include "Daemon.h" #include #include #include +#include "framework/MicroService.h" +#include "SMS_provider_aws.h" + namespace OpenWifi { bool SMS_provider_aws::Initialize() { - SecretKey_ = Daemon()->ConfigGetString("smssender.aws.secretkey",""); - AccessKey_ = Daemon()->ConfigGetString("smssender.aws.accesskey",""); - Region_ = Daemon()->ConfigGetString("smssender.aws.region",""); + SecretKey_ = MicroService::instance().ConfigGetString("smssender.aws.secretkey",""); + AccessKey_ = MicroService::instance().ConfigGetString("smssender.aws.accesskey",""); + Region_ = MicroService::instance().ConfigGetString("smssender.aws.region",""); if(SecretKey_.empty() || AccessKey_.empty() || Region_.empty()) { Logger_.debug("SMSSender is disabled. Please provide key, secret, and region."); diff --git a/src/SMS_provider_aws.h b/src/SMS_provider_aws.h index c79a0f4..b59a131 100644 --- a/src/SMS_provider_aws.h +++ b/src/SMS_provider_aws.h @@ -5,11 +5,12 @@ #ifndef OWSEC_SMS_PROVIDER_AWS_H #define OWSEC_SMS_PROVIDER_AWS_H -#include "SMS_provider.h" #include #include #include +#include "SMS_provider.h" + namespace OpenWifi { class SMS_provider_aws : public SMS_provider { public: diff --git a/src/SMS_provider_twilio.cpp b/src/SMS_provider_twilio.cpp index 7d0d275..f3f033e 100644 --- a/src/SMS_provider_twilio.cpp +++ b/src/SMS_provider_twilio.cpp @@ -4,18 +4,18 @@ #include "SMS_provider_twilio.h" -#include "Daemon.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/MicroService.h" namespace OpenWifi { bool SMS_provider_twilio::Initialize() { - Sid_ = Daemon()->ConfigGetString("smssender.twilio.sid",""); - Token_ = Daemon()->ConfigGetString("smssender.twilio.token",""); - PhoneNumber_ = Daemon()->ConfigGetString("smssender.twilio.phonenumber",""); + Sid_ = MicroService::instance().ConfigGetString("smssender.twilio.sid",""); + Token_ = MicroService::instance().ConfigGetString("smssender.twilio.token",""); + PhoneNumber_ = MicroService::instance().ConfigGetString("smssender.twilio.phonenumber",""); if(Sid_.empty() || Token_.empty() || PhoneNumber_.empty()) { Logger_.debug("SMSSender is disabled. Please provide SID, TOKEN, and PHONE NUMBER."); diff --git a/src/SMTPMailerService.cpp b/src/SMTPMailerService.cpp index 2502213..d601b83 100644 --- a/src/SMTPMailerService.cpp +++ b/src/SMTPMailerService.cpp @@ -17,21 +17,20 @@ #include "Poco/Net/AcceptCertificateHandler.h" #include "SMTPMailerService.h" -#include "framework/Utils.h" -#include "Daemon.h" +#include "framework/MicroService.h" namespace OpenWifi { class SMTPMailerService * SMTPMailerService::instance_ = nullptr; void SMTPMailerService::LoadMyConfig() { - MailHost_ = Daemon()->ConfigGetString("mailer.hostname"); - SenderLoginUserName_ = Daemon()->ConfigGetString("mailer.username"); - SenderLoginPassword_ = Daemon()->ConfigGetString("mailer.password"); - Sender_ = Daemon()->ConfigGetString("mailer.sender"); - LoginMethod_ = Daemon()->ConfigGetString("mailer.loginmethod"); - MailHostPort_ = (int)Daemon()->ConfigGetInt("mailer.port"); - TemplateDir_ = Daemon()->ConfigPath("mailer.templates", Daemon()->DataDir()); + 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_ = (int) MicroService::instance().ConfigGetInt("mailer.port"); + TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir()); Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty()); } @@ -48,7 +47,7 @@ namespace OpenWifi { } void SMTPMailerService::reinitialize(Poco::Util::Application &self) { - Daemon()->LoadConfigurationFile(); + MicroService::instance().LoadConfigurationFile(); Logger_.information("Reinitializing."); LoadMyConfig(); } diff --git a/src/SMTPMailerService.h b/src/SMTPMailerService.h index 4fa212a..532a0d4 100644 --- a/src/SMTPMailerService.h +++ b/src/SMTPMailerService.h @@ -5,7 +5,7 @@ #ifndef UCENTRALSEC_SMTPMAILERSERVICE_H #define UCENTRALSEC_SMTPMAILERSERVICE_H -#include "framework/SubSystemServer.h" +#include "framework/MicroService.h" #include "Poco/File.h" #include "Poco/Net/InvalidCertificateHandler.h" diff --git a/src/StorageService.cpp b/src/StorageService.cpp index b1dfe32..d2f752e 100644 --- a/src/StorageService.cpp +++ b/src/StorageService.cpp @@ -7,57 +7,23 @@ // #include "StorageService.h" -#include "Daemon.h" -#include "Poco/Util/Application.h" -#include "framework/Utils.h" +#include "framework/MicroService.h" namespace OpenWifi { class Storage *Storage::instance_ = nullptr; - std::string Storage::ConvertParams(const std::string & S) const { - std::string R; - - R.reserve(S.size()*2+1); - - if(dbType_==pgsql) { - auto Idx=1; - for(auto const & i:S) - { - if(i=='?') { - R += '$'; - R.append(std::to_string(Idx++)); - } else { - R += i; - } - } - } else { - R = S; - } - return R; - } - int Storage::Start() { std::lock_guard Guard(Mutex_); - Logger_.setLevel(Poco::Message::PRIO_NOTICE); - Logger_.notice("Starting."); - std::string DBType = Daemon()->ConfigGetString("storage.type"); - if (DBType == "sqlite") { - Setup_SQLite(); - } else if (DBType == "postgresql") { - Setup_PostgreSQL(); - } else if (DBType == "mysql") { - Setup_MySQL(); - } - + StorageClass::Start(); Create_Tables(); - return 0; } void Storage::Stop() { Logger_.notice("Stopping."); + StorageClass::Stop(); } } // namespace \ No newline at end of file diff --git a/src/StorageService.h b/src/StorageService.h index 820b9bf..95e20dc 100644 --- a/src/StorageService.h +++ b/src/StorageService.h @@ -21,9 +21,8 @@ #endif #include "AuthService.h" -#include "RESTAPI/RESTAPI_SecurityObjects.h" -#include "framework/SubSystemServer.h" -#include "framework/Storage.h" +#include "RESTObjects/RESTAPI_SecurityObjects.h" +#include "framework/StorageClass.h" namespace OpenWifi { @@ -67,7 +66,7 @@ namespace OpenWifi { }; - class Storage : public SubSystemServer { + class Storage : public StorageClass { public: enum AUTH_ERROR { @@ -162,43 +161,14 @@ namespace OpenWifi { private: static Storage *instance_; - std::unique_ptr Pool_= nullptr; - DBType dbType_ = sqlite; - std::unique_ptr SQLiteConn_= nullptr; -#ifndef SMALL_BUILD - std::unique_ptr PostgresConn_= nullptr; - std::unique_ptr MySQLConn_= nullptr; -#endif int Create_Tables(); int Create_UserTable(); int Create_AvatarTable(); int Create_TokensTable(); - - [[nodiscard]] std::string ConvertParams(const std::string &S) const; - [[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { - if(dbType_==sqlite) { - return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) + " "; - } else if(dbType_==pgsql) { - return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; - } else if(dbType_==mysql) { - return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; - } - return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; - } - - Storage() noexcept: - SubSystemServer("Storage", "STORAGE-SVR", "storage") - { - } - - int Setup_SQLite(); - int Setup_MySQL(); - int Setup_PostgreSQL(); - }; - inline Storage * Storage() { return Storage::instance(); }; + inline Storage * StorageService() { return Storage::instance(); }; } // namespace diff --git a/src/framework/ALBHealthCheckServer.h b/src/framework/ALBHealthCheckServer.h deleted file mode 100644 index af06941..0000000 --- a/src/framework/ALBHealthCheckServer.h +++ /dev/null @@ -1,118 +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. -// - -#ifndef UCENTRALGW_ALBHEALTHCHECKSERVER_H -#define UCENTRALGW_ALBHEALTHCHECKSERVER_H - -#include -#include -#include -#include - -#include "Poco/Thread.h" -#include "Poco/Net/HTTPServer.h" -#include "Poco/Net/HTTPServerRequest.h" -#include "Poco/Net/HTTPServerResponse.h" -#include "Poco/Net/HTTPRequestHandler.h" -#include "Poco/Logger.h" - -#include "Daemon.h" -#include "SubSystemServer.h" - -namespace OpenWifi { - - class ALBRequestHandler: public Poco::Net::HTTPRequestHandler - /// Return a HTML document with the current date and time. - { - public: - explicit ALBRequestHandler(Poco::Logger & L) - : Logger_(L) - { - } - - void handleRequest(Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) override - { - Logger_.information(Poco::format("ALB-REQUEST(%s): New ALB request.",Request.clientAddress().toString())); - 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 << "uCentralGW Alive and kicking!" ; - } - - private: - Poco::Logger & Logger_; - }; - - 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_); - else - return nullptr; - } - - private: - Poco::Logger &Logger_; - }; - - class ALBHealthCheckServer : public SubSystemServer { - public: - ALBHealthCheckServer() noexcept: - SubSystemServer("ALBHealthCheckServer", "ALB-SVR", "alb") - { - } - - static ALBHealthCheckServer *instance() { - if (instance_ == nullptr) { - instance_ = new ALBHealthCheckServer; - } - return instance_; - } - - int Start() override { - if(Daemon()->ConfigGetBool("alb.enable",false)) { - Port_ = (int)Daemon()->ConfigGetInt("alb.port",15015); - Socket_ = std::make_unique(Port_); - auto Params = new Poco::Net::HTTPServerParams; - Server_ = std::make_unique(new ALBRequestHandlerFactory(Logger_), *Socket_, Params); - Server_->start(); - } - - return 0; - } - - void Stop() override { - if(Server_) - Server_->stop(); - } - - private: - static ALBHealthCheckServer *instance_; - std::unique_ptr Server_; - std::unique_ptr Socket_; - int Port_ = 0; - }; - - inline ALBHealthCheckServer * ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); } - inline class ALBHealthCheckServer * ALBHealthCheckServer::instance_ = nullptr; -} - -#endif // UCENTRALGW_ALBHEALTHCHECKSERVER_H diff --git a/src/framework/AuthClient.cpp b/src/framework/AuthClient.cpp deleted file mode 100644 index 3deb744..0000000 --- a/src/framework/AuthClient.cpp +++ /dev/null @@ -1,93 +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 - -#include "AuthClient.h" -#include "RESTAPI/RESTAPI_SecurityObjects.h" -#include "Daemon.h" -#include "OpenAPIRequest.h" - -namespace OpenWifi { - class AuthClient * AuthClient::instance_ = nullptr; - - int AuthClient::Start() { - return 0; - } - - void AuthClient::Stop() { - - } - - void AuthClient::RemovedCachedToken(const std::string &Token) { - std::lock_guard G(Mutex_); - UserCache_.erase(Token); - } - - bool IsTokenExpired(const SecurityObjects::WebToken &T) { - return ((T.expires_in_+T.created_)second.webtoken)) { - UInfo = User->second; - return true; - } else { - Types::StringPairVec QueryData; - QueryData.push_back(std::make_pair("token",SessionToken)); - OpenAPIRequestGet Req( uSERVICE_SECURITY, - "/api/v1/validateToken", - QueryData, - 5000); - Poco::JSON::Object::Ptr Response; - if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) { - if(Response->has("tokenInfo") && Response->has("userInfo")) { - SecurityObjects::UserInfoAndPolicy P; - P.from_json(Response); - UserCache_[SessionToken] = P; - UInfo = P; - } - return true; - } - - } - return false; - } - - bool AuthClient::IsTokenAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo) { - std::lock_guard G(Mutex_); - - auto User = UserCache_.find(SessionToken); - if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) { - UInfo = User->second; - return true; - } else { - Types::StringPairVec QueryData; - QueryData.push_back(std::make_pair("token",SessionToken)); - OpenAPIRequestGet Req(uSERVICE_SECURITY, - "/api/v1/validateToken", - QueryData, - 5000); - Poco::JSON::Object::Ptr Response; - if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) { - if(Response->has("tokenInfo") && Response->has("userInfo")) { - SecurityObjects::UserInfoAndPolicy P; - P.from_json(Response); - UserCache_[SessionToken] = P; - UInfo = P; - } - return true; - } - - } - return false; - } -} \ No newline at end of file diff --git a/src/framework/AuthClient.h b/src/framework/AuthClient.h deleted file mode 100644 index 6d981eb..0000000 --- a/src/framework/AuthClient.h +++ /dev/null @@ -1,45 +0,0 @@ -// -// Created by stephane bourque on 2021-06-30. -// - -#ifndef UCENTRALGW_AUTHCLIENT_H -#define UCENTRALGW_AUTHCLIENT_H - -#include "Poco/JSON/Object.h" -#include "Poco/JWT/Signer.h" -#include "Poco/Net/HTTPServerRequest.h" -#include "Poco/Net/HTTPServerResponse.h" -#include "Poco/SHA2Engine.h" -#include "RESTAPI/RESTAPI_SecurityObjects.h" -#include "SubSystemServer.h" - -namespace OpenWifi { - -class AuthClient : public SubSystemServer { - public: - explicit AuthClient() noexcept: - SubSystemServer("Authentication", "AUTH-CLNT", "authentication") - { - } - - static AuthClient *instance() { - if (instance_ == nullptr) { - instance_ = new AuthClient; - } - return instance_; - } - - int Start() override; - void Stop() override; - bool IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string &SessionToken, OpenWifi::SecurityObjects::UserInfoAndPolicy & UInfo ); - void RemovedCachedToken(const std::string &Token); - bool IsTokenAuthorized(const std::string &Token, SecurityObjects::UserInfoAndPolicy & UInfo); - private: - static AuthClient *instance_; - OpenWifi::SecurityObjects::UserInfoCache UserCache_; - }; - - inline AuthClient * AuthClient() { return AuthClient::instance(); } -} - -#endif // UCENTRALGW_AUTHCLIENT_H diff --git a/src/framework/CIDRUtils.cpp b/src/framework/CIDRUtils.cpp deleted file mode 100644 index 92a393e..0000000 --- a/src/framework/CIDRUtils.cpp +++ /dev/null @@ -1,147 +0,0 @@ -// -// Created by stephane bourque on 2021-09-29. -// - -#include "CIDRUtils.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; - } - - 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); }); - } - - 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/CIDRUtils.h b/src/framework/CIDRUtils.h deleted file mode 100644 index 762d2c2..0000000 --- a/src/framework/CIDRUtils.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// Created by stephane bourque on 2021-09-29. -// - -#ifndef OWPROV_CIDRUTILS_H -#define OWPROV_CIDRUTILS_H - -#include "framework/OpenWifiTypes.h" - -namespace OpenWifi::CIDR { - [[nodiscard]] bool IpInRanges(const std::string &IP, const Types::StringVec &R); - [[nodiscard]] bool ValidateIpRanges(const Types::StringVec & Ranges); -} - - -#endif //OWPROV_CIDRUTILS_H diff --git a/src/framework/CountryCodes.h b/src/framework/CountryCodes.h index 9bb65d4..8f33af4 100644 --- a/src/framework/CountryCodes.h +++ b/src/framework/CountryCodes.h @@ -16,7 +16,7 @@ namespace OpenWifi { std::string name; }; - static const std::vector CountryCodes { + inline static const std::vector CountryCodes { { .code= "US", .name= "United States" }, { .code= "GB", .name= "United Kingdom" }, { .code= "CA", .name= "Canada" }, diff --git a/src/framework/KafkaManager.cpp b/src/framework/KafkaManager.cpp deleted file mode 100644 index 4fc90d8..0000000 --- a/src/framework/KafkaManager.cpp +++ /dev/null @@ -1,221 +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 - -#include "KafkaManager.h" - -#include "Daemon.h" -#include "Utils.h" - -namespace OpenWifi { - - class KafkaManager *KafkaManager::instance_ = nullptr; - - KafkaManager::KafkaManager() noexcept: - SubSystemServer("KafkaManager", "KAFKA-SVR", "openwifi.kafka") - { - } - - void KafkaManager::initialize(Poco::Util::Application & self) { - SubSystemServer::initialize(self); - KafkaEnabled_ = Daemon()->ConfigGetBool("openwifi.kafka.enable",false); - } - -#ifdef SMALL_BUILD - - int KafkaManager::Start() { - return 0; - } - void KafkaManager::Stop() { - } - -#else - - int KafkaManager::Start() { - if(!KafkaEnabled_) - return 0; - ProducerThr_ = std::make_unique([this]() { this->ProducerThr(); }); - ConsumerThr_ = std::make_unique([this]() { this->ConsumerThr(); }); - return 0; - } - - void KafkaManager::Stop() { - if(KafkaEnabled_) { - ProducerRunning_ = ConsumerRunning_ = false; - ProducerThr_->join(); - ConsumerThr_->join(); - return; - } - } - - void KafkaManager::ProducerThr() { - cppkafka::Configuration Config({ - { "client.id", Daemon()->ConfigGetString("openwifi.kafka.client.id") }, - { "metadata.broker.list", Daemon()->ConfigGetString("openwifi.kafka.brokerlist") } - }); - SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" + - std::to_string(Daemon()->ID()) + - R"lit( , "host" : ")lit" + Daemon()->PrivateEndPoint() + - R"lit(" } , "payload" : )lit" ; - cppkafka::Producer Producer(Config); - ProducerRunning_ = true; - while(ProducerRunning_) { - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - try - { - std::lock_guard G(ProducerMutex_); - auto Num=0; - while (!Queue_.empty()) { - const auto M = Queue_.front(); - Producer.produce( - cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad)); - Queue_.pop(); - Num++; - } - if(Num) - Producer.flush(); - } catch (const cppkafka::HandleException &E ) { - Logger_.warning(Poco::format("Caught a Kafka exception (producer): %s",std::string{E.what()})); - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - } - } - - void KafkaManager::PartitionAssignment(const cppkafka::TopicPartitionList& partitions) { - Logger_.information(Poco::format("Partition assigned: %Lu...",(uint64_t )partitions.front().get_partition())); - } - void KafkaManager::PartitionRevocation(const cppkafka::TopicPartitionList& partitions) { - Logger_.information(Poco::format("Partition revocation: %Lu...",(uint64_t )partitions.front().get_partition())); - } - - void KafkaManager::ConsumerThr() { - cppkafka::Configuration Config({ - { "client.id", Daemon()->ConfigGetString("openwifi.kafka.client.id") }, - { "metadata.broker.list", Daemon()->ConfigGetString("openwifi.kafka.brokerlist") }, - { "group.id", Daemon()->ConfigGetString("openwifi.kafka.group.id") }, - { "enable.auto.commit", Daemon()->ConfigGetBool("openwifi.kafka.auto.commit",false) }, - { "auto.offset.reset", "latest" } , - { "enable.partition.eof", false } - }); - - 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([this](cppkafka::TopicPartitionList& partitions) { - if(!partitions.empty()) { - Logger_.information(Poco::format("Partition assigned: %Lu...", - (uint64_t)partitions.front().get_partition())); - } - }); - Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) { - if(!partitions.empty()) { - Logger_.information(Poco::format("Partition revocation: %Lu...", - (uint64_t)partitions.front().get_partition())); - } - }); - - bool AutoCommit = Daemon()->ConfigGetBool("openwifi.kafka.auto.commit",false); - auto BatchSize = Daemon()->ConfigGetInt("openwifi.kafka.consumer.batchsize",20); - - Types::StringVec Topics; - for(const auto &i:Notifiers_) - Topics.push_back(i.first); - - Consumer.subscribe(Topics); - - ConsumerRunning_ = true; - while(ConsumerRunning_) { - try { - std::vector MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(200)); - for(auto const &Msg:MsgVec) { - if (!Msg) - continue; - if (Msg.get_error()) { - if (!Msg.is_eof()) { - Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string())); - }if(!AutoCommit) - Consumer.async_commit(Msg); - continue; - } - std::lock_guard G(ConsumerMutex_); - auto It = Notifiers_.find(Msg.get_topic()); - if (It != Notifiers_.end()) { - Types::TopicNotifyFunctionList &FL = It->second; - std::string Key{Msg.get_key()}; - std::string Payload{Msg.get_payload()}; - for (auto &F : FL) { - std::thread T(F.first, Key, Payload); - T.detach(); - } - } - if (!AutoCommit) - Consumer.async_commit(Msg); - } - } catch (const cppkafka::HandleException &E) { - Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()})); - } catch (const Poco::Exception &E) { - Logger_.log(E); - } - } - } - - std::string KafkaManager::WrapSystemId(const std::string & PayLoad) { - return std::move( SystemInfoWrapper_ + PayLoad + "}"); - } - - void KafkaManager::PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage ) { - if(KafkaEnabled_) { - std::lock_guard G(Mutex_); - KMessage M{ - .Topic = topic, - .Key = key, - .PayLoad = WrapMessage ? WrapSystemId(PayLoad) : PayLoad }; - Queue_.push(M); - } - } - - int KafkaManager::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { - if(KafkaEnabled_) { - 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_++; - } else { - return 0; - } - } - - void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, int Id) { - if(KafkaEnabled_) { - 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; - } - } - } - } - -#endif -} // namespace diff --git a/src/framework/KafkaManager.h b/src/framework/KafkaManager.h deleted file mode 100644 index b084aff..0000000 --- a/src/framework/KafkaManager.h +++ /dev/null @@ -1,74 +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. -// - -#ifndef UCENTRALGW_KAFKAMANAGER_H -#define UCENTRALGW_KAFKAMANAGER_H - -#include -#include - -#include "SubSystemServer.h" -#include "OpenWifiTypes.h" - -#include "cppkafka/cppkafka.h" - -namespace OpenWifi { - - class KafkaManager : public SubSystemServer { - public: - - struct KMessage { - std::string Topic, - Key, - PayLoad; - }; - - void initialize(Poco::Util::Application & self) override; - static KafkaManager *instance() { - if(instance_== nullptr) - instance_ = new KafkaManager; - return instance_; - } - - void ProducerThr(); - void ConsumerThr(); - - int Start() override; - void Stop() override; - - void PostMessage(const std::string &topic, const std::string & key, const std::string &payload, bool WrapMessage = true); - [[nodiscard]] std::string WrapSystemId(const std::string & PayLoad); - [[nodiscard]] bool Enabled() { return KafkaEnabled_; } - int RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction & F); - void UnregisterTopicWatcher(const std::string &Topic, int FunctionId); - void WakeUp(); - void PartitionAssignment(const cppkafka::TopicPartitionList& partitions); - void PartitionRevocation(const cppkafka::TopicPartitionList& partitions); - - private: - static KafkaManager *instance_; - std::mutex ProducerMutex_; - std::mutex ConsumerMutex_; - bool KafkaEnabled_ = false; - std::atomic_bool ProducerRunning_ = false; - std::atomic_bool ConsumerRunning_ = false; - std::queue Queue_; - std::string SystemInfoWrapper_; - std::unique_ptr ConsumerThr_; - std::unique_ptr ProducerThr_; - int FunctionId_=1; - Types::NotifyTable Notifiers_; - std::unique_ptr Config_; - - KafkaManager() noexcept; - }; - - inline KafkaManager * KafkaManager() { return KafkaManager::instance(); } -} // NameSpace - -#endif // UCENTRALGW_KAFKAMANAGER_H diff --git a/src/framework/Kafka_topics.h b/src/framework/KafkaTopics.h similarity index 100% rename from src/framework/Kafka_topics.h rename to src/framework/KafkaTopics.h diff --git a/src/framework/MicroService.cpp b/src/framework/MicroService.cpp deleted file mode 100644 index 8115c82..0000000 --- a/src/framework/MicroService.cpp +++ /dev/null @@ -1,532 +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 -#include - -#include "Poco/Util/Application.h" -#include "Poco/Util/ServerApplication.h" -#include "Poco/Util/Option.h" -#include "Poco/Util/OptionSet.h" -#include "Poco/Util/HelpFormatter.h" -#include "Poco/Environment.h" -#include "Poco/Net/HTTPSStreamFactory.h" -#include "Poco/Net/HTTPStreamFactory.h" -#include "Poco/Net/FTPSStreamFactory.h" -#include "Poco/Net/FTPStreamFactory.h" -#include "Poco/Path.h" -#include "Poco/File.h" -#include "Poco/String.h" -#include "Poco/JSON/Object.h" -#include "Poco/JSON/Parser.h" -#include "Poco/JSON/Stringifier.h" - -#include "framework/ALBHealthCheckServer.h" -#ifndef SMALL_BUILD -#include "KafkaManager.h" -#endif -#include "Kafka_topics.h" - -#include "MicroService.h" -#include "Utils.h" - -#ifndef TIP_SECURITY_SERVICE -#include "framework/AuthClient.h" -#endif - -namespace OpenWifi { - - void MyErrorHandler::exception(const Poco::Exception & E) { - Poco::Thread * CurrentThread = Poco::Thread::current(); - App_.logger().log(E); - App_.logger().error(Poco::format("Exception occurred in %s",CurrentThread->getName())); - } - - void MyErrorHandler::exception(const std::exception & E) { - Poco::Thread * CurrentThread = Poco::Thread::current(); - App_.logger().warning(Poco::format("std::exception on %s",CurrentThread->getName())); - } - - void MyErrorHandler::exception() { - Poco::Thread * CurrentThread = Poco::Thread::current(); - App_.logger().warning(Poco::format("exception on %s",CurrentThread->getName())); - } - - void MicroService::Exit(int Reason) { - std::exit(Reason); - } - - void MicroService::BusMessageReceived(const std::string &Key, const std::string & Message) { - std::lock_guard G(InfraMutex_); - try { - Poco::JSON::Parser P; - auto Object = P.parse(Message).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)) { - - if (Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE && Services_.find(ID) != Services_.end()) { - Services_[ID].LastUpdate = std::time(nullptr); - } else if (Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) { - Services_.erase(ID); - logger().information(Poco::format("Service %s ID=%Lu leaving system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); - } else if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN || Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE) { - logger().information(Poco::format("Service %s ID=%Lu joining system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); - Services_[ID] = 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 = (uint64_t)std::time(nullptr)}; - for (const auto &[Id, Svc] : Services_) { - logger().information(Poco::format("ID: %Lu Type: %s EndPoint: %s",Id,Svc.Type,Svc.PrivateEndPoint)); - } - } - } else { - logger().error(Poco::format("KAFKA-MSG: invalid event '%s', 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 { - logger().error(Poco::format("KAFKA-MSG: invalid event '%s', missing token",Event)); - } - } else { - logger().error(Poco::format("Unknown Event: %s Source: %Lu", Event, ID)); - } - } - } else { - logger().error("Bad bus message."); - } - - auto i=Services_.begin(); - auto Now = (uint64_t )std::time(nullptr); - for(;i!=Services_.end();) { - if((Now - i->second.LastUpdate)>60) { - i = Services_.erase(i); - } else - ++i; - } - - } catch (const Poco::Exception &E) { - logger().log(E); - } - } - - MicroServiceMetaVec MicroService::GetServices(const std::string & Type) { - std::lock_guard G(InfraMutex_); - - auto T = Poco::toLower(Type); - MicroServiceMetaVec Res; - for(const auto &[Id,ServiceRec]:Services_) { - if(ServiceRec.Type==T) - Res.push_back(ServiceRec); - } - return Res; - } - - MicroServiceMetaVec MicroService::GetServices() { - std::lock_guard G(InfraMutex_); - - MicroServiceMetaVec Res; - for(const auto &[Id,ServiceRec]:Services_) { - Res.push_back(ServiceRec); - } - return Res; - } - - void MicroService::LoadConfigurationFile() { - std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR,"."); - Poco::Path ConfigFile; - - ConfigFile = ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : 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()); - } - - void MicroService::Reload() { - LoadConfigurationFile(); - LoadMyConfig(); - } - - void MicroService::LoadMyConfig() { - std::string KeyFile = ConfigPath("openwifi.service.key"); - std::string KeyFilePassword = ConfigPath("openwifi.service.key.password" , "" ); - AppKey_ = Poco::SharedPtr(new Poco::Crypto::RSAKey("", KeyFile, KeyFilePassword)); - Cipher_ = CipherFactory_.createCipher(*AppKey_); - 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_ = CreateHash(MyPublicEndPoint_); - } - - void MicroService::initialize(Poco::Util::Application &self) { - // add the default services - SubSystems_.push_back(KafkaManager()); - SubSystems_.push_back(ALBHealthCheckServer()); - - Poco::Net::initializeSSL(); - Poco::Net::HTTPStreamFactory::registerFactory(); - Poco::Net::HTTPSStreamFactory::registerFactory(); - Poco::Net::FTPStreamFactory::registerFactory(); - Poco::Net::FTPSStreamFactory::registerFactory(); - - LoadConfigurationFile(); - - static const char * LogFilePathKey = "logging.channels.c2.path"; - - if(LogDir_.empty()) { - std::string OriginalLogFileValue = ConfigPath(LogFilePathKey); - config().setString(LogFilePathKey, OriginalLogFileValue); - } else { - config().setString(LogFilePathKey, LogDir_); - } - - Poco::File DataDir(ConfigPath("openwifi.system.data")); - DataDir_ = DataDir.path(); - if(!DataDir.exists()) { - try { - DataDir.createDirectory(); - } catch (const Poco::Exception &E) { - logger().log(E); - } - } - - LoadMyConfig(); - - InitializeSubSystemServers(); - ServerApplication::initialize(self); - - Types::TopicNotifyFunction F = [this](std::string s1,std::string s2) { this->BusMessageReceived(s1,s2); }; - 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(const std::string &name, const std::string &value) { - HelpRequested_ = true; - displayHelp(); - stopOptionsProcessing(); - } - - void MicroService::handleVersion(const std::string &name, const std::string &value) { - HelpRequested_ = true; - std::cout << Version() << std::endl; - stopOptionsProcessing(); - } - - void MicroService::handleDebug(const std::string &name, const std::string &value) { - if(value == "true") - DebugMode_ = true ; - } - - void MicroService::handleLogs(const std::string &name, const std::string &value) { - LogDir_ = value; - } - - void MicroService::handleConfig(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() { - for(auto i:SubSystems_) { - i->Start(); - } - BusEventManager_.Start(); - } - - void MicroService::StopSubSystemServers() { - BusEventManager_.Stop(); - for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) - (*i)->Stop(); - } - - std::string MicroService::CreateUUID() { - return UUIDGenerator_.create().toString(); - } - - 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 { - // std::cout << "Sub:" << SubSystem << " Level:" << Level << std::endl; - for (auto i : SubSystems_) { - if (Sub == Poco::toLower(i->Name())) { - i->Logger().setLevel(P); - return true; - } - } - } - } catch (const Poco::Exception & E) { - std::cout << "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) { - return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; - } - - std::string MicroService::Decrypt(const std::string &S) { - return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; - } - - std::string MicroService::CreateHash(const std::string &S) { - SHA2_.update(S); - return Utils::ToHex(SHA2_.digest()); - } - - 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(); - } - - void BusEventManager::run() { - Running_ = true; - auto Msg = Daemon()->MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN); - KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false); - while(Running_) { - Poco::Thread::trySleep((unsigned long)Daemon()->DaemonBusTimer()); - if(!Running_) - break; - Msg = Daemon()->MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE); - KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false); - } - Msg = Daemon()->MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_LEAVE); - KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false); - }; - - void BusEventManager::Start() { - if(KafkaManager()->Enabled()) { - Thread_.start(*this); - } - } - - void BusEventManager::Stop() { - if(KafkaManager()->Enabled()) { - Running_ = false; - Thread_.wakeUp(); - Thread_.join(); - } - } - - [[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(Daemon()->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(const ArgVec &args) { - - MyErrorHandler ErrorHandler(*this); - Poco::ErrorHandler::set(&ErrorHandler); - - if (!HelpRequested_) { - SavePID(); - Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME); - logger.notice(Poco::format("Starting %s version %s.",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(Poco::format("System ID set to %Lu",ID_)); - StartSubSystemServers(); - waitForTerminationRequest(); - StopSubSystemServers(); - - logger.notice(Poco::format("Stopped %s...",DAEMON_APP_NAME)); - } - - return Application::EXIT_OK; - } -} \ No newline at end of file diff --git a/src/framework/MicroService.h b/src/framework/MicroService.h index f99afda..57fb930 100644 --- a/src/framework/MicroService.h +++ b/src/framework/MicroService.h @@ -6,14 +6,22 @@ // Arilia Wireless Inc. // -#ifndef UCENTRALGW_MICROSERVICE_H -#define UCENTRALGW_MICROSERVICE_H +#ifndef OPENWIFI_MICROSERVICE_H +#define OPENWIFI_MICROSERVICE_H #include #include #include #include #include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; #include "Poco/Util/Application.h" #include "Poco/Util/ServerApplication.h" @@ -27,9 +35,787 @@ #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 "OpenWifiTypes.h" -#include "SubSystemServer.h" +#include "cppkafka/cppkafka.h" + +#include "framework/OpenWifiTypes.h" +#include "framework/KafkaTopics.h" +#include "framework/RESTAPI_protocol.h" +#include "framework/RESTAPI_errors.h" +#include "framework/uCentral_Protocol.h" +#include "RESTObjects/RESTAPI_SecurityObjects.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, const std::string & S) { + Obj.set(Field,S); + } + + inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::vector & S) { + Poco::JSON::Array Array; + for(const auto &i:S) { + Poco::JSON::Object O; + O.set("tag",i.first); + O.set("value", i.second); + } + Obj.set(Field,Array); + } + + 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, uint64_t V) { + Obj.set(Field,V); + } + + 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); + } + + + template void field_to_json(Poco::JSON::Object &Obj, + const char *Field, + const T &V, + std::function F) { + Obj.set(Field, F(V)); + } + + template bool field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, T & V, + std::function F) { + if(Obj->has(Field)) + V = F(Obj->get(Field).toString()); + return true; + } + + inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, std::string &S) { + if(Obj->has(Field)) + S = Obj->get(Field).toString(); + } + + inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, uint64_t &V) { + if(Obj->has(Field)) + V = Obj->get(Field); + } + + inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, bool &V) { + if(Obj->has(Field)) + V = (Obj->get(Field).toString() == "true"); + } + + inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, Types::StringPairVec &Vec) { + if(Obj->isArray(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(Poco::JSON::Object::Ptr Obj, const char *Field, Types::StringVec &V) { + if(Obj->isArray(Field)) { + V.clear(); + Poco::JSON::Array::Ptr A = Obj->getArray(Field); + for(const auto &i:*A) { + V.push_back(i.toString()); + } + } + } + + inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, Types::TagList &V) { + if(Obj->isArray(Field)) { + V.clear(); + Poco::JSON::Array::Ptr A = Obj->getArray(Field); + for(const auto &i:*A) { + V.push_back(i); + } + } + } + + 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 void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::vector &Value) { + if(Obj->isArray(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)) { + 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(); + } + + 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 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; + } + + 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 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 { + + 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 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; + } + + [[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, unsigned long size) { + std::string encoded; + encoded.reserve(((size / 3) + (size % 3 > 0)) * 4); + + std::uint32_t temp; + + std::size_t i; + + int ee = (int)(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 = Hours = 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) { + uint64_t R=0; + + for(const auto &i:S) + if(i>='0' && i<='9') { + R <<= 4; + R += (i-'0'); + } else if(i>='a' && i<='f') { + R <<= 4; + R += (i-'a') + 10 ; + } else if(i>='A' && i<='F') { + R <<= 4; + R += (i-'A') + 10 ; + } + return R; + } + + + [[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; + } + + inline void SaveSystemId(uint64_t Id); + + [[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) ; + SaveSystemId(S); + std::cout << "ID: " << S << std::endl; + return S; + } + + [[nodiscard]] inline uint64_t GetSystemId(); + + [[nodiscard]] inline bool ValidEMailAddress(const std::string &email) { + // define a regular expression + static const std::regex pattern + ("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+"); + + // 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(); + } + +} namespace OpenWifi { @@ -45,23 +831,1272 @@ namespace OpenWifi { class MyErrorHandler : public Poco::ErrorHandler { public: explicit MyErrorHandler(Poco::Util::Application &App) : App_(App) {} - void exception(const Poco::Exception & E) override; - void exception(const std::exception & E) override; - void exception() override; + inline void exception(const Poco::Exception & E) { + Poco::Thread * CurrentThread = Poco::Thread::current(); + App_.logger().log(E); + App_.logger().error(Poco::format("Exception occurred in %s",CurrentThread->getName())); + } + + inline void exception(const std::exception & E) { + Poco::Thread * CurrentThread = Poco::Thread::current(); + App_.logger().warning(Poco::format("std::exception on %s",CurrentThread->getName())); + } + + inline void exception() { + Poco::Thread * CurrentThread = Poco::Thread::current(); + App_.logger().warning(Poco::format("exception on %s",CurrentThread->getName())); + } private: Poco::Util::Application &App_; }; class BusEventManager : public Poco::Runnable { public: - void run() override; - void Start(); - void Stop(); + inline void run() final; + inline void Start(); + inline void Stop(); private: std::atomic_bool Running_ = false; Poco::Thread Thread_; }; + class MyPrivateKeyPassphraseHandler : public Poco::Net::PrivateKeyPassphraseHandler { + public: + explicit MyPrivateKeyPassphraseHandler(const std::string &Password, Poco::Logger & Logger): + PrivateKeyPassphraseHandler(true), + Logger_(Logger), + Password_(Password) {} + void onPrivateKeyRequested(const void * pSender,std::string & privateKey) { + Logger_.information("Returning key passphrase."); + privateKey = Password_; + }; + 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), key_file_(std::move(Key_file)), + cert_file_(std::move(Cert_file)), root_ca_(std::move(RootCa)), + issuer_cert_file_(std::move(Issuer)), client_cas_(std::move(ClientCas)), + cas_(std::move(Cas)), key_file_password_(std::move(Key_file_password)), + name_(std::move(Name)), level_(M), backlog_(backlog){}; + + [[nodiscard]] inline const std::string &Address() const { return address_; }; + [[nodiscard]] inline uint32_t Port() const { return port_; }; + [[nodiscard]] inline const std::string &KeyFile() const { return key_file_; }; + [[nodiscard]] inline const std::string &CertFile() const { return cert_file_; }; + [[nodiscard]] inline const std::string &RootCA() const { return root_ca_; }; + [[nodiscard]] inline const std::string &KeyFilePassword() const { return key_file_password_; }; + [[nodiscard]] inline const std::string &IssuerCertFile() const { return issuer_cert_file_; }; + [[nodiscard]] inline const std::string &Name() const { return name_; }; + [[nodiscard]] inline int Backlog() const { return backlog_; } + + [[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(Poco::format("Wrong Certificate(%s) for Key(%s)", 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(10); + 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); + } + } + + inline void LogCertInfo(Poco::Logger &L, const Poco::Crypto::X509Certificate &C) const { + L.information("============================================================================================="); + L.information(Poco::format("> Issuer: %s", C.issuerName())); + L.information("---------------------------------------------------------------------------------------------"); + L.information(Poco::format("> Common Name: %s", + C.issuerName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); + L.information(Poco::format("> Country: %s", + C.issuerName(Poco::Crypto::X509Certificate::NID_COUNTRY))); + L.information(Poco::format("> Locality: %s", + C.issuerName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); + L.information(Poco::format("> State/Prov: %s", + C.issuerName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); + L.information(Poco::format("> Org name: %s", + C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); + L.information( + Poco::format("> Org unit: %s", + C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); + L.information( + Poco::format("> Email: %s", + C.issuerName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); + L.information(Poco::format("> Serial#: %s", + C.issuerName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); + L.information("---------------------------------------------------------------------------------------------"); + L.information(Poco::format("> Subject: %s", C.subjectName())); + L.information("---------------------------------------------------------------------------------------------"); + L.information(Poco::format("> Common Name: %s", + C.subjectName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); + L.information(Poco::format("> Country: %s", + C.subjectName(Poco::Crypto::X509Certificate::NID_COUNTRY))); + L.information(Poco::format("> Locality: %s", + C.subjectName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); + L.information( + Poco::format("> State/Prov: %s", + C.subjectName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); + L.information( + Poco::format("> Org name: %s", + C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); + L.information( + Poco::format("> Org unit: %s", + C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); + L.information( + Poco::format("> Email: %s", + C.subjectName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); + L.information(Poco::format("> Serial#: %s", + C.subjectName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); + L.information("---------------------------------------------------------------------------------------------"); + L.information(Poco::format("> Signature Algo: %s", C.signatureAlgorithm())); + auto From = Poco::DateTimeFormatter::format(C.validFrom(), Poco::DateTimeFormat::HTTP_FORMAT); + L.information(Poco::format("> Valid from: %s", From)); + auto Expires = + Poco::DateTimeFormatter::format(C.expiresOn(), Poco::DateTimeFormat::HTTP_FORMAT); + L.information(Poco::format("> Expires on: %s", Expires)); + L.information(Poco::format("> Version: %d", (int)C.version())); + L.information(Poco::format("> Serial #: %s", C.serialNumber())); + L.information("============================================================================================="); + } + + inline void LogCert(Poco::Logger &L) const { + try { + Poco::Crypto::X509Certificate C(cert_file_); + L.information("============================================================================================="); + L.information("============================================================================================="); + L.information(Poco::format("Certificate Filename: %s", 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(Poco::format("Issues Certificate Filename: %s", 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(Poco::format("Client CAs Filename: %s", client_cas_)); + L.information("============================================================================================="); + auto i = 1; + for (const auto &C3 : Certs) { + L.information(Poco::format(" Index: %d", 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(Poco::format("CA Filename: %s", root_ca_)); + L.information("============================================================================================="); + auto i = 1; + for (const auto &C : Certs) { + L.information(Poco::format(" Index: %d", i)); + L.information("============================================================================================="); + LogCertInfo(L, C); + i++; + } + L.information("============================================================================================="); + } catch (const Poco::Exception &E) { + L.log(E); + } + } + + private: + std::string address_; + 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_; + uint32_t port_; + std::string name_; + int backlog_; + Poco::Net::Context::VerificationMode level_; + }; + + class SubSystemServer : public Poco::Util::Application::Subsystem { + public: + SubSystemServer(std::string Name, const std::string &LoggingPrefix, + std::string SubSystemConfigPrefix) + : Name_(std::move(Name)), Logger_(Poco::Logger::get(LoggingPrefix)), + SubSystemConfigPrefix_(std::move(SubSystemConfigPrefix)) { + Logger_.setLevel(Poco::Message::PRIO_NOTICE); + } + + inline void initialize(Poco::Util::Application &self) override; + inline void uninitialize() override { + } + inline void reinitialize(Poco::Util::Application &self) override { + Logger_.information("Reloading of this subsystem is not supported."); + } + inline void defineOptions(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() { return Logger_; }; + inline void SetLoggingLevel(Poco::Message::Priority NewPriority) { Logger_.setLevel(NewPriority); } + inline int GetLoggingLevel() { return Logger_.getLevel(); } + + virtual int Start() = 0; + virtual void Stop() = 0; + + protected: + std::recursive_mutex Mutex_; + Poco::Logger &Logger_; + std::string Name_; + std::vector ConfigServersList_; + 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 RESTAPIHandler : public Poco::Net::HTTPRequestHandler { + public: + struct QueryBlock { + uint64_t StartDate = 0 , EndDate = 0 , Offset = 0 , Limit = 0, LogType = 0 ; + std::string SerialNumber, Filter, Select; + bool Lifetime=false, LastOnly=false, Newest=false, CountOnly=false, AdditionalInfo=false; + }; + typedef std::map BindingMap; + + RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector Methods, RESTAPI_GenericServer & Server, bool Internal=false, bool AlwaysAuthorize=true) + : Bindings_(std::move(map)), Logger_(l), Methods_(std::move(Methods)), Server_(Server), Internal_(Internal), AlwaysAuthorize_(AlwaysAuthorize) {} + + inline void handleRequest(Poco::Net::HTTPServerRequest &RequestIn, + Poco::Net::HTTPServerResponse &ResponseIn) final { + try { + Request = &RequestIn; + Response = &ResponseIn; + + if (!ContinueProcessing()) + return; + + if (AlwaysAuthorize_ && !IsAuthorized()) + return; + + ParseParameters(); + if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_GET) + DoGet(); + else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_POST) + DoPost(); + else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) + DoDelete(); + else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_PUT) + DoPut(); + else + BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod); + return; + } catch (const Poco::Exception &E) { + Logger_.log(E); + BadRequest(RESTAPI::Errors::InternalError); + } + } + + inline const Poco::JSON::Object::Ptr & ParseStream() { + return IncomingParser_.parse(Request->stream()).extract(); + } + + inline static bool ParseBindings(const std::string & Request, const std::list & EndPoints, BindingMap &bindings) { + bindings.clear(); + std::vector PathItems = Utils::Split(Request, '/'); + + for(const auto &EndPoint:EndPoints) { + std::vector ParamItems = Utils::Split(EndPoint, '/'); + if (PathItems.size() != ParamItems.size()) + continue; + + bool Matched = true; + for (auto i = 0; i != PathItems.size() && Matched; 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; + } + } + } + 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; + } + + inline uint64_t GetParameter(const std::string &Name, const uint64_t Default) { + auto Hint = std::find_if(Parameters_.begin(),Parameters_.end(),[Name](const std::pair &S){ return S.first==Name; }); + if(Hint==Parameters_.end() || !is_number(Hint->second)) + return Default; + return std::stoull(Hint->second); + } + + inline bool GetBoolParameter(const std::string &Name, bool Default) { + auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](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_),[Name](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_),[Name](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_),[Name](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; + } + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + inline void AddCORS() { + auto Origin = Request->find("Origin"); + if (Origin != Request->end()) { + Response->set("Access-Control-Allow-Origin", Origin->second); + Response->set("Vary", "Origin"); + } else { + Response->set("Access-Control-Allow-Origin", "*"); + } + Response->set("Access-Control-Allow-Headers", "*"); + Response->set("Access-Control-Allow-Methods", MakeList(Methods_)); + Response->set("Access-Control-Max-Age", "86400"); + } + + inline void SetCommonHeaders(bool CloseConnection=false) { + Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1); + Response->setChunkedTransferEncoding(true); + Response->setContentType("application/json"); + if(CloseConnection) { + Response->set("Connection", "close"); + Response->setKeepAlive(false); + } else { + Response->setKeepAlive(true); + Response->set("Connection", "Keep-Alive"); + Response->set("Keep-Alive", "timeout=5, max=1000"); + } + } + + inline void ProcessOptions() { + AddCORS(); + SetCommonHeaders(); + Response->setContentLength(0); + Response->set("Access-Control-Allow-Credentials", "true"); + Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK); + Response->set("Vary", "Origin, Access-Control-Request-Headers, Access-Control-Request-Method"); + Response->send(); + } + + inline void PrepareResponse(Poco::Net::HTTPResponse::HTTPStatus Status = Poco::Net::HTTPResponse::HTTP_OK, + bool CloseConnection = false) { + Response->setStatus(Status); + AddCORS(); + SetCommonHeaders(CloseConnection); + } + + inline void BadRequest(const std::string & Reason) { + PrepareResponse(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",400); + ErrorObject.set("ErrorDetails",Request->getMethod()); + ErrorObject.set("ErrorDescription",Reason.empty() ? "Command is missing parameters or wrong values." : Reason) ; + std::ostream &Answer = Response->send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); + } + + inline void InternalError(const std::string & Reason = "") { + PrepareResponse(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",500); + ErrorObject.set("ErrorDetails",Request->getMethod()); + ErrorObject.set("ErrorDescription",Reason.empty() ? "Please try later or review the data submitted." : Reason) ; + std::ostream &Answer = Response->send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); + } + + inline void UnAuthorized(const std::string & Reason = "") { + PrepareResponse(Poco::Net::HTTPResponse::HTTP_FORBIDDEN); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",403); + ErrorObject.set("ErrorDetails",Request->getMethod()); + ErrorObject.set("ErrorDescription",Reason.empty() ? "No access allowed." : Reason) ; + 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()); + ErrorObject.set("ErrorDescription","This resource does not exist."); + std::ostream &Answer = Response->send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); + Logger_.debug(Poco::format("RES-NOTFOUND: User='%s@%s' Method='%s' Path='%s", + 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 SendFile(Poco::File & File, const std::string & UUID) { + 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", "private"); + Response->set("Pragma", "private"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + Response->set("Content-Length", std::to_string(File.getSize())); + AddCORS(); + Response->sendFile(File.path(),"application/octet-stream"); + } + + inline void SendFile(Poco::File & File) { + 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", "private"); + Response->set("Pragma", "private"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + AddCORS(); + Response->sendFile(File.path(),MT.ContentType); + } + + inline void SendFile(Poco::TemporaryFile &TempAvatar, const std::string &Type, const std::string & Name) { + 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", "private"); + Response->set("Pragma", "private"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + AddCORS(); + Response->sendFile(TempAvatar.path(),MT.ContentType); + } + + inline void SendHTMLFileBack(Poco::File & File, + const Types::StringPairVec & FormVars) { + Response->set("Pragma", "private"); + Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); + Response->set("Content-Length", std::to_string(File.getSize())); + AddCORS(); + auto FormContent = Utils::LoadFile(File.path()); + Utils::ReplaceVariables(FormContent, FormVars); + 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(); + + inline void ReturnObject(Poco::JSON::Object &Object) { + PrepareResponse(); + 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, 1); + QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100); + QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, ""); + QB_.Select = GetParameter(RESTAPI::Protocol::SELECT, ""); + 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); + + if(QB_.Offset<1) + QB_.Offset=1; + 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 ; + + protected: + BindingMap Bindings_; + Poco::URI::QueryParameters Parameters_; + Poco::Logger &Logger_; + std::string SessionToken_; + SecurityObjects::UserInfoAndPolicy UserInfo_; + std::vector Methods_; + QueryBlock QB_; + bool Internal_=false; + bool QueryBlockInitialized_=false; + Poco::Net::HTTPServerRequest *Request= nullptr; + Poco::Net::HTTPServerResponse *Response= nullptr; + bool AlwaysAuthorize_=true; + Poco::JSON::Parser IncomingParser_; + RESTAPI_GenericServer & Server_; + }; + + class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { + public: + RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server) + : RESTAPIHandler(bindings, L, std::vector{}, Server) {} + 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) { + 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, false); + } + + if constexpr (sizeof...(Args) == 0) { + return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server); + } else { + return RESTAPI_Router(RequestedPath, Bindings, Logger, Server); + } + } + + template + RESTAPIHandler * RESTAPI_Router_I(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, Poco::Logger & Logger, RESTAPI_GenericServer & Server) { + 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, true); + } + + if constexpr (sizeof...(Args) == 0) { + return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server); + } else { + return RESTAPI_Router_I(RequestedPath, Bindings, Logger, Server); + } + } + + class OpenAPIRequestGet { + public: + explicit OpenAPIRequestGet( std::string Type, + std::string EndPoint, + Types::StringPairVec & QueryData, + uint64_t msTimeout): + Type_(std::move(Type)), + EndPoint_(std::move(EndPoint)), + QueryData_(QueryData), + msTimeout_(msTimeout) {}; + inline int Do(Poco::JSON::Object::Ptr &ResponseObject); + private: + std::string Type_; + std::string EndPoint_; + Types::StringPairVec QueryData_; + uint64_t msTimeout_; + }; + + class OpenAPIRequestPut { + public: + explicit OpenAPIRequestPut( std::string Type, + std::string EndPoint, + Types::StringPairVec & QueryData, + Poco::JSON::Object Body, + uint64_t msTimeout): + Type_(std::move(Type)), + EndPoint_(std::move(EndPoint)), + QueryData_(QueryData), + msTimeout_(msTimeout), + Body_(std::move(Body)){}; + + inline int Do(Poco::JSON::Object::Ptr &ResponseObject); + + private: + std::string Type_; + std::string EndPoint_; + Types::StringPairVec QueryData_; + uint64_t msTimeout_; + Poco::JSON::Object Body_; + }; + + class OpenAPIRequestPost { + public: + explicit OpenAPIRequestPost( std::string Type, + std::string EndPoint, + Types::StringPairVec & QueryData, + Poco::JSON::Object Body, + uint64_t msTimeout): + Type_(std::move(Type)), + EndPoint_(std::move(EndPoint)), + QueryData_(QueryData), + msTimeout_(msTimeout), + Body_(std::move(Body)){}; + inline int Do(Poco::JSON::Object::Ptr &ResponseObject); + private: + std::string Type_; + std::string EndPoint_; + Types::StringPairVec QueryData_; + uint64_t msTimeout_; + Poco::JSON::Object Body_; + }; + + class KafkaManager : public SubSystemServer { + public: + struct KMessage { + std::string Topic, + Key, + PayLoad; + }; + + inline void initialize(Poco::Util::Application & self) override; + + static KafkaManager *instance() { + if(instance_== nullptr) + instance_ = new KafkaManager; + return instance_; + } + + inline int Start() override { + if(!KafkaEnabled_) + return 0; + ProducerThr_ = std::make_unique([this]() { this->ProducerThr(); }); + ConsumerThr_ = std::make_unique([this]() { this->ConsumerThr(); }); + return 0; + } + + inline void Stop() override { + if(KafkaEnabled_) { + ProducerRunning_ = ConsumerRunning_ = false; + ProducerThr_->join(); + ConsumerThr_->join(); + return; + } + } + + inline void ProducerThr(); + inline void ConsumerThr(); + + inline void PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage ) { + if(KafkaEnabled_) { + std::lock_guard G(Mutex_); + KMessage M{ + .Topic = topic, + .Key = key, + .PayLoad = WrapMessage ? WrapSystemId(PayLoad) : PayLoad }; + Queue_.push(M); + } + } + + [[nodiscard]] inline std::string WrapSystemId(const std::string & PayLoad) { + return std::move( SystemInfoWrapper_ + PayLoad + "}"); + } + + [[nodiscard]] inline bool Enabled() const { return KafkaEnabled_; } + + inline int RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { + if(KafkaEnabled_) { + 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_++; + } else { + return 0; + } + } + + inline void UnregisterTopicWatcher(const std::string &Topic, int Id) { + if(KafkaEnabled_) { + 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 WakeUp(); + + private: + static KafkaManager *instance_; + std::mutex ProducerMutex_; + std::mutex ConsumerMutex_; + bool KafkaEnabled_ = false; + std::atomic_bool ProducerRunning_ = false; + std::atomic_bool ConsumerRunning_ = false; + std::queue Queue_; + std::string SystemInfoWrapper_; + std::unique_ptr ConsumerThr_; + std::unique_ptr ProducerThr_; + int FunctionId_=1; + Types::NotifyTable Notifiers_; + std::unique_ptr Config_; + + inline void PartitionAssignment(const cppkafka::TopicPartitionList& partitions) { + Logger_.information(Poco::format("Partition assigned: %Lu...",(uint64_t )partitions.front().get_partition())); + } + inline void PartitionRevocation(const cppkafka::TopicPartitionList& partitions) { + Logger_.information(Poco::format("Partition revocation: %Lu...",(uint64_t )partitions.front().get_partition())); + } + + KafkaManager() noexcept: + SubSystemServer("KafkaManager", "KAFKA-SVR", "openwifi.kafka") + { + } + }; + + inline KafkaManager * KafkaManager() { return KafkaManager::instance(); } + inline class KafkaManager *KafkaManager::instance_ = nullptr; + + class AuthClient : public SubSystemServer { + public: + explicit AuthClient() noexcept: + SubSystemServer("Authentication", "AUTH-CLNT", "authentication") + { + } + + static AuthClient *instance() { + if (instance_ == nullptr) { + instance_ = new AuthClient; + } + return instance_; + } + + inline int Start() override { + return 0; + } + + inline void Stop() override { + } + + inline void RemovedCachedToken(const std::string &Token) { + std::lock_guard G(Mutex_); + UserCache_.erase(Token); + } + + inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) { + return ((T.expires_in_+T.created_)second.webtoken)) { + UInfo = User->second; + return true; + } else { + Types::StringPairVec QueryData; + QueryData.push_back(std::make_pair("token",SessionToken)); + OpenAPIRequestGet Req( uSERVICE_SECURITY, + "/api/v1/validateToken", + QueryData, + 5000); + Poco::JSON::Object::Ptr Response; + if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) { + if(Response->has("tokenInfo") && Response->has("userInfo")) { + SecurityObjects::UserInfoAndPolicy P; + P.from_json(Response); + UserCache_[SessionToken] = P; + UInfo = P; + } + return true; + } + + } + return false; + } + + inline bool IsTokenAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo) { + std::lock_guard G(Mutex_); + + auto User = UserCache_.find(SessionToken); + if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) { + UInfo = User->second; + return true; + } else { + Types::StringPairVec QueryData; + QueryData.push_back(std::make_pair("token",SessionToken)); + OpenAPIRequestGet Req(uSERVICE_SECURITY, + "/api/v1/validateToken", + QueryData, + 5000); + Poco::JSON::Object::Ptr Response; + if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) { + if(Response->has("tokenInfo") && Response->has("userInfo")) { + SecurityObjects::UserInfoAndPolicy P; + P.from_json(Response); + UserCache_[SessionToken] = P; + UInfo = P; + } + return true; + } + + } + return false; + } + + private: + static AuthClient *instance_; + OpenWifi::SecurityObjects::UserInfoCache UserCache_; + }; + + inline AuthClient * AuthClient() { return AuthClient::instance(); } + inline class AuthClient * AuthClient::instance_ = nullptr; + + class ALBRequestHandler: public Poco::Net::HTTPRequestHandler + /// Return a HTML document with the current date and time. + { + public: + explicit ALBRequestHandler(Poco::Logger & L) + : Logger_(L) + { + } + + void handleRequest(Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) + { + Logger_.information(Poco::format("ALB-REQUEST(%s): New ALB request.",Request.clientAddress().toString())); + 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 << "uCentralGW Alive and kicking!" ; + } + + private: + Poco::Logger & Logger_; + }; + + 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_); + else + return nullptr; + } + + private: + Poco::Logger &Logger_; + }; + + class ALBHealthCheckServer : public SubSystemServer { + public: + ALBHealthCheckServer() noexcept: + SubSystemServer("ALBHealthCheckServer", "ALB-SVR", "alb") + { + } + + static ALBHealthCheckServer *instance() { + if (instance_ == nullptr) { + instance_ = new ALBHealthCheckServer; + } + return instance_; + } + + inline int Start() override; + + inline void Stop() override { + if(Server_) + Server_->stop(); + } + + private: + static ALBHealthCheckServer *instance_; + std::unique_ptr Server_; + std::unique_ptr Socket_; + int Port_ = 0; + }; + + inline ALBHealthCheckServer * ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); } + inline class ALBHealthCheckServer * ALBHealthCheckServer::instance_ = nullptr; + struct MicroServiceMeta { uint64_t Id=0; std::string Type; @@ -72,8 +2107,10 @@ namespace OpenWifi { 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: @@ -82,78 +2119,77 @@ namespace OpenWifi { std::string ConfigVar, std::string AppName, uint64_t BusTimer, - Types::SubSystemVec Subsystems) : + SubSystemVec Subsystems) : DAEMON_PROPERTIES_FILENAME(std::move(PropFile)), DAEMON_ROOT_ENV_VAR(std::move(RootEnv)), DAEMON_CONFIG_ENV_VAR(std::move(ConfigVar)), DAEMON_APP_NAME(std::move(AppName)), DAEMON_BUS_TIMER(BusTimer), SubSystems_(std::move(Subsystems)) { + instance_ = this; } - int main(const ArgVec &args) override; - void initialize(Application &self) override; - void uninitialize() override; - void reinitialize(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(); - void Exit(int Reason); - bool SetSubsystemLogLevel(const std::string & SubSystem, const std::string & Level); [[nodiscard]] std::string Version() { return Version_; } [[nodiscard]] const Poco::SharedPtr & Key() { return AppKey_; } [[nodiscard]] inline const std::string & DataDir() { return DataDir_; } - [[nodiscard]] std::string CreateUUID(); [[nodiscard]] bool Debug() const { return DebugMode_; } [[nodiscard]] uint64_t ID() const { return ID_; } - [[nodiscard]] Types::StringVec GetSubSystems() const; - [[nodiscard]] Types::StringPairVec GetLogLevels() ; - [[nodiscard]] static const Types::StringVec & GetLogLevelNames(); - [[nodiscard]] std::string ConfigGetString(const std::string &Key,const std::string & Default); - [[nodiscard]] std::string ConfigGetString(const std::string &Key); - [[nodiscard]] std::string ConfigPath(const std::string &Key,const std::string & Default); - [[nodiscard]] std::string ConfigPath(const std::string &Key); - [[nodiscard]] uint64_t ConfigGetInt(const std::string &Key,uint64_t Default); - [[nodiscard]] uint64_t ConfigGetInt(const std::string &Key); - [[nodiscard]] uint64_t ConfigGetBool(const std::string &Key,bool Default); - [[nodiscard]] uint64_t ConfigGetBool(const std::string &Key); - [[nodiscard]] std::string Encrypt(const std::string &S); - [[nodiscard]] std::string Decrypt(const std::string &S); - [[nodiscard]] std::string CreateHash(const std::string &S); [[nodiscard]] std::string Hash() const { return MyHash_; }; [[nodiscard]] std::string ServiceType() const { return DAEMON_APP_NAME; }; [[nodiscard]] std::string PrivateEndPoint() const { return MyPrivateEndPoint_; }; [[nodiscard]] std::string PublicEndPoint() const { return MyPublicEndPoint_; }; - [[nodiscard]] std::string MakeSystemEventMessage( const std::string & Type ) const ; - [[nodiscard]] const Types::SubSystemVec & GetFullSubSystems() { return SubSystems_; } + [[nodiscard]] const SubSystemVec & GetFullSubSystems() { return SubSystems_; } inline uint64_t DaemonBusTimer() const { return DAEMON_BUS_TIMER; }; - - void BusMessageReceived( const std::string & Key, const std::string & Message); - [[nodiscard]] MicroServiceMetaVec GetServices(const std::string & type); - [[nodiscard]] MicroServiceMetaVec GetServices(); - [[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); [[nodiscard]] const std::string & AppName() { return DAEMON_APP_NAME; } - - static void SavePID(); 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_;}; - void Reload(const std::string &Name); // reload a subsystem - void Reload(); // reload the daemon itself - void LoadMyConfig(); - - void LoadConfigurationFile(); + inline void Exit(int Reason); + inline void BusMessageReceived(const std::string &Key, const std::string & Message); + 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]] 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 CreateHash(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_; } private: + static MicroService * instance_; bool HelpRequested_ = false; std::string LogDir_; std::string ConfigFileName_; @@ -162,7 +2198,7 @@ namespace OpenWifi { Poco::SharedPtr AppKey_ = nullptr; bool DebugMode_ = false; std::string DataDir_; - Types::SubSystemVec SubSystems_; + SubSystemVec SubSystems_; Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory(); Poco::Crypto::Cipher * Cipher_ = nullptr; Poco::SHA2Engine SHA2_; @@ -181,6 +2217,1180 @@ namespace OpenWifi { std::string DAEMON_APP_NAME; uint64_t DAEMON_BUS_TIMER; }; + + inline void MicroService::Exit(int Reason) { + std::exit(Reason); + } + + inline void MicroService::BusMessageReceived(const std::string &Key, const std::string & Message) { + std::lock_guard G(InfraMutex_); + try { + Poco::JSON::Parser P; + auto Object = P.parse(Message).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)) { + + if (Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE && Services_.find(ID) != Services_.end()) { + Services_[ID].LastUpdate = std::time(nullptr); + } else if (Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) { + Services_.erase(ID); + logger().information(Poco::format("Service %s ID=%Lu leaving system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); + } else if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN || Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE) { + logger().information(Poco::format("Service %s ID=%Lu joining system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); + Services_[ID] = 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 = (uint64_t)std::time(nullptr)}; + for (const auto &[Id, Svc] : Services_) { + logger().information(Poco::format("ID: %Lu Type: %s EndPoint: %s",Id,Svc.Type,Svc.PrivateEndPoint)); + } + } + } else { + logger().error(Poco::format("KAFKA-MSG: invalid event '%s', 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 { + logger().error(Poco::format("KAFKA-MSG: invalid event '%s', missing token",Event)); + } + } else { + logger().error(Poco::format("Unknown Event: %s Source: %Lu", Event, ID)); + } + } + } else { + logger().error("Bad bus message."); + } + + auto i=Services_.begin(); + auto Now = (uint64_t )std::time(nullptr); + 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 &[Id,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 &[Id,ServiceRec]:Services_) { + Res.push_back(ServiceRec); + } + return Res; + } + + inline void MicroService::LoadConfigurationFile() { + std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR,"."); + Poco::Path ConfigFile; + + ConfigFile = ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : 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()); + } + + inline void MicroService::Reload() { + LoadConfigurationFile(); + LoadMyConfig(); + } + + inline void MicroService::LoadMyConfig() { + std::string KeyFile = ConfigPath("openwifi.service.key"); + std::string KeyFilePassword = ConfigPath("openwifi.service.key.password" , "" ); + AppKey_ = Poco::SharedPtr(new Poco::Crypto::RSAKey("", KeyFile, KeyFilePassword)); + Cipher_ = CipherFactory_.createCipher(*AppKey_); + 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_ = CreateHash(MyPublicEndPoint_); + } + + inline void MicroService::initialize(Poco::Util::Application &self) { + // add the default services + SubSystems_.push_back(KafkaManager()); + SubSystems_.push_back(ALBHealthCheckServer()); + + Poco::Net::initializeSSL(); + Poco::Net::HTTPStreamFactory::registerFactory(); + Poco::Net::HTTPSStreamFactory::registerFactory(); + Poco::Net::FTPStreamFactory::registerFactory(); + Poco::Net::FTPSStreamFactory::registerFactory(); + + LoadConfigurationFile(); + + static const char * LogFilePathKey = "logging.channels.c2.path"; + + if(LogDir_.empty()) { + std::string OriginalLogFileValue = ConfigPath(LogFilePathKey); + config().setString(LogFilePathKey, OriginalLogFileValue); + } else { + config().setString(LogFilePathKey, LogDir_); + } + + Poco::File DataDir(ConfigPath("openwifi.system.data")); + DataDir_ = DataDir.path(); + if(!DataDir.exists()) { + try { + DataDir.createDirectory(); + } catch (const Poco::Exception &E) { + logger().log(E); + } + } + + LoadMyConfig(); + + InitializeSubSystemServers(); + ServerApplication::initialize(self); + + Types::TopicNotifyFunction F = [this](std::string s1,std::string s2) { this->BusMessageReceived(s1,s2); }; + 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(const std::string &name, const std::string &value) { + HelpRequested_ = true; + displayHelp(); + stopOptionsProcessing(); + } + + inline void MicroService::handleVersion(const std::string &name, const std::string &value) { + HelpRequested_ = true; + std::cout << Version() << std::endl; + stopOptionsProcessing(); + } + + inline void MicroService::handleDebug(const std::string &name, const std::string &value) { + if(value == "true") + DebugMode_ = true ; + } + + inline void MicroService::handleLogs(const std::string &name, const std::string &value) { + LogDir_ = value; + } + + inline void MicroService::handleConfig(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() { + for(auto i:SubSystems_) { + i->Start(); + } + BusEventManager_.Start(); + } + + inline void MicroService::StopSubSystemServers() { + BusEventManager_.Stop(); + for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) + (*i)->Stop(); + } + + [[nodiscard]] inline std::string MicroService::CreateUUID() { + return UUIDGenerator_.create().toString(); + } + + 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 { + // std::cout << "Sub:" << SubSystem << " Level:" << Level << std::endl; + for (auto i : SubSystems_) { + if (Sub == Poco::toLower(i->Name())) { + i->Logger().setLevel(P); + return true; + } + } + } + } catch (const Poco::Exception & E) { + std::cout << "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) { + return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; + } + + inline std::string MicroService::Decrypt(const std::string &S) { + return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; + } + + inline std::string MicroService::CreateHash(const std::string &S) { + SHA2_.update(S); + return Utils::ToHex(SHA2_.digest()); + } + + 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 int MicroService::main(const ArgVec &args) { + + MyErrorHandler ErrorHandler(*this); + Poco::ErrorHandler::set(&ErrorHandler); + + if (!HelpRequested_) { + SavePID(); + Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME); + logger.notice(Poco::format("Starting %s version %s.",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(Poco::format("System ID set to %Lu",ID_)); + StartSubSystemServers(); + waitForTerminationRequest(); + StopSubSystemServers(); + + logger.notice(Poco::format("Stopped %s...",DAEMON_APP_NAME)); + } + + return Application::EXIT_OK; + } + + inline void SubSystemServer::initialize(Poco::Util::Application &self) { + Logger_.notice("Initializing..."); + auto i = 0; + bool good = true; + + 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)) { + Port_ = (int)MicroService::instance().ConfigGetInt("alb.port",15015); + Socket_ = std::make_unique(Port_); + auto Params = new Poco::Net::HTTPServerParams; + Server_ = std::make_unique(new ALBRequestHandlerFactory(Logger_), *Socket_, Params); + Server_->start(); + } + + return 0; + } + + inline void BusEventManager::run() { + Running_ = true; + 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()) { + Running_ = false; + Thread_.wakeUp(); + Thread_.join(); + } + } + + inline void KafkaManager::initialize(Poco::Util::Application & self) { + SubSystemServer::initialize(self); + KafkaEnabled_ = MicroService::instance().ConfigGetBool("openwifi.kafka.enable",false); + } + + inline void KafkaManager::ProducerThr() { + cppkafka::Configuration Config({ + { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, + { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") } + }); + 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); + ProducerRunning_ = true; + while(ProducerRunning_) { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + try + { + std::lock_guard G(ProducerMutex_); + auto Num=0; + while (!Queue_.empty()) { + const auto M = Queue_.front(); + Producer.produce( + cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad)); + Queue_.pop(); + Num++; + } + if(Num) + Producer.flush(); + } catch (const cppkafka::HandleException &E ) { + Logger_.warning(Poco::format("Caught a Kafka exception (producer): %s",std::string{E.what()})); + } catch (const Poco::Exception &E) { + Logger_.log(E); + } + } + } + + inline void KafkaManager::ConsumerThr() { + 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 } + }); + + 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([this](cppkafka::TopicPartitionList& partitions) { + if(!partitions.empty()) { + Logger_.information(Poco::format("Partition assigned: %Lu...", + (uint64_t)partitions.front().get_partition())); + } + }); + Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) { + if(!partitions.empty()) { + Logger_.information(Poco::format("Partition revocation: %Lu...", + (uint64_t)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; + for(const auto &i:Notifiers_) + Topics.push_back(i.first); + + Consumer.subscribe(Topics); + + ConsumerRunning_ = true; + while(ConsumerRunning_) { + try { + std::vector MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(200)); + for(auto const &Msg:MsgVec) { + if (!Msg) + continue; + if (Msg.get_error()) { + if (!Msg.is_eof()) { + Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string())); + }if(!AutoCommit) + Consumer.async_commit(Msg); + continue; + } + std::lock_guard G(ConsumerMutex_); + auto It = Notifiers_.find(Msg.get_topic()); + if (It != Notifiers_.end()) { + Types::TopicNotifyFunctionList &FL = It->second; + std::string Key{Msg.get_key()}; + std::string Payload{Msg.get_payload()}; + for (auto &F : FL) { + std::thread T(F.first, Key, Payload); + T.detach(); + } + } + if (!AutoCommit) + Consumer.async_commit(Msg); + } + } catch (const cppkafka::HandleException &E) { + Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()})); + } catch (const Poco::Exception &E) { + Logger_.log(E); + } + } + } + + class RESTAPI_system_command : public RESTAPIHandler { + public: + RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, bool Internal) + : RESTAPIHandler(bindings, L, + std::vector{Poco::Net::HTTPRequest::HTTP_POST, + Poco::Net::HTTPRequest::HTTP_GET, + Poco::Net::HTTPRequest::HTTP_OPTIONS}, + Server, + Internal) {} + static const std::list 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()) { + auto InsertResult = CertNames.insert(CertFileName); + if(InsertResult.second) { + Poco::JSON::Object Inner; + Inner.set("filename", CertFileName); + 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); + } + BadRequest(RESTAPI::Errors::InvalidCommand); + } + + inline void DoPost() final { + auto Obj = ParseStream(); + 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( + Poco::format("Setting log level for %s at %s", 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 int OpenAPIRequestGet::Do(Poco::JSON::Object::Ptr &ResponseObject) { + try { + auto Services = MicroService::instance().GetServices(Type_); + for(auto const &Svc:Services) { + Poco::URI URI(Svc.PrivateEndPoint); + Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); + + URI.setPath(EndPoint_); + for (const auto &qp : QueryData_) + URI.addQueryParameter(qp.first, qp.second); + + std::string Path(URI.getPathAndQuery()); + Session.setTimeout(Poco::Timespan(msTimeout_/1000, msTimeout_ % 1000)); + + Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET, + Path, + Poco::Net::HTTPMessage::HTTP_1_1); + Request.add("X-API-KEY", Svc.AccessKey); + Request.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); + 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) + { + std::cerr << E.displayText() << std::endl; + } + return -1; + } + + inline int OpenAPIRequestPut::Do(Poco::JSON::Object::Ptr &ResponseObject) { + try { + auto Services = MicroService::instance().GetServices(Type_); + for(auto const &Svc:Services) { + Poco::URI URI(Svc.PrivateEndPoint); + Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); + + URI.setPath(EndPoint_); + for (const auto &qp : QueryData_) + URI.addQueryParameter(qp.first, qp.second); + + std::string Path(URI.getPathAndQuery()); + Session.setTimeout(Poco::Timespan(msTimeout_/1000, msTimeout_ % 1000)); + + 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()); + + Request.add("X-API-KEY", Svc.AccessKey); + Request.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); + + 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(); + // std::cout << "Response OK" << std::endl; + } else { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + // std::cout << "Response: " << Response.getStatus() << std::endl; + } + return Response.getStatus(); + } + } + catch (const Poco::Exception &E) + { + std::cerr << E.displayText() << std::endl; + } + return -1; + } + + int OpenAPIRequestPost::Do(Poco::JSON::Object::Ptr &ResponseObject) { + try { + auto Services = MicroService::instance().GetServices(Type_); + for(auto const &Svc:Services) { + Poco::URI URI(Svc.PrivateEndPoint); + Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); + + URI.setPath(EndPoint_); + for (const auto &qp : QueryData_) + URI.addQueryParameter(qp.first, qp.second); + + std::string Path(URI.getPathAndQuery()); + Session.setTimeout(Poco::Timespan(msTimeout_/1000, msTimeout_ % 1000)); + + 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()); + + Request.add("X-API-KEY", Svc.AccessKey); + Request.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); + + 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(); + // std::cout << "Response OK" << std::endl; + } else { + Poco::JSON::Parser P; + ResponseObject = P.parse(is).extract(); + // std::cout << "Response: " << Response.getStatus() << std::endl; + } + return Response.getStatus(); + } + } + catch (const Poco::Exception &E) + { + std::cerr << E.displayText() << std::endl; + } + return -1; + } + + + 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 ); +#endif + inline bool RESTAPIHandler::IsAuthorized() { + if(Internal_) { + auto Allowed = MicroService::instance().IsValidAPIKEY(*Request); + if(!Allowed) { + if(Server_.LogBadTokens(false)) { + Logger_.debug(Poco::format("I-REQ-DENIED(%s): Method='%s' Path='%s", + Utils::FormatIPv6(Request->clientAddress().toString()), + Request->getMethod(), Request->getURI())); + } + } else { + auto Id = Request->get("X-INTERNAL-NAME", "unknown"); + if(Server_.LogIt(Request->getMethod(),true)) { + Logger_.debug(Poco::format("I-REQ-ALLOWED(%s): User='%s' Method='%s' Path='%s", + Utils::FormatIPv6(Request->clientAddress().toString()), 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_)) { +#else + if (AuthClient()->IsAuthorized(*Request, SessionToken_, UserInfo_)) { +#endif + if(Server_.LogIt(Request->getMethod(),true)) { + Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s", + UserInfo_.userinfo.email, + Utils::FormatIPv6(Request->clientAddress().toString()), + Request->clientAddress().toString(), + Request->getMethod(), + Request->getURI())); + } + return true; + } else { + if(Server_.LogBadTokens(true)) { + Logger_.debug(Poco::format("X-REQ-DENIED(%s): Method='%s' Path='%s", + Utils::FormatIPv6(Request->clientAddress().toString()), + Request->getMethod(), Request->getURI())); + } + UnAuthorized(); + } + return false; + } + } + + inline MicroService * MicroService::instance_ = nullptr; +} + +namespace OpenWifi::Utils { + inline void SaveSystemId(uint64_t Id) { + try { + std::ofstream O; + O.open(MicroService::instance().DataDir() + "/system.id",std::ios::binary | std::ios::trunc); + O << Id; + O.close(); + } catch (...) + { + std::cout << "Could not save system ID" << std::endl; + } + } + + [[nodiscard]] inline uint64_t GetSystemId() { + uint64_t ID=0; + + // if the system ID file exists, open and read it. + Poco::File SID( MicroService::instance().DataDir() + "/system.id"); + try { + if (SID.exists()) { + std::ifstream I; + I.open(SID.path()); + I >> ID; + I.close(); + if (ID == 0) + return InitializeSystemId(); + return ID; + } else { + return InitializeSystemId(); + } + } catch (...) { + return InitializeSystemId(); + } + } + + 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); + } + } + } #endif // UCENTRALGW_MICROSERVICE_H diff --git a/src/framework/OpenAPIRequest.cpp b/src/framework/OpenAPIRequest.cpp deleted file mode 100644 index f438947..0000000 --- a/src/framework/OpenAPIRequest.cpp +++ /dev/null @@ -1,162 +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 - -#include "OpenAPIRequest.h" - -#include "Poco/Net/HTTPSClientSession.h" -#include -#include -#include -#include -#include -#include "Utils.h" -#include "Daemon.h" - -namespace OpenWifi { - - int OpenAPIRequestGet::Do(Poco::JSON::Object::Ptr &ResponseObject) { - try { - auto Services = Daemon()->GetServices(Type_); - for(auto const &Svc:Services) { - Poco::URI URI(Svc.PrivateEndPoint); - Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); - - URI.setPath(EndPoint_); - for (const auto &qp : QueryData_) - URI.addQueryParameter(qp.first, qp.second); - - std::string Path(URI.getPathAndQuery()); - Session.setTimeout(Poco::Timespan(msTimeout_/1000, msTimeout_ % 1000)); - - Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET, - Path, - Poco::Net::HTTPMessage::HTTP_1_1); - Request.add("X-API-KEY", Svc.AccessKey); - Request.add("X-INTERNAL-NAME", Daemon()->PublicEndPoint()); - 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) - { - std::cerr << E.displayText() << std::endl; - } - return -1; - } - - int OpenAPIRequestPut::Do(Poco::JSON::Object::Ptr &ResponseObject) { - try { - auto Services = Daemon()->GetServices(Type_); - for(auto const &Svc:Services) { - Poco::URI URI(Svc.PrivateEndPoint); - Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); - - URI.setPath(EndPoint_); - for (const auto &qp : QueryData_) - URI.addQueryParameter(qp.first, qp.second); - - std::string Path(URI.getPathAndQuery()); - Session.setTimeout(Poco::Timespan(msTimeout_/1000, msTimeout_ % 1000)); - - 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()); - - Request.add("X-API-KEY", Svc.AccessKey); - Request.add("X-INTERNAL-NAME", Daemon()->PublicEndPoint()); - - 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(); -// std::cout << "Response OK" << std::endl; - } else { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); -// std::cout << "Response: " << Response.getStatus() << std::endl; - } - return Response.getStatus(); - } - } - catch (const Poco::Exception &E) - { - std::cerr << E.displayText() << std::endl; - } - return -1; - } - - int OpenAPIRequestPost::Do(Poco::JSON::Object::Ptr &ResponseObject) { - try { - auto Services = Daemon()->GetServices(Type_); - for(auto const &Svc:Services) { - Poco::URI URI(Svc.PrivateEndPoint); - Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); - - URI.setPath(EndPoint_); - for (const auto &qp : QueryData_) - URI.addQueryParameter(qp.first, qp.second); - - std::string Path(URI.getPathAndQuery()); - Session.setTimeout(Poco::Timespan(msTimeout_/1000, msTimeout_ % 1000)); - - 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()); - - Request.add("X-API-KEY", Svc.AccessKey); - Request.add("X-INTERNAL-NAME", Daemon()->PublicEndPoint()); - - 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(); - // std::cout << "Response OK" << std::endl; - } else { - Poco::JSON::Parser P; - ResponseObject = P.parse(is).extract(); - // std::cout << "Response: " << Response.getStatus() << std::endl; - } - return Response.getStatus(); - } - } - catch (const Poco::Exception &E) - { - std::cerr << E.displayText() << std::endl; - } - return -1; - } - -} diff --git a/src/framework/OpenAPIRequest.h b/src/framework/OpenAPIRequest.h deleted file mode 100644 index abb2017..0000000 --- a/src/framework/OpenAPIRequest.h +++ /dev/null @@ -1,79 +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. -// - -#ifndef UCENTRALGW_OPENAPIREQUEST_H -#define UCENTRALGW_OPENAPIREQUEST_H - -#include "Poco/JSON/Object.h" - -#include "OpenWifiTypes.h" - -namespace OpenWifi { - - class OpenAPIRequestGet { - public: - explicit OpenAPIRequestGet( std::string Type, - std::string EndPoint, - Types::StringPairVec & QueryData, - uint64_t msTimeout): - Type_(std::move(Type)), - EndPoint_(std::move(EndPoint)), - QueryData_(QueryData), - msTimeout_(msTimeout) {}; - int Do(Poco::JSON::Object::Ptr &ResponseObject); - private: - std::string Type_; - std::string EndPoint_; - Types::StringPairVec QueryData_; - uint64_t msTimeout_; - }; - - class OpenAPIRequestPut { - public: - explicit OpenAPIRequestPut( std::string Type, - std::string EndPoint, - Types::StringPairVec & QueryData, - Poco::JSON::Object Body, - uint64_t msTimeout): - Type_(std::move(Type)), - EndPoint_(std::move(EndPoint)), - QueryData_(QueryData), - msTimeout_(msTimeout), - Body_(std::move(Body)){}; - int Do(Poco::JSON::Object::Ptr &ResponseObject); - private: - std::string Type_; - std::string EndPoint_; - Types::StringPairVec QueryData_; - uint64_t msTimeout_; - Poco::JSON::Object Body_; - }; - - class OpenAPIRequestPost { - public: - explicit OpenAPIRequestPost( std::string Type, - std::string EndPoint, - Types::StringPairVec & QueryData, - Poco::JSON::Object Body, - uint64_t msTimeout): - Type_(std::move(Type)), - EndPoint_(std::move(EndPoint)), - QueryData_(QueryData), - msTimeout_(msTimeout), - Body_(std::move(Body)){}; - int Do(Poco::JSON::Object::Ptr &ResponseObject); - private: - std::string Type_; - std::string EndPoint_; - Types::StringPairVec QueryData_; - uint64_t msTimeout_; - Poco::JSON::Object Body_; - }; -} - -#endif // UCENTRALGW_OPENAPIREQUEST_H diff --git a/src/framework/OpenWifiTypes.h b/src/framework/OpenWifiTypes.h index 942114e..8952de6 100644 --- a/src/framework/OpenWifiTypes.h +++ b/src/framework/OpenWifiTypes.h @@ -9,8 +9,6 @@ #ifndef UCENTRALGW_UCENTRALTYPES_H #define UCENTRALGW_UCENTRALTYPES_H -#include "SubSystemServer.h" - #include #include #include @@ -29,7 +27,6 @@ namespace OpenWifi::Types { typedef std::queue StringPairQueue; typedef std::vector StringVec; typedef std::set StringSet; - typedef std::vector SubSystemVec; typedef std::map> StringMapStringSet; typedef std::function TopicNotifyFunction; typedef std::list> TopicNotifyFunctionList; diff --git a/src/framework/RESTAPI_GenericServer.cpp b/src/framework/RESTAPI_GenericServer.cpp deleted file mode 100644 index 6dc9356..0000000 --- a/src/framework/RESTAPI_GenericServer.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by stephane bourque on 2021-09-15. -// - -#include "RESTAPI_GenericServer.h" diff --git a/src/framework/RESTAPI_GenericServer.h b/src/framework/RESTAPI_GenericServer.h deleted file mode 100644 index a97d0e1..0000000 --- a/src/framework/RESTAPI_GenericServer.h +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by stephane bourque on 2021-09-15. -// - -#ifndef OWPROV_RESTAPI_GENERICSERVER_H -#define OWPROV_RESTAPI_GENERICSERVER_H - -#include -#include - -#include "Daemon.h" -#include "Poco/StringTokenizer.h" -#include "Poco/Net/HTTPRequest.h" - -namespace OpenWifi { - - 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() { - std::string Public = Daemon()->ConfigGetString("apilogging.public.methods","PUT,POST,DELETE"); - SetFlags(true, Public); - std::string Private = Daemon()->ConfigGetString("apilogging.private.methods","PUT,POST,DELETE"); - SetFlags(false, Private); - - std::string PublicBadTokens = Daemon()->ConfigGetString("apilogging.public.badtokens.methods",""); - LogBadTokens_[0] = (Poco::icompare(PublicBadTokens,"true")==0); - std::string PrivateBadTokens = Daemon()->ConfigGetString("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}; - }; - -} - - -#endif //OWPROV_RESTAPI_GENERICSERVER_H diff --git a/src/framework/RESTAPI_handler.cpp b/src/framework/RESTAPI_handler.cpp deleted file mode 100644 index 33e1a47..0000000 --- a/src/framework/RESTAPI_handler.cpp +++ /dev/null @@ -1,489 +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 -#include -#include -#include -#include -#include -#include - -#include "Poco/URI.h" -#include "Poco/Net/OAuth20Credentials.h" - -#include "RESTAPI_errors.h" - -#ifdef TIP_SECURITY_SERVICE -#include "AuthService.h" -#else -#include "framework/AuthClient.h" -#endif - -#include "RESTAPI_handler.h" -#include "RESTAPI_protocol.h" -#include "Utils.h" -#include "Daemon.h" - -namespace OpenWifi { - - void RESTAPIHandler::handleRequest(Poco::Net::HTTPServerRequest &RequestIn, - Poco::Net::HTTPServerResponse &ResponseIn) { - try { - Request = &RequestIn; - Response = &ResponseIn; - - if (!ContinueProcessing()) - return; - - if (AlwaysAuthorize_ && !IsAuthorized()) - return; - - ParseParameters(); - if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_GET) - DoGet(); - else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_POST) - DoPost(); - else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) - DoDelete(); - else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_PUT) - DoPut(); - else - BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod); - return; - } catch (const Poco::Exception &E) { - Logger_.log(E); - BadRequest(RESTAPI::Errors::InternalError); - } - } - - const Poco::JSON::Object::Ptr &RESTAPIHandler::ParseStream() { - return IncomingParser_.parse(Request->stream()).extract(); - } - - bool RESTAPIHandler::ParseBindings(const std::string & Request, const std::list & EndPoints, BindingMap &bindings) { - bindings.clear(); - std::vector PathItems = Utils::Split(Request, '/'); - - for(const auto &EndPoint:EndPoints) { - std::vector ParamItems = Utils::Split(EndPoint, '/'); - if (PathItems.size() != ParamItems.size()) - continue; - - bool Matched = true; - for (auto i = 0; i != PathItems.size() && Matched; 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; - } - } - } - if(Matched) - return true; - } - return false; - } - - void RESTAPIHandler::PrintBindings() { - for (const auto &[key, value] : Bindings_) - std::cout << "Key = " << key << " Value= " << value << std::endl; - } - - void RESTAPIHandler::ParseParameters() { - Poco::URI uri(Request->getURI()); - Parameters_ = uri.getQueryParameters(); - InitQueryBlock(); - } - - static bool is_number(const std::string &s) { - return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); - } - - static bool is_bool(const std::string &s) { - if (s == "true" || s == "false") - return true; - return false; - } - - uint64_t RESTAPIHandler::GetParameter(const std::string &Name, const uint64_t Default) { - auto Hint = std::find_if(Parameters_.begin(),Parameters_.end(),[Name](const std::pair &S){ return S.first==Name; }); - if(Hint==Parameters_.end() || !is_number(Hint->second)) - return Default; - return std::stoull(Hint->second); - } - - bool RESTAPIHandler::GetBoolParameter(const std::string &Name, bool Default) { - auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair &S){ return S.first==Name; }); - if(Hint==end(Parameters_) || !is_bool(Hint->second)) - return Default; - return Hint->second=="true"; - } - - std::string RESTAPIHandler::GetParameter(const std::string &Name, const std::string &Default) { - auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair &S){ return S.first==Name; }); - if(Hint==end(Parameters_)) - return Default; - return Hint->second; - } - - bool RESTAPIHandler::HasParameter(const std::string &Name, std::string &Value) { - auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair &S){ return S.first==Name; }); - if(Hint==end(Parameters_)) - return false; - Value = Hint->second; - return true; - } - - bool RESTAPIHandler::HasParameter(const std::string &Name, uint64_t & Value) { - auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair &S){ return S.first==Name; }); - if(Hint==end(Parameters_)) - return false; - Value = std::stoull(Hint->second); - return true; - } - - const std::string &RESTAPIHandler::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; - } - - 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; - } - - bool RESTAPIHandler::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; - } - - bool RESTAPIHandler::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; - } - - bool RESTAPIHandler::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; - } - - void RESTAPIHandler::AddCORS() { - auto Origin = Request->find("Origin"); - if (Origin != Request->end()) { - Response->set("Access-Control-Allow-Origin", Origin->second); - Response->set("Vary", "Origin"); - } else { - Response->set("Access-Control-Allow-Origin", "*"); - } - Response->set("Access-Control-Allow-Headers", "*"); - Response->set("Access-Control-Allow-Methods", MakeList(Methods_)); - Response->set("Access-Control-Max-Age", "86400"); - } - - void RESTAPIHandler::SetCommonHeaders(bool CloseConnection) { - Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1); - Response->setChunkedTransferEncoding(true); - Response->setContentType("application/json"); - if(CloseConnection) { - Response->set("Connection", "close"); - Response->setKeepAlive(false); - } else { - Response->setKeepAlive(true); - Response->set("Connection", "Keep-Alive"); - Response->set("Keep-Alive", "timeout=5, max=1000"); - } - } - - void RESTAPIHandler::ProcessOptions() { - AddCORS(); - SetCommonHeaders(); - Response->setContentLength(0); - Response->set("Access-Control-Allow-Credentials", "true"); - Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK); - Response->set("Vary", "Origin, Access-Control-Request-Headers, Access-Control-Request-Method"); - Response->send(); - } - - void RESTAPIHandler::PrepareResponse( Poco::Net::HTTPResponse::HTTPStatus Status, - bool CloseConnection) { - Response->setStatus(Status); - AddCORS(); - SetCommonHeaders(CloseConnection); - } - - void RESTAPIHandler::BadRequest(const std::string & Reason) { - PrepareResponse(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST); - Poco::JSON::Object ErrorObject; - ErrorObject.set("ErrorCode",400); - ErrorObject.set("ErrorDetails",Request->getMethod()); - ErrorObject.set("ErrorDescription",Reason.empty() ? "Command is missing parameters or wrong values." : Reason) ; - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - } - - void RESTAPIHandler::InternalError(const std::string & Reason) { - PrepareResponse(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); - Poco::JSON::Object ErrorObject; - ErrorObject.set("ErrorCode",500); - ErrorObject.set("ErrorDetails",Request->getMethod()); - ErrorObject.set("ErrorDescription",Reason.empty() ? "Please try later or review the data submitted." : Reason) ; - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - } - - void RESTAPIHandler::UnAuthorized(const std::string & Reason) { - PrepareResponse(Poco::Net::HTTPResponse::HTTP_FORBIDDEN); - Poco::JSON::Object ErrorObject; - ErrorObject.set("ErrorCode",403); - ErrorObject.set("ErrorDetails",Request->getMethod()); - ErrorObject.set("ErrorDescription",Reason.empty() ? "No access allowed." : Reason) ; - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - } - - void RESTAPIHandler::NotFound() { - PrepareResponse(Poco::Net::HTTPResponse::HTTP_NOT_FOUND); - Poco::JSON::Object ErrorObject; - ErrorObject.set("ErrorCode",404); - ErrorObject.set("ErrorDetails",Request->getMethod()); - ErrorObject.set("ErrorDescription","This resource does not exist."); - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(ErrorObject, Answer); - Logger_.debug(Poco::format("RES-NOTFOUND: User='%s@%s' Method='%s' Path='%s", - UserInfo_.userinfo.email, - Utils::FormatIPv6(Request->clientAddress().toString()), - Request->getMethod(), - Request->getURI())); - } - - void RESTAPIHandler::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); - } - } - - void RESTAPIHandler::SendFile(Poco::File & File, const std::string & UUID) { - 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", "private"); - Response->set("Pragma", "private"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - Response->set("Content-Length", std::to_string(File.getSize())); - AddCORS(); - Response->sendFile(File.path(),"application/octet-stream"); - } - - void RESTAPIHandler::SendFile(Poco::File & File) { - 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", "private"); - Response->set("Pragma", "private"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - AddCORS(); - Response->sendFile(File.path(),MT.ContentType); - } - - void RESTAPIHandler::SendFile(Poco::TemporaryFile &TempAvatar, const std::string &Type, const std::string & Name) { - 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", "private"); - Response->set("Pragma", "private"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - AddCORS(); - Response->sendFile(TempAvatar.path(),MT.ContentType); - } - - void RESTAPIHandler::SendHTMLFileBack(Poco::File & File, - const Types::StringPairVec & FormVars) { - Response->set("Pragma", "private"); - Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); - Response->set("Content-Length", std::to_string(File.getSize())); - AddCORS(); - auto FormContent = Utils::LoadFile(File.path()); - Utils::ReplaceVariables(FormContent, FormVars); - Response->setChunkedTransferEncoding(true); - Response->setContentType("text/html"); - std::ostream& ostr = Response->send(); - ostr << FormContent; - } - - void RESTAPIHandler::ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status, bool CloseConnection) { - PrepareResponse(Status, CloseConnection); - if(Status == Poco::Net::HTTPResponse::HTTP_NO_CONTENT) { - Response->setContentLength(0); - Response->erase("Content-Type"); - Response->setChunkedTransferEncoding(false); - } - Response->send(); - } - - bool RESTAPIHandler::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; - } - - bool RESTAPIHandler::IsAuthorized() { - if(Internal_) { - auto Allowed = Daemon()->IsValidAPIKEY(*Request); - if(!Allowed) { - if(Server_.LogBadTokens(false)) { - Logger_.debug(Poco::format("I-REQ-DENIED(%s): Method='%s' Path='%s", - Utils::FormatIPv6(Request->clientAddress().toString()), - Request->getMethod(), Request->getURI())); - } - } else { - auto Id = Request->get("X-INTERNAL-NAME", "unknown"); - if(Server_.LogIt(Request->getMethod(),true)) { - Logger_.debug(Poco::format("I-REQ-ALLOWED(%s): User='%s' Method='%s' Path='%s", - Utils::FormatIPv6(Request->clientAddress().toString()), 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 (AuthService()->IsAuthorized(*Request, SessionToken_, UserInfo_)) { -#else - if (AuthClient()->IsAuthorized(*Request, SessionToken_, UserInfo_)) { -#endif - if(Server_.LogIt(Request->getMethod(),true)) { - Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s", - UserInfo_.userinfo.email, - Utils::FormatIPv6(Request->clientAddress().toString()), - Request->clientAddress().toString(), - Request->getMethod(), - Request->getURI())); - } - return true; - } else { - if(Server_.LogBadTokens(true)) { - Logger_.debug(Poco::format("X-REQ-DENIED(%s): Method='%s' Path='%s", - Utils::FormatIPv6(Request->clientAddress().toString()), - Request->getMethod(), Request->getURI())); - } - UnAuthorized(); - } - return false; - } - } - - void RESTAPIHandler::ReturnObject(Poco::JSON::Object &Object) { - PrepareResponse(); - std::ostream &Answer = Response->send(); - Poco::JSON::Stringifier::stringify(Object, Answer); - } - - void RESTAPIHandler::ReturnCountOnly(uint64_t Count) { - Poco::JSON::Object Answer; - Answer.set("count", Count); - ReturnObject(Answer); - } - - bool RESTAPIHandler::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, 1); - QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100); - QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, ""); - QB_.Select = GetParameter(RESTAPI::Protocol::SELECT, ""); - 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); - - if(QB_.Offset<1) - QB_.Offset=1; - return true; - } - - [[nodiscard]] uint64_t RESTAPIHandler::Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default){ - if(Obj->has(Parameter)) - return Obj->get(Parameter); - return Default; - } - - [[nodiscard]] std::string RESTAPIHandler::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]] bool RESTAPIHandler::GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default){ - if(Obj->has(Parameter)) - return Obj->get(Parameter).toString()=="true"; - return Default; - } - - [[nodiscard]] uint64_t RESTAPIHandler::GetWhen(const Poco::JSON::Object::Ptr &Obj) { - return RESTAPIHandler::Get(RESTAPI::Protocol::WHEN, Obj); - } -} \ No newline at end of file diff --git a/src/framework/RESTAPI_handler.h b/src/framework/RESTAPI_handler.h deleted file mode 100644 index a2fab0e..0000000 --- a/src/framework/RESTAPI_handler.h +++ /dev/null @@ -1,234 +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. -// - -#ifndef UCENTRAL_RESTAPI_HANDLER_H -#define UCENTRAL_RESTAPI_HANDLER_H - -#include "Poco/URI.h" -#include "Poco/Net/HTTPRequestHandler.h" -#include "Poco/Net/HTTPRequestHandlerFactory.h" -#include "Poco/Net/HTTPServerRequest.h" -#include "Poco/Net/HTTPServerResponse.h" -#include "Poco/Net/NetException.h" -#include "Poco/Net/PartHandler.h" - -#include "Poco/Logger.h" -#include "Poco/File.h" -#include "Poco/TemporaryFile.h" -#include "Poco/JSON/Object.h" -#include "Poco/CountingStream.h" -#include "Poco/NullStream.h" - -#include "RESTAPI/RESTAPI_SecurityObjects.h" -#include "RESTAPI_utils.h" -#include "RESTAPI_GenericServer.h" - -namespace OpenWifi { - - class RESTAPI_PartHandler: public Poco::Net::PartHandler - { - public: - RESTAPI_PartHandler(): - _length(0) - { - } - - 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]] int length() const - { - return _length; - } - - [[nodiscard]] const std::string& name() const - { - return _name; - } - - [[nodiscard]] const std::string& fileName() const - { - return _fileName; - } - - [[nodiscard]] const std::string& contentType() const - { - return _type; - } - - private: - int _length; - std::string _type; - std::string _name; - std::string _fileName; - }; - - 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, Select; - bool Lifetime=false, LastOnly=false, Newest=false, CountOnly=false, AdditionalInfo=false; - }; - - typedef std::map BindingMap; - - RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector Methods, RESTAPI_GenericServer & Server, bool Internal=false, bool AlwaysAuthorize=true) - : Bindings_(std::move(map)), Logger_(l), Methods_(std::move(Methods)), Server_(Server), Internal_(Internal), AlwaysAuthorize_(AlwaysAuthorize) {} - - static bool ParseBindings(const std::string & Request, const std::list & EndPoints, BindingMap &Keys); - void PrintBindings(); - void ParseParameters(); - - void AddCORS(); - void SetCommonHeaders(bool CloseConnection=false); - void ProcessOptions(); - void - PrepareResponse(Poco::Net::HTTPResponse::HTTPStatus Status = Poco::Net::HTTPResponse::HTTP_OK, - bool CloseConnection = false); - bool ContinueProcessing(); - bool IsAuthorized(); - - uint64_t GetParameter(const std::string &Name, uint64_t Default); - std::string GetParameter(const std::string &Name, const std::string &Default); - bool GetBoolParameter(const std::string &Name, bool Default); - - void BadRequest(const std::string &Reason ); - void InternalError(const std::string &Reason = ""); - void UnAuthorized(const std::string &Reason = ""); - void ReturnObject(Poco::JSON::Object &Object); - void NotFound(); - void OK(); - void ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status, - bool CloseConnection=false); - void SendFile(Poco::File & File, const std::string & UUID); - void SendHTMLFileBack(Poco::File & File, - const Types::StringPairVec & FormVars); - void SendFile(Poco::TemporaryFile &TempAvatar, const std::string &Type, const std::string & Name); - - void SendFile(Poco::File & File); - - const std::string &GetBinding(const std::string &Name, const std::string &Default); - bool InitQueryBlock(); - - void ReturnCountOnly(uint64_t Count); - - [[nodiscard]] static uint64_t Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default=0); - [[nodiscard]] static std::string GetS(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, const std::string & Default=""); - [[nodiscard]] static bool GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default=false); - [[nodiscard]] static uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj); - bool HasParameter(const std::string &QueryParameter, std::string &Value); - bool HasParameter(const std::string &QueryParameter, uint64_t & Value); - - static bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, std::string &Value); - static bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, uint64_t &Value); - static bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, bool &Value); - - 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_; } - - void handleRequest(Poco::Net::HTTPServerRequest &request, - Poco::Net::HTTPServerResponse &response) final; - - virtual void DoGet() = 0 ; - virtual void DoDelete() = 0 ; - virtual void DoPost() = 0 ; - virtual void DoPut() = 0 ; - - const Poco::JSON::Object::Ptr & ParseStream(); - - protected: - BindingMap Bindings_; - Poco::URI::QueryParameters Parameters_; - Poco::Logger &Logger_; - std::string SessionToken_; - SecurityObjects::UserInfoAndPolicy UserInfo_; - std::vector Methods_; - QueryBlock QB_; - bool Internal_=false; - bool QueryBlockInitialized_=false; - Poco::Net::HTTPServerRequest *Request= nullptr; - Poco::Net::HTTPServerResponse *Response= nullptr; - bool AlwaysAuthorize_=true; - Poco::JSON::Parser IncomingParser_; - RESTAPI_GenericServer & Server_; - }; - - class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { - public: - RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server) - : RESTAPIHandler(bindings, L, std::vector{}, Server) {} - 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) { - 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, false); - } - - if constexpr (sizeof...(Args) == 0) { - return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server); - } else { - return RESTAPI_Router(RequestedPath, Bindings, Logger, Server); - } - } - - template - RESTAPIHandler * RESTAPI_Router_I(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, Poco::Logger & Logger, RESTAPI_GenericServer & Server) { - 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, true); - } - - if constexpr (sizeof...(Args) == 0) { - return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server); - } else { - return RESTAPI_Router_I(RequestedPath, Bindings, Logger, Server); - } - } - - -} - -#endif //UCENTRAL_RESTAPI_HANDLER_H diff --git a/src/framework/RESTAPI_system_command.cpp b/src/framework/RESTAPI_system_command.cpp deleted file mode 100644 index bc89151..0000000 --- a/src/framework/RESTAPI_system_command.cpp +++ /dev/null @@ -1,140 +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 "RESTAPI_system_command.h" - -#include "Poco/Exception.h" -#include "Poco/JSON/Parser.h" -#include "Poco/DateTime.h" -#include "Poco/DateTimeFormat.h" - -#include "Daemon.h" -#include "framework/RESTAPI_protocol.h" -#include "framework/RESTAPI_errors.h" -#include -#include - -using namespace std::chrono_literals; - -namespace OpenWifi { - void RESTAPI_system_command::DoPost() { - auto Obj = ParseStream(); - 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); - Daemon()->SetSubsystemLogLevel(Name, Value); - Logger_.information( - Poco::format("Setting log level for %s at %s", Name, Value)); - } - } - return OK(); - } - } else if (Command == RESTAPI::Protocol::GETLOGLEVELS) { - auto CurrentLogLevels = Daemon()->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 = Daemon()->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 = Daemon()->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") - Daemon()->Reload(); - else - Daemon()->Reload(i); - } - }); - ReloadThread.detach(); - } - return OK(); - } - } else { - return BadRequest(RESTAPI::Errors::InvalidCommand); - } - BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); - } - - void RESTAPI_system_command::DoGet() { - std::string Arg; - if(HasParameter("command",Arg) && Arg=="info") { - Poco::JSON::Object Answer; - Answer.set(RESTAPI::Protocol::VERSION, Daemon()->Version()); - Answer.set(RESTAPI::Protocol::UPTIME, Daemon()->uptime().totalSeconds()); - Answer.set(RESTAPI::Protocol::START, Daemon()->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, Daemon()->GetUIURI()); - - Poco::JSON::Array Certificates; - auto SubSystems = Daemon()->GetFullSubSystems(); - std::set CertNames; - - for(const auto &i:SubSystems) { - auto Hosts=i->HostSize(); - for(uint64_t j=0;jHost(j).CertFile(); - if(!CertFileName.empty()) { - auto InsertResult = CertNames.insert(CertFileName); - if(InsertResult.second) { - Poco::JSON::Object Inner; - Inner.set("filename", CertFileName); - 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); - } - BadRequest(RESTAPI::Errors::InvalidCommand); - } -} \ No newline at end of file diff --git a/src/framework/RESTAPI_system_command.h b/src/framework/RESTAPI_system_command.h deleted file mode 100644 index 9c95f85..0000000 --- a/src/framework/RESTAPI_system_command.h +++ /dev/null @@ -1,32 +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. -// - -#ifndef UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H -#define UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H - -#include "framework/RESTAPI_handler.h" - -namespace OpenWifi { -class RESTAPI_system_command : public RESTAPIHandler { - public: - RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, bool Internal) - : RESTAPIHandler(bindings, L, - std::vector{Poco::Net::HTTPRequest::HTTP_POST, - Poco::Net::HTTPRequest::HTTP_GET, - Poco::Net::HTTPRequest::HTTP_OPTIONS}, - Server, - Internal) {} - static const std::list PathName() { return std::list{"/api/v1/system"};} - - void DoGet() final; - void DoPost() final; - void DoPut() final {}; - void DoDelete() final {}; - }; -} -#endif // UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H diff --git a/src/framework/RESTAPI_utils.cpp b/src/framework/RESTAPI_utils.cpp deleted file mode 100644 index d6451b6..0000000 --- a/src/framework/RESTAPI_utils.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// -// Created by stephane bourque on 2021-07-05. -// - -#include "RESTAPI_utils.h" - -namespace OpenWifi::RESTAPI_utils { - - 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); - } -} - diff --git a/src/framework/RESTAPI_utils.h b/src/framework/RESTAPI_utils.h deleted file mode 100644 index 0600b43..0000000 --- a/src/framework/RESTAPI_utils.h +++ /dev/null @@ -1,294 +0,0 @@ -// -// Created by stephane bourque on 2021-07-05. -// - -#ifndef UCENTRALGW_RESTAPI_UTILS_H -#define UCENTRALGW_RESTAPI_UTILS_H -#include - -#include "Poco/JSON/Object.h" -#include "Poco/JSON/Parser.h" -#include "Poco/Net/HTTPServerRequest.h" -#include "framework/OpenWifiTypes.h" -#include "framework/Utils.h" - -namespace OpenWifi::RESTAPI_utils { - - void EmbedDocument(const std::string & ObjName, Poco::JSON::Object & Obj, const std::string &ObjStr); - - 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, const std::string & S) { - Obj.set(Field,S); - } - - inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::vector & S) { - Poco::JSON::Array Array; - for(const auto &i:S) { - Poco::JSON::Object O; - O.set("tag",i.first); - O.set("value", i.second); - } - Obj.set(Field,Array); - } - - 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, uint64_t V) { - Obj.set(Field,V); - } - - 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); - } - - - template void field_to_json(Poco::JSON::Object &Obj, - const char *Field, - const T &V, - std::function F) { - Obj.set(Field, F(V)); - } - - template bool field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, T & V, - std::function F) { - if(Obj->has(Field)) - V = F(Obj->get(Field).toString()); - return true; - } - - inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, std::string &S) { - if(Obj->has(Field)) - S = Obj->get(Field).toString(); - } - - inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, uint64_t &V) { - if(Obj->has(Field)) - V = Obj->get(Field); - } - - inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, bool &V) { - if(Obj->has(Field)) - V = (Obj->get(Field).toString() == "true"); - } - - inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, Types::StringPairVec &Vec) { - if(Obj->isArray(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(Poco::JSON::Object::Ptr Obj, const char *Field, Types::StringVec &V) { - if(Obj->isArray(Field)) { - V.clear(); - Poco::JSON::Array::Ptr A = Obj->getArray(Field); - for(const auto &i:*A) { - V.push_back(i.toString()); - } - } - } - - inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, Types::TagList &V) { - if(Obj->isArray(Field)) { - V.clear(); - Poco::JSON::Array::Ptr A = Obj->getArray(Field); - for(const auto &i:*A) { - V.push_back(i); - } - } - } - - 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 void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::vector &Value) { - if(Obj->isArray(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)) { - 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(); - } - - 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 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; - } - - 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 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; - } -} - -#endif // UCENTRALGW_RESTAPI_UTILS_H diff --git a/src/framework/Storage.h b/src/framework/Storage.h deleted file mode 100644 index cd11a63..0000000 --- a/src/framework/Storage.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Created by stephane bourque on 2021-10-06. -// - -#ifndef OWPROV_STORAGE_H -#define OWPROV_STORAGE_H - - -namespace OpenWifi { - enum DBType { - sqlite, - pgsql, - mysql - }; -} - - -#endif //OWPROV_STORAGE_H diff --git a/src/framework/StorageClass.h b/src/framework/StorageClass.h new file mode 100644 index 0000000..0b7f878 --- /dev/null +++ b/src/framework/StorageClass.h @@ -0,0 +1,171 @@ +// +// Created by stephane bourque on 2021-10-06. +// + +#ifndef OPENWIFI_STORAGE_H +#define OPENWIFI_STORAGE_H + +#include "framework/MicroService.h" + +namespace OpenWifi { + enum DBType { + sqlite, + pgsql, + mysql + }; + + class StorageClass : public SubSystemServer { + public: +/* static StorageClass *instance() { + if (instance_ == nullptr) { + instance_ = new StorageClass; + } + return instance_; + } +*/ + StorageClass() noexcept: + SubSystemServer("StorageClass", "STORAGE-SVR", "storage") + { + } + + int Start() override { + std::lock_guard Guard(Mutex_); + + Logger_.setLevel(Poco::Message::PRIO_NOTICE); + Logger_.notice("Starting."); + std::string DBType = MicroService::instance().ConfigGetString("storage.type"); + + if (DBType == "sqlite") { + Setup_SQLite(); + } else if (DBType == "postgresql") { + Setup_PostgreSQL(); + } else if (DBType == "mysql") { + Setup_MySQL(); + } + return 0; + } + + void Stop() override { + + } + + [[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { + if(dbType_==sqlite) { + return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) + " "; + } else if(dbType_==pgsql) { + return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; + } else if(dbType_==mysql) { + return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; + } + return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; + } + + inline std::string ConvertParams(const std::string & S) const { + std::string R; + R.reserve(S.size()*2+1); + if(dbType_==pgsql) { + auto Idx=1; + for(auto const & i:S) + { + if(i=='?') { + R += '$'; + R.append(std::to_string(Idx++)); + } else { + R += i; + } + } + } else { + R = S; + } + return R; + } + + private: + inline int Setup_SQLite(); + inline int Setup_MySQL(); + inline int Setup_PostgreSQL(); + + protected: + std::unique_ptr Pool_; + std::unique_ptr SQLiteConn_; + std::unique_ptr PostgresConn_; + std::unique_ptr MySQLConn_; + DBType dbType_ = sqlite; + }; + +// inline StorageClass * Storage() { return StorageClass::instance(); } + +#ifdef SMALL_BUILD + int Service::Setup_MySQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } + int Service::Setup_PostgreSQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } +#else + + inline int StorageClass::Setup_SQLite() { + Logger_.notice("SQLite StorageClass enabled."); + dbType_ = sqlite; + auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db"); + auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64); + auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60); + SQLiteConn_ = std::make_unique(); + SQLiteConn_->registerConnector(); + Pool_ = std::make_unique(SQLiteConn_->name(), DBName, 4, NumSessions, IdleTime); + return 0; + } + + inline int StorageClass::Setup_MySQL() { + Logger_.notice("MySQL StorageClass enabled."); + dbType_ = mysql; + auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.mysql.maxsessions", 64); + auto IdleTime = 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"); + + std::string ConnectionStr = + "host=" + Host + + ";user=" + Username + + ";password=" + Password + + ";db=" + Database + + ";port=" + Port + + ";compress=true;auto-reconnect=true"; + + MySQLConn_ = std::make_unique(); + MySQLConn_->registerConnector(); + Pool_ = std::make_unique(MySQLConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); + + return 0; + } + + inline int StorageClass::Setup_PostgreSQL() { + Logger_.notice("PostgreSQL StorageClass enabled."); + dbType_ = pgsql; + auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.postgresql.maxsessions", 64); + auto IdleTime = 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"); + + std::string ConnectionStr = + "host=" + Host + + " user=" + Username + + " password=" + Password + + " dbname=" + Database + + " port=" + Port + + " connect_timeout=" + ConnectionTimeout; + + PostgresConn_ = std::make_unique(); + PostgresConn_->registerConnector(); + Pool_ = std::make_unique(PostgresConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); + + return 0; + } +#endif + +} + +#endif //OPENWIFI_STORAGE_H diff --git a/src/framework/SubSystemServer.cpp b/src/framework/SubSystemServer.cpp deleted file mode 100644 index 97bf2f9..0000000 --- a/src/framework/SubSystemServer.cpp +++ /dev/null @@ -1,306 +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 "SubSystemServer.h" -#include "Daemon.h" - -#include "Poco/Net/X509Certificate.h" -#include "Poco/DateTimeFormatter.h" -#include "Poco/DateTimeFormat.h" -#include "Poco/Net/PrivateKeyPassphraseHandler.h" -#include "Poco/Net/SSLManager.h" - -#include "openssl/ssl.h" - -#include "Daemon.h" - -namespace OpenWifi { -SubSystemServer::SubSystemServer(std::string Name, const std::string &LoggingPrefix, - std::string SubSystemConfigPrefix) - : Name_(std::move(Name)), Logger_(Poco::Logger::get(LoggingPrefix)), - SubSystemConfigPrefix_(std::move(SubSystemConfigPrefix)) { - Logger_.setLevel(Poco::Message::PRIO_NOTICE); -} - -void SubSystemServer::initialize(Poco::Util::Application &self) { - Logger_.notice("Initializing..."); - auto i = 0; - bool good = true; - - ConfigServersList_.clear(); - while (good) { - std::string root{SubSystemConfigPrefix_ + ".host." + std::to_string(i) + "."}; - - std::string address{root + "address"}; - if (Daemon()->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 = Daemon()->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(Daemon()->ConfigGetString(address, ""), - Daemon()->ConfigGetInt(port, 0), - Daemon()->ConfigPath(key, ""), - Daemon()->ConfigPath(cert, ""), - Daemon()->ConfigPath(rootca, ""), - Daemon()->ConfigPath(issuer, ""), - Daemon()->ConfigPath(clientcas, ""), - Daemon()->ConfigPath(cas, ""), - Daemon()->ConfigGetString(key_password, ""), - Daemon()->ConfigGetString(name, ""), M, - (int)Daemon()->ConfigGetInt(backlog, 64)); - ConfigServersList_.push_back(entry); - i++; - } - } -} - -void SubSystemServer::uninitialize() { -} - -void SubSystemServer::reinitialize(Poco::Util::Application &self) { - Logger_.information("Reloading of this subsystem is not supported."); -} - -void SubSystemServer::defineOptions(Poco::Util::OptionSet &options) {} - -class MyPrivateKeyPassphraseHandler : public Poco::Net::PrivateKeyPassphraseHandler { - public: - explicit MyPrivateKeyPassphraseHandler(const std::string &Password, Poco::Logger & Logger): - PrivateKeyPassphraseHandler(true), - Logger_(Logger), - Password_(Password) {} - void onPrivateKeyRequested(const void * pSender,std::string & privateKey) { - Logger_.information("Returning key passphrase."); - privateKey = Password_; - }; - private: - std::string Password_; - Poco::Logger & Logger_; -}; - -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(Poco::format("Wrong Certificate(%s) for Key(%s)", 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(10); - 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); - } -} - -void PropertiesFileServerEntry::LogCertInfo(Poco::Logger &L, - const Poco::Crypto::X509Certificate &C) { - - L.information("============================================================================================="); - L.information(Poco::format("> Issuer: %s", C.issuerName())); - L.information("---------------------------------------------------------------------------------------------"); - L.information(Poco::format("> Common Name: %s", - C.issuerName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); - L.information(Poco::format("> Country: %s", - C.issuerName(Poco::Crypto::X509Certificate::NID_COUNTRY))); - L.information(Poco::format("> Locality: %s", - C.issuerName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); - L.information(Poco::format("> State/Prov: %s", - C.issuerName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); - L.information(Poco::format("> Org name: %s", - C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); - L.information( - Poco::format("> Org unit: %s", - C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); - L.information( - Poco::format("> Email: %s", - C.issuerName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); - L.information(Poco::format("> Serial#: %s", - C.issuerName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); - L.information("---------------------------------------------------------------------------------------------"); - L.information(Poco::format("> Subject: %s", C.subjectName())); - L.information("---------------------------------------------------------------------------------------------"); - L.information(Poco::format("> Common Name: %s", - C.subjectName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); - L.information(Poco::format("> Country: %s", - C.subjectName(Poco::Crypto::X509Certificate::NID_COUNTRY))); - L.information(Poco::format("> Locality: %s", - C.subjectName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); - L.information( - Poco::format("> State/Prov: %s", - C.subjectName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); - L.information( - Poco::format("> Org name: %s", - C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); - L.information( - Poco::format("> Org unit: %s", - C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); - L.information( - Poco::format("> Email: %s", - C.subjectName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); - L.information(Poco::format("> Serial#: %s", - C.subjectName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); - L.information("---------------------------------------------------------------------------------------------"); - L.information(Poco::format("> Signature Algo: %s", C.signatureAlgorithm())); - auto From = Poco::DateTimeFormatter::format(C.validFrom(), Poco::DateTimeFormat::HTTP_FORMAT); - L.information(Poco::format("> Valid from: %s", From)); - auto Expires = - Poco::DateTimeFormatter::format(C.expiresOn(), Poco::DateTimeFormat::HTTP_FORMAT); - L.information(Poco::format("> Expires on: %s", Expires)); - L.information(Poco::format("> Version: %d", (int)C.version())); - L.information(Poco::format("> Serial #: %s", C.serialNumber())); - L.information("============================================================================================="); -} - -void PropertiesFileServerEntry::LogCert(Poco::Logger &L) const { - try { - Poco::Crypto::X509Certificate C(cert_file_); - L.information("============================================================================================="); - L.information("============================================================================================="); - L.information(Poco::format("Certificate Filename: %s", 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(Poco::format("Issues Certificate Filename: %s", 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(Poco::format("Client CAs Filename: %s", client_cas_)); - L.information("============================================================================================="); - auto i = 1; - for (const auto &C3 : Certs) { - L.information(Poco::format(" Index: %d", 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(Poco::format("CA Filename: %s", root_ca_)); - L.information("============================================================================================="); - auto i = 1; - for (const auto &C : Certs) { - L.information(Poco::format(" Index: %d", i)); - L.information("============================================================================================="); - LogCertInfo(L, C); - i++; - } - L.information("============================================================================================="); - } catch (const Poco::Exception &E) { - L.log(E); - } -} -} \ No newline at end of file diff --git a/src/framework/SubSystemServer.h b/src/framework/SubSystemServer.h deleted file mode 100644 index ff5e045..0000000 --- a/src/framework/SubSystemServer.h +++ /dev/null @@ -1,95 +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. -// - -#ifndef UCENTRAL_SUBSYSTEMSERVER_H -#define UCENTRAL_SUBSYSTEMSERVER_H - -#include - -#include "Poco/Util/Application.h" -#include "Poco/Util/Option.h" -#include "Poco/Util/OptionSet.h" -#include "Poco/Util/HelpFormatter.h" -#include "Poco/Logger.h" -#include "Poco/Net/SecureServerSocket.h" - -#include "Poco/Net/X509Certificate.h" - -namespace OpenWifi { -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), key_file_(std::move(Key_file)), - cert_file_(std::move(Cert_file)), root_ca_(std::move(RootCa)), - issuer_cert_file_(std::move(Issuer)), client_cas_(std::move(ClientCas)), - cas_(std::move(Cas)), key_file_password_(std::move(Key_file_password)), - name_(std::move(Name)), level_(M), backlog_(backlog){}; - - [[nodiscard]] const std::string &Address() const { return address_; }; - [[nodiscard]] uint32_t Port() const { return port_; }; - [[nodiscard]] const std::string &KeyFile() const { return key_file_; }; - [[nodiscard]] const std::string &CertFile() const { return cert_file_; }; - [[nodiscard]] const std::string &RootCA() const { return root_ca_; }; - [[nodiscard]] const std::string &KeyFilePassword() const { return key_file_password_; }; - [[nodiscard]] const std::string &IssuerCertFile() const { return issuer_cert_file_; }; - [[nodiscard]] const std::string &Name() const { return name_; }; - [[nodiscard]] Poco::Net::SecureServerSocket CreateSecureSocket(Poco::Logger &L) const; - [[nodiscard]] int Backlog() const { return backlog_; } - void LogCert(Poco::Logger &L) const; - void LogCas(Poco::Logger &L) const; - static void LogCertInfo(Poco::Logger &L, const Poco::Crypto::X509Certificate &C); - - private: - std::string address_; - 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_; - uint32_t port_; - std::string name_; - int backlog_; - Poco::Net::Context::VerificationMode level_; -}; - -class SubSystemServer : public Poco::Util::Application::Subsystem { - - public: - SubSystemServer(std::string Name, const std::string &LoggingName, std::string SubSystemPrefix); - void initialize(Poco::Util::Application &self) override; - void uninitialize() override; - void reinitialize(Poco::Util::Application &self) override; - void defineOptions(Poco::Util::OptionSet &options) override; - inline const std::string & Name() const { return Name_; }; - const char * name() const override { return Name_.c_str(); } - - const PropertiesFileServerEntry & Host(uint64_t index) { return ConfigServersList_[index]; }; - uint64_t HostSize() const { return ConfigServersList_.size(); } - Poco::Logger &Logger() { return Logger_; }; - void SetLoggingLevel(Poco::Message::Priority NewPriority) { Logger_.setLevel(NewPriority); } - int GetLoggingLevel() { return Logger_.getLevel(); } - virtual int Start() = 0; - virtual void Stop() = 0; - - protected: - std::recursive_mutex Mutex_; - Poco::Logger &Logger_; - std::string Name_; - std::vector ConfigServersList_; - std::string SubSystemConfigPrefix_; -}; -} -#endif //UCENTRAL_SUBSYSTEMSERVER_H diff --git a/src/framework/Utils.cpp b/src/framework/Utils.cpp deleted file mode 100644 index 37d0308..0000000 --- a/src/framework/Utils.cpp +++ /dev/null @@ -1,523 +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 -#include -#include -#include -#include -#include - -#include "Utils.h" - -#include "Poco/Exception.h" -#include "Poco/DateTimeFormat.h" -#include "Poco/DateTimeFormatter.h" -#include "Poco/DateTime.h" -#include "Poco/DateTimeParser.h" -#include "Poco/StringTokenizer.h" -#include "Poco/Message.h" -#include "Poco/File.h" -#include "Poco/StreamCopier.h" -#include "Poco/Path.h" -#include "Poco/URI.h" -#include "Poco/Net/HTTPSClientSession.h" -#include "Poco/Net/HTTPResponse.h" - -#include "uCentralProtocol.h" -#include "Daemon.h" - -namespace OpenWifi::Utils { - - [[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]] 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; - } - - [[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 = '='; - - std::string base64encode(const byte *input, unsigned long size) { - std::string encoded; - encoded.reserve(((size / 3) + (size % 3 > 0)) * 4); - - std::uint32_t temp; - - std::size_t i; - - int ee = (int)(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; - } - - 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; - } - - std::string to_RFC3339(uint64_t t) - { - if(t==0) - return ""; - return Poco::DateTimeFormatter::format(Poco::DateTime(Poco::Timestamp::fromEpochTime(t)), Poco::DateTimeFormat::ISO8601_FORMAT); - } - - uint64_t from_RFC3339(const std::string &TimeString) - { - if(TimeString.empty() || TimeString=="0") - return 0; - - try { - int TZ; - Poco::DateTime DT = Poco::DateTimeParser::parse(Poco::DateTimeFormat::ISO8601_FORMAT,TimeString,TZ); - return DT.timestamp().epochTime(); - } - catch( const Poco::Exception & E ) - { - - } - return 0; - } - - bool ParseTime(const std::string &Time, int & Hours, int & Minutes, int & Seconds) { - Poco::StringTokenizer TimeTokens(Time,":",Poco::StringTokenizer::TOK_TRIM); - - Hours = Minutes = Hours = 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; - } - - 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"; - } - } - - 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)); - } - - uint64_t SerialNumberToInt(const std::string & S) { - uint64_t R=0; - - for(const auto &i:S) - if(i>='0' && i<='9') { - R <<= 4; - R += (i-'0'); - } else if(i>='a' && i<='f') { - R <<= 4; - R += (i-'a') + 10 ; - } else if(i>='A' && i<='F') { - R <<= 4; - R += (i-'A') + 10 ; - } - return R; - } - - 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; - } - - 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; - } - - void SaveSystemId(uint64_t Id) { - try { - std::ofstream O; - O.open(Daemon()->DataDir() + "/system.id",std::ios::binary | std::ios::trunc); - O << Id; - O.close(); - } catch (...) - { - std::cout << "Could not save system ID" << std::endl; - } - } - - 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) ; - SaveSystemId(S); - std::cout << "ID: " << S << std::endl; - return S; - } - - uint64_t GetSystemId() { - uint64_t ID=0; - - // if the system ID file exists, open and read it. - Poco::File SID( Daemon()->DataDir() + "/system.id"); - try { - if (SID.exists()) { - std::ifstream I; - I.open(SID.path()); - I >> ID; - I.close(); - if (ID == 0) - return InitializeSystemId(); - return ID; - } else { - return InitializeSystemId(); - } - } catch (...) { - return InitializeSystemId(); - } - } - - bool ValidEMailAddress(const std::string &email) { - // define a regular expression - const std::regex pattern - ("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+"); - - // try to match the string with the regular expression - return std::regex_match(email, pattern); - } - - 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); - } - } - - 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" }; - } - - 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; - } - - 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; - } - - 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; - } - -} diff --git a/src/framework/Utils.h b/src/framework/Utils.h deleted file mode 100644 index 761f13e..0000000 --- a/src/framework/Utils.h +++ /dev/null @@ -1,89 +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. -// - -#ifndef UCENTRALGW_UTILS_H -#define UCENTRALGW_UTILS_H - -#include -#include -#include -#include - -#include "Poco/Net/NetworkInterface.h" -#include "Poco/Net/IPAddress.h" -#include "Poco/String.h" -#include "Poco/File.h" -#include "framework/OpenWifiTypes.h" - -#define DBGLINE { std::cout << __FILE__ << ":" << __func__ << ":" << __LINE__ << std::endl; }; - -namespace OpenWifi::Utils { - - enum MediaTypeEncodings { - PLAIN, - BINARY, - BASE64 - }; - struct MediaTypeEncoding { - MediaTypeEncodings Encoding=PLAIN; - std::string ContentType; - }; - - [[nodiscard]] std::vector Split(const std::string &List, char Delimiter=','); - [[nodiscard]] std::string FormatIPv6(const std::string & I ); - inline void padTo(std::string& str, size_t num, char paddingChar = '\0') { - str.append(num - str.length() % num, paddingChar); - } - - [[nodiscard]] std::string SerialToMAC(const std::string &Serial); - [[nodiscard]] std::string ToHex(const std::vector & B); - - using byte = std::uint8_t; - - [[nodiscard]] std::string base64encode(const byte *input, unsigned long size); - std::vector base64decode(const std::string& input); - -// [[nodiscard]] std::string to_RFC3339(uint64_t t); -// [[nodiscard]] uint64_t from_RFC3339(const std::string &t); - - 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]] bool ValidSerialNumber(const std::string &Serial); - [[nodiscard]] std::string LogLevelToString(int Level); - - [[nodiscard]] bool SerialNumberMatch(const std::string &S1, const std::string &S2, int extrabits=2); - [[nodiscard]] uint64_t SerialNumberToInt(const std::string & S); - [[nodiscard]] uint64_t SerialNumberToOUI(const std::string & S); - - [[nodiscard]] uint64_t GetDefaultMacAsInt64(); - [[nodiscard]] uint64_t GetSystemId(); - - [[nodiscard]] bool ValidEMailAddress(const std::string &E); - [[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(); - } - - -} -#endif // UCENTRALGW_UTILS_H diff --git a/src/framework/orm.h b/src/framework/orm.h index 18f4b72..0c868f0 100644 --- a/src/framework/orm.h +++ b/src/framework/orm.h @@ -24,7 +24,7 @@ #include "Poco/Data/SQLite/Connector.h" #include "Poco/Logger.h" #include "Poco/StringTokenizer.h" -#include "Storage.h" +#include "StorageClass.h" namespace ORM { diff --git a/src/framework/storage_setup.cpp b/src/framework/storage_setup.cpp deleted file mode 100644 index b5e6dd9..0000000 --- a/src/framework/storage_setup.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// -// Created by stephane bourque on 2021-08-22. -// - -#include "StorageService.h" -#include "Daemon.h" - -namespace OpenWifi { - -#ifdef SMALL_BUILD - int Service::Setup_MySQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } - int Service::Setup_PostgreSQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } -#else - - int Storage::Setup_SQLite() { - Logger_.notice("SQLite Storage enabled."); - dbType_ = sqlite; - auto DBName = Daemon()->DataDir() + "/" + Daemon()->ConfigGetString("storage.type.sqlite.db"); - auto NumSessions = Daemon()->ConfigGetInt("storage.type.sqlite.maxsessions", 64); - auto IdleTime = Daemon()->ConfigGetInt("storage.type.sqlite.idletime", 60); - SQLiteConn_ = std::make_unique(); - SQLiteConn_->registerConnector(); - Pool_ = std::make_unique(SQLiteConn_->name(), DBName, 4, NumSessions, IdleTime); - return 0; - } - - int Storage::Setup_MySQL() { - Logger_.notice("MySQL Storage enabled."); - dbType_ = mysql; - auto NumSessions = Daemon()->ConfigGetInt("storage.type.mysql.maxsessions", 64); - auto IdleTime = Daemon()->ConfigGetInt("storage.type.mysql.idletime", 60); - auto Host = Daemon()->ConfigGetString("storage.type.mysql.host"); - auto Username = Daemon()->ConfigGetString("storage.type.mysql.username"); - auto Password = Daemon()->ConfigGetString("storage.type.mysql.password"); - auto Database = Daemon()->ConfigGetString("storage.type.mysql.database"); - auto Port = Daemon()->ConfigGetString("storage.type.mysql.port"); - - std::string ConnectionStr = - "host=" + Host + - ";user=" + Username + - ";password=" + Password + - ";db=" + Database + - ";port=" + Port + - ";compress=true;auto-reconnect=true"; - - MySQLConn_ = std::make_unique(); - MySQLConn_->registerConnector(); - Pool_ = std::make_unique(MySQLConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); - - return 0; - } - - int Storage::Setup_PostgreSQL() { - Logger_.notice("PostgreSQL Storage enabled."); - dbType_ = pgsql; - auto NumSessions = Daemon()->ConfigGetInt("storage.type.postgresql.maxsessions", 64); - auto IdleTime = Daemon()->ConfigGetInt("storage.type.postgresql.idletime", 60); - auto Host = Daemon()->ConfigGetString("storage.type.postgresql.host"); - auto Username = Daemon()->ConfigGetString("storage.type.postgresql.username"); - auto Password = Daemon()->ConfigGetString("storage.type.postgresql.password"); - auto Database = Daemon()->ConfigGetString("storage.type.postgresql.database"); - auto Port = Daemon()->ConfigGetString("storage.type.postgresql.port"); - auto ConnectionTimeout = Daemon()->ConfigGetString("storage.type.postgresql.connectiontimeout"); - - std::string ConnectionStr = - "host=" + Host + - " user=" + Username + - " password=" + Password + - " dbname=" + Database + - " port=" + Port + - " connect_timeout=" + ConnectionTimeout; - - PostgresConn_ = std::make_unique(); - PostgresConn_->registerConnector(); - Pool_ = std::make_unique(PostgresConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); - - return 0; - } - -#endif - -} - diff --git a/src/framework/uCentralProtocol.h b/src/framework/uCentral_Protocol.h similarity index 98% rename from src/framework/uCentralProtocol.h rename to src/framework/uCentral_Protocol.h index 716db3b..33c7558 100644 --- a/src/framework/uCentralProtocol.h +++ b/src/framework/uCentral_Protocol.h @@ -105,7 +105,7 @@ namespace OpenWifi::uCentralProtocol { ET_TELEMETRY }; - static EVENT_MSG EventFromString(const std::string & Method) { + inline static EVENT_MSG EventFromString(const std::string & Method) { if (!Poco::icompare(Method, CONNECT)) { return ET_CONNECT; } else if (!Poco::icompare(Method, STATE)) { diff --git a/src/storage/storage_tables.cpp b/src/storage/storage_tables.cpp index 4c0e389..fbf780d 100644 --- a/src/storage/storage_tables.cpp +++ b/src/storage/storage_tables.cpp @@ -2,8 +2,7 @@ // Created by stephane bourque on 2021-06-13. // -#include "../StorageService.h" -#include "../framework/Utils.h" +#include "StorageService.h" #include "storage_users.h" #include "storage_avatar.h" diff --git a/src/storage/storage_users.cpp b/src/storage/storage_users.cpp index a922183..2215c7a 100644 --- a/src/storage/storage_users.cpp +++ b/src/storage/storage_users.cpp @@ -2,13 +2,12 @@ // Created by stephane bourque on 2021-06-25. // -#include "../StorageService.h" -#include "../framework/RESTAPI_utils.h" -#include "../Daemon.h" - #include "Poco/Tuple.h" #include "storage_users.h" +#include "StorageService.h" +#include "framework/MicroService.h" + namespace OpenWifi { bool Convert(const UserInfoRecord &T, SecurityObjects::UserInfo &U) { @@ -102,7 +101,7 @@ namespace OpenWifi { if(!Records.empty()) return false; - NewUser.Id = Daemon()->CreateUUID(); + NewUser.Id = MicroService::instance().CreateUUID(); NewUser.creationDate = std::time(nullptr); // if there is a password, we assume that we do not want email verification,