diff --git a/src/ActionLinkManager.cpp b/src/ActionLinkManager.cpp index 4e18fee..ad2a6fe 100644 --- a/src/ActionLinkManager.cpp +++ b/src/ActionLinkManager.cpp @@ -53,6 +53,10 @@ namespace OpenWifi { i.action==OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP ) && !StorageService()->SubDB().GetUserById(i.userId,UInfo)) { StorageService()->ActionLinksDB().CancelAction(i.id); continue; + } else if((i.action=OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION) && + (OpenWifi::Now()-i.created)>(24*60*60)) { + StorageService()->ActionLinksDB().CancelAction(i.id); + continue; } switch(i.action) { @@ -96,6 +100,14 @@ namespace OpenWifi { } break; + case OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION: { + if(AuthService::SendEmailToSubUser(i.id, UInfo.email, AuthService::EMAIL_INVITATION)) { + Logger().information(fmt::format("Send new subscriber email invitation link to {}",UInfo.email)); + } + StorageService()->ActionLinksDB().SentAction(i.id); + } + break; + default: { StorageService()->ActionLinksDB().SentAction(i.id); } diff --git a/src/ActionLinkManager.h b/src/ActionLinkManager.h index 043c8e6..e01e048 100644 --- a/src/ActionLinkManager.h +++ b/src/ActionLinkManager.h @@ -12,14 +12,6 @@ namespace OpenWifi { class ActionLinkManager : public SubSystemServer, Poco::Runnable { public: -/* enum Actions { - FORGOT_PASSWORD, - VERIFY_EMAIL, - SUB_FORGOT_PASSWORD, - SUB_VERIFY_EMAIL, - SUB_SIGNUP - }; -*/ static ActionLinkManager * instance() { static auto instance_ = new ActionLinkManager; return instance_; diff --git a/src/AuthService.cpp b/src/AuthService.cpp index 7fbbb3a..b36e9fe 100644 --- a/src/AuthService.cpp +++ b/src/AuthService.cpp @@ -23,6 +23,11 @@ namespace OpenWifi { + inline const static std::vector EmailTemplateNames = { "password_reset" , + "email_verification", + "signuo_verification", + "email_invitation" }; + AuthService::ACCESS_TYPE AuthService::IntToAccessType(int C) { switch (C) { case 1: return USERNAME; @@ -576,7 +581,7 @@ namespace OpenWifi { Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "Password reset link"; Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ; - SMTPMailerService()->SendMessage(UInfo.email, "password_reset.txt", Attrs); + SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[FORGOT_PASSWORD], Attrs); } break; @@ -586,11 +591,22 @@ namespace OpenWifi { Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "e-mail Address Verification"; Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ; - SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); + SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[EMAIL_VERIFICATION], Attrs); UInfo.waitingForEmailCheck = true; } break; + case EMAIL_INVITATION: { + MessageAttributes Attrs; + Attrs[RECIPIENT_EMAIL] = UInfo.email; + Attrs[LOGO] = GetLogoAssetURI(); + Attrs[SUBJECT] = "e-mail Invitation"; + Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_invitation&id=" + LinkId ; + SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[EMAIL_INVITATION], Attrs); + UInfo.waitingForEmailCheck = true; + } + break; + default: break; } @@ -611,7 +627,7 @@ namespace OpenWifi { Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "Password reset link"; Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ; - SMTPMailerService()->SendMessage(UInfo.email, "password_reset.txt", Attrs); + SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[FORGOT_PASSWORD], Attrs); } break; @@ -621,7 +637,7 @@ namespace OpenWifi { Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "e-mail Address Verification"; Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ; - SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); + SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[EMAIL_VERIFICATION], Attrs); UInfo.waitingForEmailCheck = true; } break; @@ -632,7 +648,7 @@ namespace OpenWifi { Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "Signup e-mail Address Verification"; Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=signup_verification&id=" + LinkId ; - SMTPMailerService()->SendMessage(UInfo.email, "signup_verification.txt", Attrs); + SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[SIGNUP_VERIFICATION], Attrs); UInfo.waitingForEmailCheck = true; } break; diff --git a/src/AuthService.h b/src/AuthService.h index ea05b1a..438fa43 100644 --- a/src/AuthService.h +++ b/src/AuthService.h @@ -37,9 +37,10 @@ namespace OpenWifi{ }; enum EMAIL_REASON { - FORGOT_PASSWORD, + FORGOT_PASSWORD = 0, EMAIL_VERIFICATION, - SIGNUP_VERIFICATION + SIGNUP_VERIFICATION, + EMAIL_INVITATION }; static ACCESS_TYPE IntToAccessType(int C); diff --git a/src/RESTAPI/RESTAPI_action_links.cpp b/src/RESTAPI/RESTAPI_action_links.cpp index c5d3d93..30ed3f3 100644 --- a/src/RESTAPI/RESTAPI_action_links.cpp +++ b/src/RESTAPI/RESTAPI_action_links.cpp @@ -38,6 +38,8 @@ namespace OpenWifi { return CompleteResetPassword(); else if(Action=="signup_completion") return CompleteSubVerification(); + else if(Action=="email_invitation") + return CompleteEmailInvitation(); else return DoReturnA404(); } @@ -262,4 +264,8 @@ namespace OpenWifi { SendHTMLFileBack(FormFile, FormVars); } + void RESTAPI_action_links::CompleteEmailInvitation() { + /// TODO: + } + } diff --git a/src/RESTAPI/RESTAPI_action_links.h b/src/RESTAPI/RESTAPI_action_links.h index 427679a..e804a48 100644 --- a/src/RESTAPI/RESTAPI_action_links.h +++ b/src/RESTAPI/RESTAPI_action_links.h @@ -27,6 +27,7 @@ namespace OpenWifi { void DoEmailVerification(SecurityObjects::ActionLink &Link); void DoReturnA404(); void DoNewSubVerification(SecurityObjects::ActionLink &Link); + void CompleteEmailInvitation(); void DoGet() final; void DoPost() final; diff --git a/src/RESTObjects/RESTAPI_OWLSobjects.cpp b/src/RESTObjects/RESTAPI_OWLSobjects.cpp new file mode 100644 index 0000000..5a16165 --- /dev/null +++ b/src/RESTObjects/RESTAPI_OWLSobjects.cpp @@ -0,0 +1,110 @@ +// +// Created by stephane bourque on 2021-08-31. +// + +#include "framework/MicroService.h" + +using OpenWifi::RESTAPI_utils::field_to_json; +using OpenWifi::RESTAPI_utils::field_from_json; +using OpenWifi::RESTAPI_utils::EmbedDocument; + +#include "RESTAPI_OWLSobjects.h" + +// SIM -> 0x53/0x073, 0x49/0x69, 0x4d/0x6d + +namespace OpenWifi::OWLSObjects { + + void SimulationDetails::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"id", id); + field_to_json(Obj,"name", name); + field_to_json(Obj,"gateway", gateway); + field_to_json(Obj,"certificate", certificate); + field_to_json(Obj,"key", key); + field_to_json(Obj,"macPrefix", macPrefix); + field_to_json(Obj,"deviceType", deviceType); + field_to_json(Obj,"devices", devices); + field_to_json(Obj,"healthCheckInterval", healthCheckInterval); + field_to_json(Obj,"stateInterval", stateInterval); + field_to_json(Obj,"minAssociations", minAssociations); + field_to_json(Obj,"maxAssociations", maxAssociations); + field_to_json(Obj,"minClients", minClients); + field_to_json(Obj,"maxClients", maxClients); + field_to_json(Obj,"simulationLength", simulationLength); + field_to_json(Obj,"threads", threads); + field_to_json(Obj,"clientInterval", clientInterval); + field_to_json(Obj,"keepAlive", keepAlive); + field_to_json(Obj,"reconnectInterval", reconnectInterval); + field_to_json(Obj,"concurrentDevices", concurrentDevices); + } + + bool SimulationDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"id", id); + field_from_json(Obj,"name", name); + field_from_json(Obj,"gateway", gateway); + field_from_json(Obj,"certificate", certificate); + field_from_json(Obj,"key", key); + field_from_json(Obj,"macPrefix", macPrefix); + field_from_json(Obj,"deviceType", deviceType); + field_from_json(Obj,"devices", devices); + field_from_json(Obj,"healthCheckInterval", healthCheckInterval); + field_from_json(Obj,"stateInterval", stateInterval); + field_from_json(Obj,"minAssociations", minAssociations); + field_from_json(Obj,"maxAssociations", maxAssociations); + field_from_json(Obj,"minClients", minClients); + field_from_json(Obj,"maxClients", maxClients); + field_from_json(Obj,"simulationLength", simulationLength); + field_from_json(Obj,"threads", threads); + field_from_json(Obj,"clientInterval", clientInterval); + field_from_json(Obj,"keepAlive", keepAlive); + field_from_json(Obj,"reconnectInterval", reconnectInterval); + field_from_json(Obj,"concurrentDevices", concurrentDevices); + return true; + } catch(...) { + + } + return false; + } + + void SimulationDetailsList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"list", list); + } + + bool SimulationDetailsList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"list", list); + return true; + } catch(...) { + + } + return false; + } + + void SimulationStatus::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"id", id); + field_to_json(Obj,"simulationId", simulationId); + field_to_json(Obj,"state", state); + field_to_json(Obj,"tx", tx); + field_to_json(Obj,"rx", rx); + field_to_json(Obj,"msgsTx", msgsTx); + field_to_json(Obj,"msgsRx", msgsRx); + field_to_json(Obj,"liveDevices", liveDevices); + field_to_json(Obj,"timeToFullDevices", timeToFullDevices); + field_to_json(Obj,"startTime", startTime); + field_to_json(Obj,"endTime", endTime); + field_to_json(Obj,"errorDevices", errorDevices); + field_to_json(Obj,"owner", owner); + } + + void Dashboard::to_json([[maybe_unused]] Poco::JSON::Object &Obj) const { + + } + + bool Dashboard::from_json([[maybe_unused]] const Poco::JSON::Object::Ptr &Obj) { + return true; + } + + void Dashboard::reset() { + + } +} diff --git a/src/RESTObjects/RESTAPI_OWLSobjects.h b/src/RESTObjects/RESTAPI_OWLSobjects.h new file mode 100644 index 0000000..4dadcb6 --- /dev/null +++ b/src/RESTObjects/RESTAPI_OWLSobjects.h @@ -0,0 +1,77 @@ +// +// Created by stephane bourque on 2021-08-31. +// + +#ifndef UCENTRALSIM_RESTAPI_OWLSOBJECTS_H +#define UCENTRALSIM_RESTAPI_OWLSOBJECTS_H + +#include +#include "Poco/JSON/Object.h" + +namespace OpenWifi::OWLSObjects { + + struct SimulationDetails { + std::string id; + std::string name; + std::string gateway; + std::string certificate; + std::string key; + std::string macPrefix; + std::string deviceType; + uint64_t devices = 5; + uint64_t healthCheckInterval = 60; + uint64_t stateInterval = 60 ; + uint64_t minAssociations = 1; + uint64_t maxAssociations = 3; + uint64_t minClients = 1 ; + uint64_t maxClients = 3; + uint64_t simulationLength = 60 * 60; + uint64_t threads = 16; + uint64_t clientInterval = 1; + uint64_t keepAlive = 300; + uint64_t reconnectInterval = 30 ; + uint64_t concurrentDevices = 5; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct SimulationDetailsList { + std::vector list; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct SimulationStatus { + std::string id; + std::string simulationId; + std::string state; + uint64_t tx; + uint64_t rx; + uint64_t msgsTx; + uint64_t msgsRx; + uint64_t liveDevices; + uint64_t timeToFullDevices; + uint64_t startTime; + uint64_t endTime; + uint64_t errorDevices; + std::string owner; + + void to_json(Poco::JSON::Object &Obj) const; + }; + + + struct Dashboard { + int O; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + void reset(); + + }; + +} + + +#endif //UCENTRALSIM_RESTAPI_OWLSOBJECTS_H diff --git a/src/RESTObjects/RESTAPI_SecurityObjects.h b/src/RESTObjects/RESTAPI_SecurityObjects.h index a3f06e8..ef74bc4 100644 --- a/src/RESTObjects/RESTAPI_SecurityObjects.h +++ b/src/RESTObjects/RESTAPI_SecurityObjects.h @@ -251,7 +251,8 @@ namespace OpenWifi { VERIFY_EMAIL, SUB_FORGOT_PASSWORD, SUB_VERIFY_EMAIL, - SUB_SIGNUP + SUB_SIGNUP, + EMAIL_INVITATION }; struct ActionLink { diff --git a/src/SMTPMailerService.cpp b/src/SMTPMailerService.cpp index 1be61b3..2b6a94c 100644 --- a/src/SMTPMailerService.cpp +++ b/src/SMTPMailerService.cpp @@ -32,7 +32,9 @@ namespace OpenWifi { TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir()); MailRetry_ = MicroService::instance().ConfigGetInt("mailer.retry",2*60); MailAbandon_ = MicroService::instance().ConfigGetInt("mailer.abandon",2*60*60); + UseHTML_ = MicroService::instance().ConfigGetBool("mailer.html",false); Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty()); + EmailLogo_ = TemplateDir_ + "/" + MicroService::instance().ConfigGetString("mailer.logo","logo.jpg"); } } @@ -59,7 +61,7 @@ namespace OpenWifi { PendingMessages_.push_back(MessageEvent{.Posted= OpenWifi::Now(), .LastTry=0, .Sent=0, - .File=Poco::File(TemplateDir_ + "/" +Name), + .File=Poco::File(TemplateDir_ + "/" + Name + (UseHTML_ ? ".html" : ".txt")), .Attrs=Attrs}); return true; } @@ -116,13 +118,11 @@ namespace OpenWifi { } MessageSendStatus SMTPMailerService::SendIt(const MessageEvent &Msg) { + std::string Recipient; try { - Poco::Net::MailMessage Message; - Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second; - auto H1 = Msg.Attrs.find(SENDER); std::string TheSender; if(H1!=Msg.Attrs.end()) { @@ -130,32 +130,35 @@ namespace OpenWifi { } else { TheSender = Sender_ ; } - Message.setSender( TheSender ); + + auto Message = std::make_unique(); + + Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second; + Message->setSender( TheSender ); + Message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient)); + Message->setSubject(Msg.Attrs.find(SUBJECT)->second); + Logger().information(fmt::format("Sending message to:{} from {}",Recipient,TheSender)); - Message.addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient)); - Message.setSubject(Msg.Attrs.find(SUBJECT)->second); if(Msg.Attrs.find(TEXT) != Msg.Attrs.end()) { std::string Content = Msg.Attrs.find(TEXT)->second; - Message.addContent(new Poco::Net::StringPartSource(Content)); + Message->addContent(new Poco::Net::StringPartSource(Content)); } else { std::string Content = Utils::LoadFile(Msg.File); - // std::cout << "Mailing " << Content << std::endl; Types::StringPairVec Variables; FillVariables(Msg.Attrs, Variables); Utils::ReplaceVariables(Content, Variables); - // std::cout << "Mailing " << Content << std::endl; - Message.addContent(new Poco::Net::StringPartSource(Content)); + Message->addContent(new Poco::Net::StringPartSource(Content, UseHTML_ ? "text/html" : "text/plain")); } auto Logo = Msg.Attrs.find(LOGO); if(Logo!=Msg.Attrs.end()) { try { - Poco::File LogoFile(AuthService::GetLogoAssetFileName()); + Poco::File LogoFile(EmailLogo_); std::ifstream IF(LogoFile.path()); std::ostringstream OS; Poco::StreamCopier::copyStream(IF, OS); - Message.addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/png")); + Message->addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/png")); } catch (...) { Logger().warning(fmt::format("Cannot add '{}' logo in email",AuthService::GetLogoAssetFileName())); } @@ -178,7 +181,7 @@ namespace OpenWifi { SenderLoginUserName_, SenderLoginPassword_ ); - session.sendMessage(Message); + session.sendMessage(*Message); session.close(); return MessageSendStatus::msg_sent; } diff --git a/src/SMTPMailerService.h b/src/SMTPMailerService.h index b765a2a..ed51eeb 100644 --- a/src/SMTPMailerService.h +++ b/src/SMTPMailerService.h @@ -102,6 +102,8 @@ namespace OpenWifi { Poco::Thread SenderThr_; std::atomic_bool Running_=false; bool Enabled_=false; + bool UseHTML_=false; + std::string EmailLogo_{"logo.jpg"}; SMTPMailerService() noexcept: SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer") diff --git a/templates/email_invitation.html b/templates/email_invitation.html new file mode 100644 index 0000000..566549b --- /dev/null +++ b/templates/email_invitation.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + \ No newline at end of file diff --git a/templates/email_invitation.txt b/templates/email_invitation.txt new file mode 100644 index 0000000..144f578 --- /dev/null +++ b/templates/email_invitation.txt @@ -0,0 +1,7 @@ +Dear ${RECIPIENT_EMAIL}, + + You have been invited to join the OpenWifi system. Please click below and follow the instructions + + ${ACTION_LINK} + +Thank you! \ No newline at end of file diff --git a/wwwassets/invitation_error.html b/wwwassets/invitation_error.html new file mode 100644 index 0000000..e69de29 diff --git a/wwwassets/invitation_success.html b/wwwassets/invitation_success.html new file mode 100644 index 0000000..e69de29