From d805fd2d502b2c09198008d2d7c8201c71762603 Mon Sep 17 00:00:00 2001 From: stephb9959 Date: Fri, 15 Oct 2021 10:40:21 -0700 Subject: [PATCH] Adding Twilio SMS support. Refactored for multiple SMS providers. --- CMakeLists.txt | 2 +- build | 2 +- src/SMSSender.cpp | 42 +++++--------------- src/SMSSender.h | 7 +--- src/SMS_provider.cpp | 5 +++ src/SMS_provider.h | 24 ++++++++++++ src/SMS_provider_aws.cpp | 60 +++++++++++++++++++++++++++++ src/SMS_provider_aws.h | 34 +++++++++++++++++ src/SMS_provider_twilio.cpp | 76 +++++++++++++++++++++++++++++++++++++ src/SMS_provider_twilio.h | 30 +++++++++++++++ 10 files changed, 242 insertions(+), 40 deletions(-) create mode 100644 src/SMS_provider.cpp create mode 100644 src/SMS_provider.h create mode 100644 src/SMS_provider_aws.cpp create mode 100644 src/SMS_provider_aws.h create mode 100644 src/SMS_provider_twilio.cpp create mode 100644 src/SMS_provider_twilio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 15d3d9e..ff6966a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ add_executable( owsec src/RESTAPI_GenericServer.h src/RESTAPI_GenericServer.cpp src/RESTAPI_errors.h src/Storage.h - src/SMSSender.cpp src/SMSSender.h src/RESTAPI_sms_handler.cpp src/RESTAPI_sms_handler.h src/MFAServer.cpp src/MFAServer.h) + src/SMSSender.cpp src/SMSSender.h src/RESTAPI_sms_handler.cpp src/RESTAPI_sms_handler.h src/MFAServer.cpp src/MFAServer.h src/SMS_provider_aws.cpp src/SMS_provider_aws.h src/SMS_provider.cpp src/SMS_provider.h src/SMS_provider_twilio.cpp src/SMS_provider_twilio.h) if(NOT SMALL_BUILD) target_link_libraries(owsec PUBLIC diff --git a/build b/build index c5b431b..8783e30 100644 --- a/build +++ b/build @@ -1 +1 @@ -50 \ No newline at end of file +53 \ No newline at end of file diff --git a/src/SMSSender.cpp b/src/SMSSender.cpp index 567bc43..b3462fc 100644 --- a/src/SMSSender.cpp +++ b/src/SMSSender.cpp @@ -10,25 +10,20 @@ #include #include "MFAServer.h" +#include "SMS_provider_aws.h" +#include "SMS_provider_twilio.h" + namespace OpenWifi { class SMSSender * SMSSender::instance_ = nullptr; int SMSSender::Start() { Provider_ = Daemon()->ConfigGetString("sms.provider","aws"); if(Provider_=="aws") { - SecretKey_ = Daemon()->ConfigGetString("smssender.aws.secretkey",""); - AccessKey_ = Daemon()->ConfigGetString("smssender.aws.accesskey",""); - Region_ = Daemon()->ConfigGetString("smssender.aws.region",""); - - if(SecretKey_.empty() || AccessKey_.empty() || Region_.empty()) { - Logger_.debug("SMSSender is disabled. Please provide key, secret, and region."); - return -1; - } - Enabled_=true; - AwsConfig_.region = Region_; - AwsCreds_.SetAWSAccessKeyId(AccessKey_.c_str()); - AwsCreds_.SetAWSSecretKey(SecretKey_.c_str()); + ProviderImpl_ = std::make_unique(Logger_); + } else if(Provider_=="twilio") { + ProviderImpl_ = std::make_unique(Logger_); } + Enabled_ = ProviderImpl_->Initialize(); return 0; } @@ -52,7 +47,7 @@ namespace OpenWifi { auto Challenge = MFAServer::MakeChallenge(); Cache_.emplace_back(SMSValidationCacheEntry{.Number=Number, .Code=Challenge, .UserName=UserName, .Created=Now}); std::string Message = "Please enter the following code on your login screen: " + Challenge; - return Send(Number, Message); + return ProviderImpl_->Send(Number, Message); } bool SMSSender::IsNumberValid(const std::string &Number, const std::string &UserName) { @@ -77,30 +72,11 @@ namespace OpenWifi { return false; } - bool SMSSender::SendAWS(const std::string &PhoneNumber, const std::string &Message) { - Aws::SNS::SNSClient sns(AwsCreds_,AwsConfig_); - - Aws::SNS::Model::PublishRequest psms_req; - psms_req.SetMessage(Message.c_str()); - psms_req.SetPhoneNumber(PhoneNumber.c_str()); - - auto psms_out = sns.Publish(psms_req); - if (psms_out.IsSuccess()) { - Logger_.debug(Poco::format("SMS sent to %s",PhoneNumber)); - return true; - } - std::string ErrMsg = psms_out.GetError().GetMessage().c_str(); - Logger_.debug(Poco::format("SMS NOT sent to %s: %s",PhoneNumber, ErrMsg)); - return false; - } - bool SMSSender::Send(const std::string &PhoneNumber, const std::string &Message) { if(!Enabled_) { Logger_.information("SMS has not been enabled. Messages cannot be sent."); return false; } - if(Provider_=="aws") - return SendAWS(PhoneNumber, Message); - return false; + return ProviderImpl_->Send(PhoneNumber,Message); } } \ No newline at end of file diff --git a/src/SMSSender.h b/src/SMSSender.h index 89ad8ea..881f38a 100644 --- a/src/SMSSender.h +++ b/src/SMSSender.h @@ -9,6 +9,7 @@ #include #include #include +#include "SMS_provider.h" namespace OpenWifi { @@ -38,14 +39,10 @@ namespace OpenWifi { [[nodiscard]] bool Send(const std::string &PhoneNumber, const std::string &Message); private: static SMSSender * instance_; - std::string SecretKey_; - std::string AccessKey_; - std::string Region_; std::string Provider_; - Aws::Client::ClientConfiguration AwsConfig_; - Aws::Auth::AWSCredentials AwsCreds_; bool Enabled_=false; std::vector Cache_; + std::unique_ptr ProviderImpl_; SMSSender() noexcept: SubSystemServer("SMSSender", "SMS-SVR", "smssender.aws") diff --git a/src/SMS_provider.cpp b/src/SMS_provider.cpp new file mode 100644 index 0000000..1afd16c --- /dev/null +++ b/src/SMS_provider.cpp @@ -0,0 +1,5 @@ +// +// Created by stephane bourque on 2021-10-15. +// + +#include "SMS_provider.h" diff --git a/src/SMS_provider.h b/src/SMS_provider.h new file mode 100644 index 0000000..1d1f423 --- /dev/null +++ b/src/SMS_provider.h @@ -0,0 +1,24 @@ +// +// Created by stephane bourque on 2021-10-15. +// + +#ifndef OWSEC_SMS_PROVIDER_H +#define OWSEC_SMS_PROVIDER_H + +#include "Poco/Logger.h" + +namespace OpenWifi { + class SMS_provider { + public: + virtual bool Initialize() = 0 ; + virtual bool Start() = 0 ; + virtual bool Stop() = 0 ; + virtual bool Running() = 0 ; + virtual bool Send(const std::string &Number, const std::string &Message) = 0; + virtual ~SMS_provider() {}; + private: + }; +} + + +#endif //OWSEC_SMS_PROVIDER_H diff --git a/src/SMS_provider_aws.cpp b/src/SMS_provider_aws.cpp new file mode 100644 index 0000000..11176e6 --- /dev/null +++ b/src/SMS_provider_aws.cpp @@ -0,0 +1,60 @@ +// +// Created by stephane bourque on 2021-10-15. +// + +#include "SMS_provider_aws.h" +#include "Daemon.h" + +#include +#include +#include + +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",""); + + if(SecretKey_.empty() || AccessKey_.empty() || Region_.empty()) { + Logger_.debug("SMSSender is disabled. Please provide key, secret, and region."); + return false; + } + Running_=true; + AwsConfig_.region = Region_; + AwsCreds_.SetAWSAccessKeyId(AccessKey_.c_str()); + AwsCreds_.SetAWSSecretKey(SecretKey_.c_str()); + return true; + } + + bool SMS_provider_aws::Start() { + return true; + } + + bool SMS_provider_aws::Stop() { + return true; + } + + bool SMS_provider_aws::Running() { + return Running_; + } + + bool SMS_provider_aws::Send(const std::string &PhoneNumber, const std::string &Message) { + if(!Running_) + return false; + + Aws::SNS::SNSClient sns(AwsCreds_,AwsConfig_); + Aws::SNS::Model::PublishRequest psms_req; + psms_req.SetMessage(Message.c_str()); + psms_req.SetPhoneNumber(PhoneNumber.c_str()); + + auto psms_out = sns.Publish(psms_req); + if (psms_out.IsSuccess()) { + Logger_.debug(Poco::format("SMS sent to %s",PhoneNumber)); + return true; + } + std::string ErrMsg{psms_out.GetError().GetMessage()}; + Logger_.debug(Poco::format("SMS NOT sent to %s: %s",PhoneNumber, ErrMsg)); + return false; + } + +} \ No newline at end of file diff --git a/src/SMS_provider_aws.h b/src/SMS_provider_aws.h new file mode 100644 index 0000000..c79a0f4 --- /dev/null +++ b/src/SMS_provider_aws.h @@ -0,0 +1,34 @@ +// +// Created by stephane bourque on 2021-10-15. +// + +#ifndef OWSEC_SMS_PROVIDER_AWS_H +#define OWSEC_SMS_PROVIDER_AWS_H + +#include "SMS_provider.h" +#include +#include +#include + +namespace OpenWifi { + class SMS_provider_aws : public SMS_provider { + public: + explicit SMS_provider_aws(Poco::Logger &L) : Logger_(L) {} + ~SMS_provider_aws() {}; + bool Initialize() final ; + bool Start() final ; + bool Stop() final ; + bool Send(const std::string &Number, const std::string &Message) final; + bool Running() final; + private: + bool Running_=false; + Poco::Logger &Logger_; + std::string SecretKey_; + std::string AccessKey_; + std::string Region_; + Aws::Client::ClientConfiguration AwsConfig_; + Aws::Auth::AWSCredentials AwsCreds_; + }; +} + +#endif //OWSEC_SMS_PROVIDER_AWS_H diff --git a/src/SMS_provider_twilio.cpp b/src/SMS_provider_twilio.cpp new file mode 100644 index 0000000..7d0d275 --- /dev/null +++ b/src/SMS_provider_twilio.cpp @@ -0,0 +1,76 @@ +// +// Created by stephane bourque on 2021-10-15. +// + +#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" + +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",""); + + if(Sid_.empty() || Token_.empty() || PhoneNumber_.empty()) { + Logger_.debug("SMSSender is disabled. Please provide SID, TOKEN, and PHONE NUMBER."); + return false; + } + Running_=true; + Uri_ = "https://api.twilio.com/2010-04-01/Accounts/" + Sid_ + "/Messages.json"; + return true; + } + + bool SMS_provider_twilio::Start() { + return true; + } + + bool SMS_provider_twilio::Stop() { + return true; + } + + bool SMS_provider_twilio::Running() { + return Running_; + } + + bool SMS_provider_twilio::Send(const std::string &PhoneNumber, const std::string &Message) { + if(!Running_) + return false; + + Poco::Net::HTTPBasicCredentials Creds(Sid_,Token_); + Poco::URI uri(Uri_); + Poco::Net::HTMLForm form; + + Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort()); + Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_POST, uri.getPath(), Poco::Net::HTTPMessage::HTTP_1_1); + Creds.authenticate(req); + + Poco::JSON::Object RObj; + + form.add("To",PhoneNumber); + form.add("From",PhoneNumber_); + form.add("Body","This is from twillio"); + + form.prepareSubmit(req); + std::ostream& ostr = session.sendRequest(req); + form.write(ostr); + + Poco::Net::HTTPResponse res; + std::istream& rs = session.receiveResponse(res); + + if(res.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) { + Logger_.information(Poco::format("Message sent to %s", PhoneNumber)); + return true; + } else { + std::ostringstream os; + Poco::StreamCopier::copyStream(rs,os); + Logger_.information(Poco::format("Message was not to %s: Error:%s", PhoneNumber, os.str())); + return false; + } + } +} \ No newline at end of file diff --git a/src/SMS_provider_twilio.h b/src/SMS_provider_twilio.h new file mode 100644 index 0000000..eb89f8d --- /dev/null +++ b/src/SMS_provider_twilio.h @@ -0,0 +1,30 @@ +// +// Created by stephane bourque on 2021-10-15. +// + +#ifndef OWSEC_SMS_PROVIDER_TWILIO_H +#define OWSEC_SMS_PROVIDER_TWILIO_H + +#include "SMS_provider.h" + +namespace OpenWifi { + class SMS_provider_twilio : public SMS_provider { + public: + explicit SMS_provider_twilio(Poco::Logger &L) : Logger_(L) {} + ~SMS_provider_twilio() {}; + bool Initialize() final ; + bool Start() final ; + bool Stop() final ; + bool Send(const std::string &Number, const std::string &Message) final; + bool Running() final; + private: + bool Running_=false; + Poco::Logger &Logger_; + std::string Sid_; + std::string Token_; + std::string PhoneNumber_; + std::string Uri_; + }; +} + +#endif //OWSEC_SMS_PROVIDER_TWILIO_H