From d15a1a3cc4fbccac2cf520ee18352e2ae6db4fce Mon Sep 17 00:00:00 2001 From: stephb9959 Date: Mon, 28 Jun 2021 23:45:25 -0700 Subject: [PATCH] New router and simplifiedrest handler --- CMakeLists.txt | 4 +- openpapi/ucentralsec/ucentralsec.yaml | 46 +++++++ set_env.sh | 4 + src/Daemon.cpp | 5 +- src/Daemon.h | 4 - src/KafkaManager.cpp | 123 ++++++++++--------- src/KafkaManager.h | 12 +- src/Kafka_topics.h | 17 +++ src/MicroService.cpp | 159 +++++++++++++++++++++++-- src/MicroService.h | 43 ++++++- src/RESTAPI_action_links.h | 1 + src/RESTAPI_handler.cpp | 42 ++++--- src/RESTAPI_handler.h | 57 +++++++-- src/RESTAPI_oauth2Handler.cpp | 86 ++++++------- src/RESTAPI_oauth2Handler.h | 23 ++-- src/RESTAPI_objects.cpp | 93 ++++++++++++++- src/RESTAPI_objects.h | 74 ++++++++---- src/RESTAPI_server.cpp | 26 ++-- src/RESTAPI_systemServices_handler.cpp | 11 ++ src/RESTAPI_systemServices_handler.h | 27 +++++ src/RESTAPI_system_command.h | 1 + src/RESTAPI_unknownRequestHandler.cpp | 16 --- src/RESTAPI_unknownRequestHandler.h | 25 ---- src/RESTAPI_user_handler.h | 2 + src/RESTAPI_users_handler.h | 3 +- src/StorageService.h | 55 ++++++++- src/Utils.cpp | 13 +- src/Utils.h | 2 + src/storage_tables.cpp | 8 +- src/storage_users.cpp | 38 ++++++ src/uCentralTypes.h | 2 +- ucentralsec.properties | 23 ++-- 32 files changed, 772 insertions(+), 273 deletions(-) create mode 100755 set_env.sh create mode 100644 src/Kafka_topics.h create mode 100644 src/RESTAPI_systemServices_handler.cpp create mode 100644 src/RESTAPI_systemServices_handler.h delete mode 100644 src/RESTAPI_unknownRequestHandler.cpp delete mode 100644 src/RESTAPI_unknownRequestHandler.h create mode 100644 src/storage_users.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 15c7bbd..1e3adf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,6 @@ add_executable( ucentralsec src/Daemon.h src/Daemon.cpp src/MicroService.cpp src/MicroService.h src/SubSystemServer.cpp src/SubSystemServer.h - src/RESTAPI_unknownRequestHandler.h src/RESTAPI_unknownRequestHandler.cpp src/RESTAPI_oauth2Handler.h src/RESTAPI_oauth2Handler.cpp src/RESTAPI_handler.h src/RESTAPI_handler.cpp src/RESTAPI_server.cpp src/RESTAPI_server.h @@ -70,7 +69,8 @@ add_executable( ucentralsec src/storage_tables.cpp src/SMTPMailerService.cpp src/SMTPMailerService.h src/RESTAPI_users_handler.cpp src/RESTAPI_users_handler.h src/RESTAPI_user_handler.cpp src/RESTAPI_user_handler.h - src/RESTAPI_action_links.cpp src/RESTAPI_action_links.h) + src/RESTAPI_action_links.cpp src/RESTAPI_action_links.h src/storage_users.cpp + src/RESTAPI_systemServices_handler.cpp src/RESTAPI_systemServices_handler.h) target_link_libraries(ucentralsec PUBLIC ${Poco_LIBRARIES} ${Boost_LIBRARIES} ${ZLIB_LIBRARIES} ${AWSSDK_LINK_LIBRARIES} CppKafka::cppkafka ) diff --git a/openpapi/ucentralsec/ucentralsec.yaml b/openpapi/ucentralsec/ucentralsec.yaml index 9b96faf..0702488 100644 --- a/openpapi/ucentralsec/ucentralsec.yaml +++ b/openpapi/ucentralsec/ucentralsec.yaml @@ -308,6 +308,11 @@ components: - instagram oauthUserInfo: type: string + securityPolicy: + type: string + securityPolicyChange: + type: integer + format: int64 UserList: type: object @@ -417,6 +422,29 @@ components: items: $ref: '#/components/schemas/SecurityProfile' + InternalServiceInfo: + type: object + properties: + privateURI: + type: string + publicURI: + type: string + auth: + type: string + + + InternalSystemServices: + type: object + properties: + key: + type: string + version: + type: integer + services: + type: array + items: + $ref: '#/components/schemas/InternalServiceInfo' + ######################################################################################### ## ## End of uCentral system wide values @@ -741,6 +769,24 @@ paths: 404: $ref: '#/components/responses/NotFound' +######################################################################################### +## The following calls are restricted to the private system side APIs +######################################################################################### + /systemServices: + get: + tags: + - Security + summary: Retrieve the basic system information. This information is used between services only. + operationId: getSystemServices + responses: + 200: + $ref: '#/components/schemas/InternalSystemServices' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + + ######################################################################################### ## ## These are endpoints that all services in the uCentral stack must provide diff --git a/set_env.sh b/set_env.sh new file mode 100755 index 0000000..0b1000b --- /dev/null +++ b/set_env.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +export UCENTRALSEC_CONFIG=`pwd` +export UCENTRALSEC_ROOT=`pwd` diff --git a/src/Daemon.cpp b/src/Daemon.cpp index 5206428..a3d2c61 100644 --- a/src/Daemon.cpp +++ b/src/Daemon.cpp @@ -38,9 +38,7 @@ namespace uCentral { Types::SubSystemVec{ Storage(), RESTAPI_Server(), - KafkaManager(), - SMTPMailerService(), - ALBHealthCheckServer() + SMTPMailerService() }); } return instance_; @@ -49,7 +47,6 @@ namespace uCentral { void Daemon::initialize(Poco::Util::Application &self) { MicroService::initialize(*this); } - } int main(int argc, char **argv) { diff --git a/src/Daemon.h b/src/Daemon.h index 3e12091..4e81a61 100644 --- a/src/Daemon.h +++ b/src/Daemon.h @@ -39,14 +39,10 @@ namespace uCentral { Types::SubSystemVec SubSystems) : MicroService( PropFile, RootEnv, ConfigEnv, AppName, SubSystems) {}; - bool AutoProvisioning() const { return AutoProvisioning_ ; } - [[nodiscard]] std::string IdentifyDevice(const std::string & Compatible) const; void initialize(Poco::Util::Application &self); static Daemon *instance(); private: static Daemon *instance_; - bool AutoProvisioning_ = false; - Types::StringMapStringSet DeviceTypeIdentifications_; }; inline Daemon * Daemon() { return Daemon::instance(); } diff --git a/src/KafkaManager.cpp b/src/KafkaManager.cpp index 82e9bdd..857d70f 100644 --- a/src/KafkaManager.cpp +++ b/src/KafkaManager.cpp @@ -38,93 +38,104 @@ namespace uCentral { int KafkaManager::Start() { if(!KafkaEnabled_) return 0; - Running_ = true; - ProducerThr_ = std::make_unique(Producer,this); - ProducerThr_->detach(); - ConsumerThr_ = std::make_unique(Consumer,this); - ConsumerThr_->detach(); + ProducerThr_ = std::make_unique([this]() { this->Producer(); }); + ConsumerThr_ = std::make_unique([this]() { this->Consumer(); }); return 0; } void KafkaManager::Stop() { if(KafkaEnabled_) { - Running_ = false; - ConsumerThr_->join(); + ProducerRunning_ = ConsumerRunning_ = false; ProducerThr_->join(); + ConsumerThr_->join(); return; } } - void KafkaManager::Producer(KafkaManager *Mgr) { + void KafkaManager::Producer() { cppkafka::Configuration Config({ - { "metadata.broker.list", Daemon()->ConfigGetString("ucentral.kafka.brokerlist") } , - { "enable.auto.commit", Daemon()->ConfigGetBool("ucentral.kafka.auto.commit", false)} + { "metadata.broker.list", Daemon()->ConfigGetString("ucentral.kafka.brokerlist") } }); - Mgr->SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" + - std::to_string(Daemon()->ConfigGetInt("ucentral.system.id")) + - R"lit( , "host" : ")lit" + Daemon()->ConfigGetString("ucentral.system.uri") + + 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); - - while(Mgr->Running_) { - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - if(!Mgr->Running_) - break; + ProducerRunning_ = true; + while(ProducerRunning_) { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + try { - SubMutexGuard G(Mgr->ProducerMutex_); - while (!Mgr->Queue_.empty() && Mgr->Running_) { - const auto M = Mgr->Queue_.front(); - // std::cout << "Producing Topic: " << M.Topic << " Key: " << M.Key <Queue_.pop(); + Queue_.pop(); } - // Producer_->flush(); - + 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::Consumer(KafkaManager *Mgr ) { + void KafkaManager::Consumer() { cppkafka::Configuration Config({ - { "group.id", 1 }, + { "group.id", Daemon()->ConfigGetString("ucentral.kafka.group.id") }, { "enable.auto.commit", Daemon()->ConfigGetBool("ucentral.kafka.auto.commit",false) }, - { "metadata.broker.list", Daemon()->ConfigGetString("ucentral.kafka.brokerlist") } + { "metadata.broker.list", Daemon()->ConfigGetString("ucentral.kafka.brokerlist") }, + { "auto.offset.reset", "earliest" } , + { "enable.partition.eof", false } }); cppkafka::Consumer Consumer(Config); - - Consumer.set_assignment_callback([Mgr](const cppkafka::TopicPartitionList& partitions) { - Mgr->Logger_.information(Poco::format("Got assigned: %Lu...",(uint64_t )partitions.front().get_partition())); + Consumer.set_assignment_callback([=](const cppkafka::TopicPartitionList& partitions) { + std::cout << "Partition assigned: " << partitions.front().get_partition() << std::endl; + Logger_.information(Poco::format("Got assigned: %Lu...",(uint64_t )partitions.front().get_partition())); }); - Consumer.set_revocation_callback([Mgr](const cppkafka::TopicPartitionList& partitions) { - Mgr->Logger_.information(Poco::format("Got revoked: %Lu...",(uint64_t )partitions.front().get_partition())); + Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) { + std::cout << "Partition revocation: " << partitions.front().get_partition() << std::endl; + Logger_.information(Poco::format("Got revoked: %Lu...",(uint64_t )partitions.front().get_partition())); }); - std::vector Topics; - for(const auto &i:Mgr->Notifiers_) + Types::StringVec Topics; + for(const auto &i:Notifiers_) Topics.push_back(i.first); Consumer.subscribe(Topics); - while(Mgr->Running_) { - cppkafka::Message Msg = Consumer.poll(std::chrono::milliseconds(2000)); - if (Msg) { + + ConsumerRunning_ = true; + while(ConsumerRunning_) { + try { + cppkafka::Message Msg = Consumer.poll(std::chrono::milliseconds(200)); + if (!Msg) + continue;; if (Msg.get_error()) { if (!Msg.is_eof()) { - Mgr->Logger_.error( - Poco::format("Error: %s", Msg.get_error().to_string())); - } - } else { - SubMutexGuard G(Mgr->ConsumerMutex_); - auto It = Mgr->Notifiers_.find(Msg.get_topic()); - if (It != Mgr->Notifiers_.end()) { - Types::TopicNotifyFunctionList &FL = It->second; - for (auto &F : FL) - F.first(Msg.get_key(), Msg.get_payload()); + Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string())); } Consumer.commit(Msg); + continue; } + SubMutexGuard G(ConsumerMutex_); + auto It = Notifiers_.find(Msg.get_topic()); + if (It != Notifiers_.end()) { + Types::TopicNotifyFunctionList &FL = It->second; + for (auto &F : FL) { + std::string Key{Msg.get_key()}; + std::string Payload{Msg.get_payload()}; + std::thread T(F.first, Key, Payload); + T.detach(); + } + } + Consumer.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); } } } @@ -133,19 +144,19 @@ namespace uCentral { return std::move( SystemInfoWrapper_ + PayLoad + "}"); } - void KafkaManager::PostMessage(std::string topic, std::string key, std::string PayLoad) { - if(KafkaEnabled_ && Running_) { + void KafkaManager::PostMessage(std::string topic, std::string key, std::string PayLoad, bool WrapMessage ) { + if(KafkaEnabled_) { SubMutexGuard G(Mutex_); - KMessage M{ - .Topic = std::move(topic), .Key = std::move(key), .PayLoad = std::move(WrapSystemId(PayLoad))}; - // std::cout << "Posting Topic: " << M.Topic << " Key: " << M.Key << " Payload: " << M.PayLoad << std::endl; + .Topic = std::move(topic), + .Key = std::move(key), + .PayLoad = std::move(WrapMessage ? WrapSystemId(PayLoad) : PayLoad )}; Queue_.push(std::move(M)); } } int KafkaManager::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { - if(!Running_) { + if(KafkaEnabled_) { SubMutexGuard G(Mutex_); auto It = Notifiers_.find(Topic); if(It == Notifiers_.end()) { @@ -162,7 +173,7 @@ namespace uCentral { } void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, int Id) { - if(!Running_) { + if(KafkaEnabled_) { SubMutexGuard G(Mutex_); auto It = Notifiers_.find(Topic); if(It != Notifiers_.end()) { diff --git a/src/KafkaManager.h b/src/KafkaManager.h index 8f5599b..83be182 100644 --- a/src/KafkaManager.h +++ b/src/KafkaManager.h @@ -35,24 +35,26 @@ namespace uCentral { return instance_; } - static void Producer(KafkaManager *); - static void Consumer(KafkaManager *); + void Producer(); + void Consumer(); int Start() override; void Stop() override; - void PostMessage(std::string topic, std::string key, std::string payload); + void PostMessage(std::string topic, std::string key, std::string payload, bool WrapMessage = true); [[nodiscard]] std::string WrapSystemId(const std::string & PayLoad); - [[nodiscard]] bool Enabled() { return Running_ && KafkaEnabled_; } + [[nodiscard]] bool Enabled() { return KafkaEnabled_; } int RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction & F); void UnregisterTopicWatcher(const std::string &Topic, int FunctionId); + void WakeUp(); private: static KafkaManager *instance_; SubMutex ProducerMutex_; SubMutex ConsumerMutex_; bool KafkaEnabled_ = false; - std::atomic_bool Running_ = false; + std::atomic_bool ProducerRunning_ = false; + std::atomic_bool ConsumerRunning_ = false; std::queue Queue_; std::string SystemInfoWrapper_; std::unique_ptr ConsumerThr_; diff --git a/src/Kafka_topics.h b/src/Kafka_topics.h new file mode 100644 index 0000000..9c3d030 --- /dev/null +++ b/src/Kafka_topics.h @@ -0,0 +1,17 @@ +// +// Created by stephane bourque on 2021-06-07. +// + +#ifndef UCENTRALGW_KAFKA_TOPICS_H +#define UCENTRALGW_KAFKA_TOPICS_H + +namespace uCentral::KafkaTopics { + static const std::string HEALTHCHECK{"healthcheck"}; + static const std::string STATE{"state"}; + static const std::string CONNECTION{"connection"}; + static const std::string WIFISCAN{"wifiscan"}; + static const std::string ALERTS{"alerts"}; + static const std::string COMMAND{"command"}; + static const std::string SERVICE_EVENTS{"service_events"}; +} +#endif // UCENTRALGW_KAFKA_TOPICS_H diff --git a/src/MicroService.cpp b/src/MicroService.cpp index 6ffc983..66909a6 100644 --- a/src/MicroService.cpp +++ b/src/MicroService.cpp @@ -17,10 +17,22 @@ #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 "ALBHealthCheckServer.h" +#ifndef SMALL_BUILD +#include "KafkaManager.h" +#endif +#include "Kafka_topics.h" #include "MicroService.h" #include "Utils.h" +#undef DBGLINE +#define DBGLINE + namespace uCentral { void MyErrorHandler::exception(const Poco::Exception & E) { @@ -43,7 +55,84 @@ namespace uCentral { std::exit(Reason); } + void MicroService::BusMessageReceived(std::string Key, std::string Message) { + SubMutexGuard G(InfraMutex_); + // std::cout << "Message arrived:" << Key << " ," << Message << std::endl; + try { + Poco::JSON::Parser P; + auto Object = P.parse(Message).extract(); + if(Object->has("id")) { + uint64_t ID = Object->get("id"); + if(ID!=ID_) { + if( Object->has("event") && + Object->has("type") && + Object->has("publicEndPoint") && + Object->has("privateEndPoint") && + Object->has("version") && + Object->has("key")) { + auto Event = Object->get("event").toString(); + + if(Event == "keep-alive" && Services_.find(ID)!=Services_.end()) { + std::cout << "Keep-alive from " << ID << std::endl; + Services_[ID].LastUpdate = std::time(nullptr); + } else if (Event=="leave") { + Services_.erase(ID); + std::cout << "Leave from " << ID << std::endl; + } else if (Event== "join" || Event=="keep_alive") { + std::cout << "Join from " << ID << std::endl; + Services_[ID] = MicroServiceMeta{ + .Id = ID, + .Type = Poco::toLower(Object->get("type").toString()), + .PrivateEndPoint = Object->get("privateEndPoint").toString(), + .PublicEndPoint = Object->get("publicEndPoint").toString(), + .AccessKey = Object->get("key").toString(), + .Version = Object->get("version").toString(), + .LastUpdate = (uint64_t )std::time(nullptr) }; + for(const auto &[Id,Svc]:Services_) + std::cout << "ID:" << Id << " Type:" << Svc.Type << " EndPoint:" << Svc.PublicEndPoint << std::endl; + } else { + std::cout << "Bad packet 2 ..." << std::endl; + logger().error(Poco::format("Malformed event from device %Lu, event=%s", ID, Event)); + } + } else { + std::cout << "Bad packet 1 ..." << std::endl; + logger().error(Poco::format("Malformed event from device %Lu", ID)); + } + + } else { + std::cout << "Ignoring my own messages..." << std::endl; + } + } + } catch (const Poco::Exception &E) { + DBGLINE + logger().log(E); + DBGLINE + } + DBGLINE + } + + MicroServiceMetaVec MicroService::GetServices(const std::string & Type) { + SubMutexGuard 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; + } + void MicroService::initialize(Poco::Util::Application &self) { + + std::string V{APP_VERSION}; + std::string B{BUILD_NUMBER}; + Version_ = V + "(" + B + ")"; + + // add the default services + SubSystems_.push_back(KafkaManager()); + SubSystems_.push_back(ALBHealthCheckServer()); + Poco::Net::initializeSSL(); Poco::Net::HTTPStreamFactory::registerFactory(); Poco::Net::HTTPSStreamFactory::registerFactory(); @@ -87,9 +176,14 @@ namespace uCentral { ID_ = Utils::GetSystemId(); if(!DebugMode_) DebugMode_ = ConfigGetBool("ucentral.system.debug",false); - + MyPrivateEndPoint_ = ConfigGetString("ucentral.system.uri.private"); + MyPublicEndPoint_ = ConfigGetString("ucentral.system.uri.public"); + MyHash_ = CreateHash(MyPrivateEndPoint_); 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() { @@ -139,12 +233,6 @@ namespace uCentral { } - std::string MicroService::Version() { - std::string V = APP_VERSION; - std::string B = BUILD_NUMBER; - return V + "(" + B + ")"; - } - void MicroService::handleHelp(const std::string &name, const std::string &value) { HelpRequested_ = true; displayHelp(); @@ -186,9 +274,11 @@ namespace uCentral { 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(); } @@ -286,6 +376,58 @@ namespace uCentral { 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("event",Type); + Obj.set("id",ID_); + Obj.set("type",Poco::toLower(DAEMON_APP_NAME)); + Obj.set("publicEndPoint",MyPublicEndPoint_); + Obj.set("privateEndPoint",MyPrivateEndPoint_); + Obj.set("key",MyHash_); + Obj.set("version",Version_); + std::stringstream ResultText; + Poco::JSON::Stringifier::stringify(Obj, ResultText); + return ResultText.str(); + } + + void BusEventManager::run() { + + Running_ = true; + + auto Msg = Daemon()->MakeSystemEventMessage("join"); + KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false); + + while(Running_) { + Poco::Thread::trySleep(60000); + if(!Running_) + break; + auto Msg = Daemon()->MakeSystemEventMessage("keep-alive"); + KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false); + } + + Msg = Daemon()->MakeSystemEventMessage("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(); + } + } + int MicroService::main(const ArgVec &args) { MyErrorHandler ErrorHandler(*this); @@ -313,7 +455,4 @@ namespace uCentral { return Application::EXIT_OK; } - - - } \ No newline at end of file diff --git a/src/MicroService.h b/src/MicroService.h index 92c2081..4b2e90f 100644 --- a/src/MicroService.h +++ b/src/MicroService.h @@ -20,6 +20,7 @@ #include "Poco/Crypto/RSAKey.h" #include "Poco/Crypto/CipherFactory.h" #include "Poco/Crypto/Cipher.h" +#include "Poco/SHA2Engine.h" #include "uCentralTypes.h" #include "SubSystemServer.h" @@ -36,6 +37,29 @@ namespace uCentral { Poco::Util::Application &App_; }; + class BusEventManager : public Poco::Runnable { + public: + void run() override; + void Start(); + void Stop(); + private: + std::atomic_bool Running_ = false; + Poco::Thread Thread_; + }; + + struct MicroServiceMeta { + uint64_t Id=0; + std::string Type; + std::string PrivateEndPoint; + std::string PublicEndPoint; + std::string AccessKey; + std::string Version; + uint64_t LastUpdate=0; + }; + + typedef std::map MicroServiceMetaMap; + typedef std::vector MicroServiceMetaVec; + class MicroService : public Poco::Util::ServerApplication { public: explicit MicroService( std::string PropFile, @@ -66,7 +90,7 @@ namespace uCentral { void StopSubSystemServers(); void Exit(int Reason); bool SetSubsystemLogLevel(const std::string & SubSystem, const std::string & Level); - [[nodiscard]] static std::string Version(); + [[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(); @@ -85,6 +109,15 @@ namespace uCentral { [[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 ; + + void BusMessageReceived( std::string Key, std::string Message); + [[nodiscard]] MicroServiceMetaVec GetServices(const std::string & type); private: bool HelpRequested_ = false; @@ -98,6 +131,14 @@ namespace uCentral { Types::SubSystemVec SubSystems_; Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory(); Poco::Crypto::Cipher * Cipher_ = nullptr; + Poco::SHA2Engine SHA2_; + MicroServiceMetaMap Services_; + std::string MyHash_; + std::string MyPrivateEndPoint_; + std::string MyPublicEndPoint_; + std::string Version_; + BusEventManager BusEventManager_; + SubMutex InfraMutex_; std::string DAEMON_PROPERTIES_FILENAME; std::string DAEMON_ROOT_ENV_VAR; diff --git a/src/RESTAPI_action_links.h b/src/RESTAPI_action_links.h index c434991..45e34f5 100644 --- a/src/RESTAPI_action_links.h +++ b/src/RESTAPI_action_links.h @@ -19,6 +19,7 @@ namespace uCentral { Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override; + static const std::list PathName() { return std::list{"/api/v1/actions"}; }; }; } diff --git a/src/RESTAPI_handler.cpp b/src/RESTAPI_handler.cpp index 7a06ea6..c1de843 100644 --- a/src/RESTAPI_handler.cpp +++ b/src/RESTAPI_handler.cpp @@ -11,40 +11,46 @@ #include #include #include -#include #include #include "Poco/URI.h" -#include "Poco/DateTimeParser.h" -#include "RESTAPI_handler.h" #include "AuthService.h" +#include "RESTAPI_handler.h" #include "RESTAPI_protocol.h" #include "Utils.h" #define DBG std::cout << __LINE__ << " " __FILE__ << std::endl; namespace uCentral { - bool RESTAPIHandler::ParseBindings(const std::string & Request, const std::string & Path, BindingMap &bindings) { + + bool RESTAPIHandler::ParseBindings(const std::string & Request, const std::list & EndPoints, BindingMap &bindings) { std::string Param, Value; bindings.clear(); - std::vector PathItems = uCentral::Utils::Split(Path,'/'); - std::vector ParamItems = uCentral::Utils::Split(Request,'/'); + std::vector PathItems = uCentral::Utils::Split(Request, '/'); - if(PathItems.size()!=ParamItems.size()) - return false; + for(const auto &EndPoint:EndPoints) { + std::vector ParamItems = uCentral::Utils::Split(EndPoint, '/'); + if (PathItems.size() != ParamItems.size()) + continue; - for(auto i=0;i!=PathItems.size();i++) { - if (PathItems[i] != ParamItems[i]) { - if (PathItems[i][0] == '{') { - auto ParamName = PathItems[i].substr(1, PathItems[i].size() - 2); - bindings[ParamName] = ParamItems[i]; - } else - return false; + bool Matched = true; + for (auto i = 0; i != PathItems.size() && Matched; i++) { + // std::cout << "PATH:" << PathItems[i] << " ENDPOINT:" << ParamItems[i] << std::endl; + if (PathItems[i] != ParamItems[i]) { + if (ParamItems[i][0] == '{') { + auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2); + bindings[ParamName] = PathItems[i]; + } else { + Matched = false; + } + } } + if(Matched) + return true; } - return true; + return false; } void RESTAPIHandler::PrintBindings() { @@ -242,7 +248,7 @@ namespace uCentral { bool RESTAPIHandler::IsAuthorized(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { - if (AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) { + if (uCentral::AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) { return true; } else { UnAuthorized(Request, Response); @@ -253,7 +259,7 @@ namespace uCentral { bool RESTAPIHandler::IsAuthorized(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response, std::string &UserName) { - if (AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) { + if (uCentral::AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) { UserName = UserInfo_.username_; return true; } else { diff --git a/src/RESTAPI_handler.h b/src/RESTAPI_handler.h index 6894b13..f3a8706 100644 --- a/src/RESTAPI_handler.h +++ b/src/RESTAPI_handler.h @@ -19,25 +19,24 @@ #include "Poco/File.h" #include "Poco/JSON/Object.h" -#include "RESTAPI_objects.h" #include "AuthService.h" +#include "RESTAPI_objects.h" namespace uCentral { - 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; - }; - 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; + }; typedef std::map BindingMap; RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector Methods) : Bindings_(std::move(map)), Logger_(l), Methods_(std::move(Methods)) {} - static bool ParseBindings(const std::string & Path, const std::string & Request, BindingMap &Keys); + static bool ParseBindings(const std::string & Request, const std::list & EndPoints, BindingMap &Keys); void PrintBindings(); void ParseParameters(Poco::Net::HTTPServerRequest &request); @@ -94,14 +93,52 @@ namespace uCentral { [[nodiscard]] static uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj); protected: - BindingMap Bindings_; - Poco::URI::QueryParameters Parameters_; + BindingMap Bindings_; + Poco::URI::QueryParameters Parameters_; Poco::Logger &Logger_; std::string SessionToken_; struct uCentral::Objects::WebToken UserInfo_; std::vector Methods_; QueryBlock QB_; }; + + class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { + public: + RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) + : RESTAPIHandler(bindings, L, std::vector{}) {} + void handleRequest(Poco::Net::HTTPServerRequest &Request, + Poco::Net::HTTPServerResponse &Response) override { + if (!IsAuthorized(Request, Response)) + return; + BadRequest(Request, Response); + } + }; + + 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) { + 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); + } + + if constexpr (sizeof...(Args) == 0) { + return new RESTAPI_UnknownRequestHandler(Bindings,Logger); + } else { + return RESTAPI_Router(RequestedPath, Bindings, Logger); + } + } + } #endif //UCENTRAL_RESTAPI_HANDLER_H diff --git a/src/RESTAPI_oauth2Handler.cpp b/src/RESTAPI_oauth2Handler.cpp index fa317d3..014555b 100644 --- a/src/RESTAPI_oauth2Handler.cpp +++ b/src/RESTAPI_oauth2Handler.cpp @@ -8,55 +8,57 @@ #include "Poco/JSON/Parser.h" +#include "AuthService.h" #include "RESTAPI_oauth2Handler.h" #include "RESTAPI_protocol.h" -#include "AuthService.h" namespace uCentral { - void RESTAPI_oauth2Handler::handleRequest(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { - if (!ContinueProcessing(Request, Response)) - return; + void RESTAPI_oauth2Handler::handleRequest(Poco::Net::HTTPServerRequest &Request, + Poco::Net::HTTPServerResponse &Response) { - try { - if (Request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_POST) { + if (!ContinueProcessing(Request, Response)) + return; - // Extract the info for login... - Poco::JSON::Parser parser; - Poco::JSON::Object::Ptr Obj = parser.parse(Request.stream()).extract(); + try { + if (Request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_POST) { - auto userId = GetS(uCentral::RESTAPI::Protocol::USERID, Obj); - auto password = GetS(uCentral::RESTAPI::Protocol::PASSWORD, Obj); + // Extract the info for login... + Poco::JSON::Parser parser; + Poco::JSON::Object::Ptr Obj = + parser.parse(Request.stream()).extract(); - Poco::toLowerInPlace(userId); - uCentral::Objects::WebToken Token; + auto userId = GetS(uCentral::RESTAPI::Protocol::USERID, Obj); + auto password = GetS(uCentral::RESTAPI::Protocol::PASSWORD, Obj); - if (AuthService()->Authorize(userId, password, Token)) { - Poco::JSON::Object ReturnObj; - Token.to_json(ReturnObj); - ReturnObject(Request, ReturnObj, Response); - } else { - UnAuthorized(Request, Response); - } - } else if (Request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_DELETE) { - if (!IsAuthorized(Request, Response)) { - return; - } - auto Token = GetBinding(uCentral::RESTAPI::Protocol::TOKEN, "..."); - if (Token == SessionToken_) { - AuthService()->Logout(Token); - ReturnStatus(Request, Response, Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true); - } else { - NotFound(Request, Response); - } - } else { - BadRequest(Request, Response); - } - return; - } - catch (const Poco::Exception &E) { - Logger_.warning(Poco::format("%s: Failed with: %s", std::string(__func__), E.displayText())); - } - BadRequest(Request, Response); - } + Poco::toLowerInPlace(userId); + uCentral::Objects::WebToken Token; + + if (AuthService()->Authorize(userId, password, Token)) { + Poco::JSON::Object ReturnObj; + Token.to_json(ReturnObj); + ReturnObject(Request, ReturnObj, Response); + } else { + UnAuthorized(Request, Response); + } + } else if (Request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_DELETE) { + if (!IsAuthorized(Request, Response)) { + return; + } + auto Token = GetBinding(uCentral::RESTAPI::Protocol::TOKEN, "..."); + if (Token == SessionToken_) { + AuthService()->Logout(Token); + ReturnStatus(Request, Response, Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true); + } else { + NotFound(Request, Response); + } + } else { + BadRequest(Request, Response); + } + return; + } catch (const Poco::Exception &E) { + Logger_.warning( + Poco::format("%s: Failed with: %s", std::string(__func__), E.displayText())); + } + BadRequest(Request, Response); + } } \ No newline at end of file diff --git a/src/RESTAPI_oauth2Handler.h b/src/RESTAPI_oauth2Handler.h index 49daf22..4a13eac 100644 --- a/src/RESTAPI_oauth2Handler.h +++ b/src/RESTAPI_oauth2Handler.h @@ -12,17 +12,16 @@ #include "RESTAPI_handler.h" namespace uCentral { - class RESTAPI_oauth2Handler : public uCentral::RESTAPIHandler { - public: - RESTAPI_oauth2Handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) - : RESTAPIHandler(bindings, L, - std::vector - {Poco::Net::HTTPRequest::HTTP_POST, - Poco::Net::HTTPRequest::HTTP_DELETE, - Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} - - void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override; - }; + class RESTAPI_oauth2Handler : public RESTAPIHandler { + public: + RESTAPI_oauth2Handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) + : RESTAPIHandler(bindings, L, + std::vector{Poco::Net::HTTPRequest::HTTP_POST, + Poco::Net::HTTPRequest::HTTP_DELETE, + Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} + void handleRequest(Poco::Net::HTTPServerRequest &request, + Poco::Net::HTTPServerResponse &response) override; + static const std::list PathName() { return std::list{"/api/v1/oauth2/{token}","/api/v1/oauth2"}; }; + }; } - #endif //UCENTRAL_RESTAPI_OAUTH2HANDLER_H diff --git a/src/RESTAPI_objects.cpp b/src/RESTAPI_objects.cpp index 5fbc245..4972281 100644 --- a/src/RESTAPI_objects.cpp +++ b/src/RESTAPI_objects.cpp @@ -8,11 +8,7 @@ #include "Poco/JSON/Parser.h" #include "Poco/JSON/Stringifier.h" - -#include "Daemon.h" -#include "RESTAPI_handler.h" #include "RESTAPI_objects.h" -#include "Utils.h" namespace uCentral::Objects { @@ -44,5 +40,94 @@ namespace uCentral::Objects { Obj.set("username",username_); Obj.set("aclTemplate",AclTemplateObj); } + + void UserInfo::to_json(Poco::JSON::Object &Obj) const { + Obj.set("Id",Id); + Obj.set("name",name); + Obj.set("description", description); + Obj.set("avatar", avatar); + Obj.set("email", email); + Obj.set("validated", validated); + Obj.set("validationEmail", validationEmail); + Obj.set("validationDate", validationDate); + Obj.set("creationDate", creationDate); + Obj.set("validationURI", validationURI); + Obj.set("changePassword", changePassword); + Obj.set("lastLogin", lastLogin); + Obj.set("currentLoginURI", currentLoginURI); + Obj.set("lastPasswordChange", lastPasswordChange); + Obj.set("lastEmailCheck", lastEmailCheck); + Obj.set("waitingForEmailCheck", waitingForEmailCheck); + Obj.set("locale", locale); + Obj.set("notes", notes); + Obj.set("location", location); + Obj.set("owner", owner); + Obj.set("suspended", suspended); + Obj.set("blackListed", blackListed); + Obj.set("userRole", userRole); + Obj.set("userTypeProprietaryInfo", userTypeProprietaryInfo); + Obj.set("securityPolicy", securityPolicy); + Obj.set("securityPolicyChange", securityPolicyChange); + }; + + bool UserInfo::from_json(Poco::JSON::Object::Ptr Obj) { + try { + if(Obj->has("Id")) + Id = Obj->get("Id"); + if(Obj->has("name")) + name = Obj->get("name").toString(); + if(Obj->has("description")) + description = Obj->get("description").toString(); + if(Obj->has("avatar")) + avatar = Obj->get("avatar").toString(); + if(Obj->has("email")) + email = Obj->get("email").toString(); + if(Obj->has("validationEmail")) + validationEmail = Obj->get("validationEmail").toString(); + if(Obj->has("validationURI")) + validationURI = Obj->get("validationURI").toString(); + if(Obj->has("currentLoginURI")) + currentLoginURI = Obj->get("currentLoginURI").toString(); + if(Obj->has("locale")) + locale = Obj->get("locale").toString(); + if(Obj->has("notes")) + notes = Obj->get("notes").toString(); + if(Obj->has("userRole")) + userRole = Obj->get("userRole").toString(); + if(Obj->has("securityPolicy")) + securityPolicy = Obj->get("securityPolicy").toString(); + if(Obj->has("userTypeProprietaryInfo")) + description = Obj->get("userTypeProprietaryInfo").toString(); + if(Obj->has("description")) + description = Obj->get("description").toString(); + if(Obj->has("validationDate")) + validationDate = Obj->get("validationDate"); + if(Obj->has("creationDate")) + creationDate = Obj->get("creationDate"); + if(Obj->has("lastLogin")) + lastLogin = Obj->get("lastLogin"); + if(Obj->has("lastPasswordChange")) + lastPasswordChange = Obj->get("lastPasswordChange"); + if(Obj->has("lastEmailCheck")) + lastEmailCheck = Obj->get("lastEmailCheck"); + if(Obj->has("securityPolicyChange")) + securityPolicyChange = Obj->get("securityPolicyChange"); + if(Obj->has("validated")) + validated = (Obj->get("validated").toString() == "true"); + if(Obj->has("changePassword")) + changePassword = (Obj->get("changePassword").toString() == "true"); + if(Obj->has("waitingForEmailCheck")) + waitingForEmailCheck = (Obj->get("waitingForEmailCheck").toString() == "true"); + if(Obj->has("suspended")) + suspended = (Obj->get("suspended").toString() == "true"); + if(Obj->has("blackListed")) + blackListed = (Obj->get("blackListed").toString() == "true"); + return true; + } catch (const Poco::Exception &E) { + + } + return false; + } + } diff --git a/src/RESTAPI_objects.h b/src/RESTAPI_objects.h index 60c5492..e400c03 100644 --- a/src/RESTAPI_objects.h +++ b/src/RESTAPI_objects.h @@ -13,27 +13,61 @@ namespace uCentral::Objects { - struct AclTemplate { - bool Read_ = true ; - bool ReadWrite_ = true ; - bool ReadWriteCreate_ = true ; - bool Delete_ = true ; - bool PortalLogin_ = true ; - void to_json(Poco::JSON::Object &Obj) const ; - }; + struct AclTemplate { + bool Read_ = true; + bool ReadWrite_ = true; + bool ReadWriteCreate_ = true; + bool Delete_ = true; + bool PortalLogin_ = true; - struct WebToken { - std::string access_token_; - std::string refresh_token_; - std::string id_token_; - std::string token_type_; - std::string username_; - unsigned int expires_in_; - unsigned int idle_timeout_; - AclTemplate acl_template_; - uint64_t created_; - void to_json(Poco::JSON::Object &Obj) const ; - }; + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct WebToken { + std::string access_token_; + std::string refresh_token_; + std::string id_token_; + std::string token_type_; + std::string username_; + unsigned int expires_in_; + unsigned int idle_timeout_; + AclTemplate acl_template_; + uint64_t created_; + + void to_json(Poco::JSON::Object &Obj) const; + }; + + struct UserInfo { + uint64_t Id = 0; + std::string name; + std::string description; + std::string avatar; + std::string email; + bool validated = false; + std::string validationEmail; + uint64_t validationDate = 0; + uint64_t creationDate = 0; + std::string validationURI; + bool changePassword = true; + uint64_t lastLogin = 0; + std::string currentLoginURI; + uint64_t lastPasswordChange = 0; + uint64_t lastEmailCheck = 0; + bool waitingForEmailCheck = false; + std::string locale; + std::string notes; + std::string location; + std::string owner; + bool suspended = false; + bool blackListed = false; + std::string userRole; + std::string userTypeProprietaryInfo; + std::string securityPolicy; + uint64_t securityPolicyChange; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(Poco::JSON::Object::Ptr Obj); + }; } #endif //UCENTRAL_RESTAPI_OBJECTS_H diff --git a/src/RESTAPI_server.cpp b/src/RESTAPI_server.cpp index d620f91..b763140 100644 --- a/src/RESTAPI_server.cpp +++ b/src/RESTAPI_server.cpp @@ -10,7 +10,6 @@ #include "RESTAPI_server.h" #include "RESTAPI_oauth2Handler.h" -#include "RESTAPI_unknownRequestHandler.h" #include "RESTAPI_system_command.h" #include "RESTAPI_user_handler.h" #include "RESTAPI_users_handler.h" @@ -58,24 +57,15 @@ namespace uCentral { Poco::URI uri(Request.getURI()); const auto & Path = uri.getPath(); - RESTAPIHandler::BindingMap bindings; + RESTAPIHandler::BindingMap Bindings; - if (RESTAPIHandler::ParseBindings(Path, "/api/v1/oauth2/{token}", bindings)) { - return new RESTAPI_oauth2Handler(bindings, Logger_); - } else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/oauth2", bindings)) { - return new RESTAPI_oauth2Handler(bindings, Logger_); - } else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/users", bindings)) { - return new RESTAPI_users_handler(bindings, Logger_); - } else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/user", bindings)) { - return new RESTAPI_user_handler(bindings, Logger_); - } else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/system", bindings)) { - return new RESTAPI_system_command(bindings, Logger_); - } else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/actions", bindings)) { - return new RESTAPI_action_links(bindings, Logger_); - } - - Logger_.error(Poco::format("INVALID-API-ENDPOINT: %s",Path)); - return new RESTAPI_UnknownRequestHandler(bindings,Logger_); + return RESTAPI_Router< + RESTAPI_oauth2Handler, + RESTAPI_users_handler, + RESTAPI_user_handler, + RESTAPI_system_command, + RESTAPI_action_links + >(Path,Bindings,Logger_); } void RESTAPI_Server::Stop() { diff --git a/src/RESTAPI_systemServices_handler.cpp b/src/RESTAPI_systemServices_handler.cpp new file mode 100644 index 0000000..ffdf281 --- /dev/null +++ b/src/RESTAPI_systemServices_handler.cpp @@ -0,0 +1,11 @@ +// +// Created by stephane bourque on 2021-06-28. +// + +#include "RESTAPI_systemServices_handler.h" + +namespace uCentral { + void RESTAPI_systemServices_handler::handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { + + } +} \ No newline at end of file diff --git a/src/RESTAPI_systemServices_handler.h b/src/RESTAPI_systemServices_handler.h new file mode 100644 index 0000000..a387ec9 --- /dev/null +++ b/src/RESTAPI_systemServices_handler.h @@ -0,0 +1,27 @@ +// +// Created by stephane bourque on 2021-06-28. +// + +#ifndef UCENTRALSEC_RESTAPI_SYSTEMSERVICES_HANDLER_H +#define UCENTRALSEC_RESTAPI_SYSTEMSERVICES_HANDLER_H + +#include "RESTAPI_handler.h" + +namespace uCentral { + class RESTAPI_systemServices_handler : public RESTAPIHandler { + public: + RESTAPI_systemServices_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) + : RESTAPIHandler(bindings, L, + std::vector + {Poco::Net::HTTPRequest::HTTP_POST, + Poco::Net::HTTPRequest::HTTP_GET, + Poco::Net::HTTPRequest::HTTP_PUT, + Poco::Net::HTTPRequest::HTTP_DELETE, + Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} + void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override; + private: + + }; +} + +#endif //UCENTRALSEC_RESTAPI_SYSTEMSERVICES_HANDLER_H diff --git a/src/RESTAPI_system_command.h b/src/RESTAPI_system_command.h index b69107d..011b94e 100644 --- a/src/RESTAPI_system_command.h +++ b/src/RESTAPI_system_command.h @@ -20,6 +20,7 @@ class RESTAPI_system_command : public RESTAPIHandler { Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override; + static const std::list PathName() { return std::list{"/api/v1/system"}; }; }; } #endif // UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H diff --git a/src/RESTAPI_unknownRequestHandler.cpp b/src/RESTAPI_unknownRequestHandler.cpp deleted file mode 100644 index 09c0f42..0000000 --- a/src/RESTAPI_unknownRequestHandler.cpp +++ /dev/null @@ -1,16 +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_unknownRequestHandler.h" - -void RESTAPI_UnknownRequestHandler::handleRequest(Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) -{ - if(!IsAuthorized(Request,Response)) - return; - BadRequest(Request, Response); -} \ No newline at end of file diff --git a/src/RESTAPI_unknownRequestHandler.h b/src/RESTAPI_unknownRequestHandler.h deleted file mode 100644 index 23ef7e6..0000000 --- a/src/RESTAPI_unknownRequestHandler.h +++ /dev/null @@ -1,25 +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_UNKNOWNREQUESTHANDLER_H -#define UCENTRAL_RESTAPI_UNKNOWNREQUESTHANDLER_H - -#include "RESTAPI_handler.h" - -class RESTAPI_UnknownRequestHandler: public uCentral::RESTAPIHandler -{ -public: - RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap & bindings,Poco::Logger & L) - : RESTAPIHandler(bindings,L, - std::vector{}) {} - void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) override; -}; - - - -#endif //UCENTRAL_RESTAPI_UNKNOWNREQUESTHANDLER_H diff --git a/src/RESTAPI_user_handler.h b/src/RESTAPI_user_handler.h index 3f4f320..a774d07 100644 --- a/src/RESTAPI_user_handler.h +++ b/src/RESTAPI_user_handler.h @@ -19,6 +19,8 @@ namespace uCentral { Poco::Net::HTTPRequest::HTTP_DELETE, Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override; + static const std::list PathName() { return std::list{"/api/v1/user/{id}"}; }; + private: }; diff --git a/src/RESTAPI_users_handler.h b/src/RESTAPI_users_handler.h index a13683a..0028538 100644 --- a/src/RESTAPI_users_handler.h +++ b/src/RESTAPI_users_handler.h @@ -16,8 +16,7 @@ namespace uCentral { {Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override; - private: - + static const std::list PathName() { return std::list{"/api/v1/users"}; }; }; }; diff --git a/src/StorageService.h b/src/StorageService.h index da0dadd..777d325 100644 --- a/src/StorageService.h +++ b/src/StorageService.h @@ -45,6 +45,39 @@ namespace uCentral { PASSWORD_INVALID }; + enum USER_TYPE { + UNKNOWN, ROOT, ADMIN, SUBSCRIBER, CSR, SYSTEM, SPECIAL + }; + + static USER_TYPE to_userType(const std::string &U) { + if (U=="root") + return ROOT; + else if (U=="admin") + return ADMIN; + else if (U=="subscriber") + return SUBSCRIBER; + else if (U=="csr") + return CSR; + else if (U=="system") + return SYSTEM; + else if (U=="SPECIAL") + return SPECIAL; + return UNKNOWN; + } + + static const std::string from_userType(USER_TYPE U) { + switch(U) { + case ROOT: return "root"; + case ADMIN: return "admin"; + case SUBSCRIBER: return "subscriber"; + case CSR: return "csr"; + case SYSTEM: return "system"; + case SPECIAL: return "special"; + case UNKNOWN: + default: return "unknown"; + } + } + static Storage *instance() { if (instance_ == nullptr) { instance_ = new Storage; @@ -55,9 +88,25 @@ namespace uCentral { int Start() override; void Stop() override; - int CreateUser(const std::string & Admin, const std::string &UserName, const std::string &Password); - bool DeleteUser(const std::string & Admin, const std::string &UserName); - bool ChangePassword(const std::string & Admin, const std::string &UserName, const std::string &OldPassword, const std::string &NewPassword); + // all passwords passed here are all plaintext + bool CreateUser(const std::string & Admin, uCentral::Objects::UserInfo & NewUser); + bool DeleteUser(const std::string & Admin, uint64_t Id); + bool SetOwner(const std::string & Admin, uint64_t Id, const std::string &Owner); + bool SetLocation(const std::string & Admin, uint64_t Id, const std::string &Location); + AUTH_ERROR ChangePassword(const std::string & Admin, uint64_t Id, const std::string &OldPassword, const std::string &NewPassword); + bool AddNotes(const std::string & Admin, uint64_t Id, const std::string &Notes); + bool SetPolicyChange(const std::string & Admin, const std::string &NewPolicy); + + + + + + + + + + + bool IdentityExists(std::string & Identity, AuthService::ACCESS_TYPE Type); bool AddIdentity(std::string & Identity, std::string & Password, AuthService::ACCESS_TYPE Type, uCentral::Objects::AclTemplate & ACL); diff --git a/src/Utils.cpp b/src/Utils.cpp index 34a92a9..3033c59 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -360,12 +360,13 @@ namespace uCentral::Utils { } } - uint64_t InitializeSystemId() { - uint64_t R = ~ std::rand(); - auto S = GetDefaultMacAsInt64() ^ R; - SaveSystemId(S); - return S; - } + uint64_t InitializeSystemId() { + std::srand(std::time(nullptr)); + auto S = GetDefaultMacAsInt64() ^ std::rand(); + SaveSystemId(S); + std::cout << "ID: " << S << std::endl; + return S; + } uint64_t GetSystemId() { uint64_t ID=0; diff --git a/src/Utils.h b/src/Utils.h index 4679b0c..edb2f26 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -14,6 +14,8 @@ #include "Poco/Net/NetworkInterface.h" +#define DBGLINE { std::cout << __FILE__ << ":" << __func__ << ":" << __LINE__ << std::endl; }; + namespace uCentral::Utils { [[nodiscard]] std::vector Split(const std::string &List, char Delimiter=','); diff --git a/src/storage_tables.cpp b/src/storage_tables.cpp index 915530c..82a2bbc 100644 --- a/src/storage_tables.cpp +++ b/src/storage_tables.cpp @@ -41,7 +41,9 @@ namespace uCentral { "owner varchar, " "suspended int, " "blackListed int, " - "userType varchar, " + "userRole varchar, " + "securityPolicy text, " + "securityPolicyChange bigint, " "userTypeProprietaryInfo text" " ,INDEX emailindex (email ASC)" " ,INDEX nameindex (name ASC))", @@ -72,7 +74,9 @@ namespace uCentral { "owner varchar, " "suspended int, " "blackListed int, " - "userType varchar, " + "userRole varchar, " + "securityPolicy text, " + "securityPolicyChange bigint, " "userTypeProprietaryInfo text" ")", Poco::Data::Keywords::now; diff --git a/src/storage_users.cpp b/src/storage_users.cpp new file mode 100644 index 0000000..5b8ce7a --- /dev/null +++ b/src/storage_users.cpp @@ -0,0 +1,38 @@ +// +// Created by stephane bourque on 2021-06-25. +// + +#include "StorageService.h" + +namespace uCentral { + + bool Storage::CreateUser(const std::string & Admin, uCentral::Objects::UserInfo & NewUser) { + return true; + } + + bool Storage::DeleteUser(const std::string & Admin, uint64_t Id) { + return true; + } + + bool Storage::SetOwner(const std::string & Admin, uint64_t Id, const std::string &Owner) { + return true; + } + + bool Storage::SetLocation(const std::string & Admin, uint64_t Id, const std::string &Location) { + return true; + } + + Storage::AUTH_ERROR Storage::ChangePassword(const std::string & Admin, uint64_t Id, const std::string &OldPassword, const std::string &NewPassword) { + return SUCCESS; + } + + bool Storage::AddNotes(const std::string & Admin, uint64_t Id, const std::string &Notes) { + return true; + } + + bool Storage::SetPolicyChange(const std::string & Admin, const std::string &NewPolicy) { + return true; + } + +} + diff --git a/src/uCentralTypes.h b/src/uCentralTypes.h index 0d179a8..6aa69ef 100644 --- a/src/uCentralTypes.h +++ b/src/uCentralTypes.h @@ -19,7 +19,7 @@ namespace uCentral::Types { typedef std::vector StringVec; typedef std::vector SubSystemVec; typedef std::map> StringMapStringSet; - typedef std::function TopicNotifyFunction; + typedef std::function TopicNotifyFunction; typedef std::list> TopicNotifyFunctionList; typedef std::map NotifyTable; }; diff --git a/ucentralsec.properties b/ucentralsec.properties index d354245..2d7e7ad 100644 --- a/ucentralsec.properties +++ b/ucentralsec.properties @@ -11,7 +11,7 @@ ucentral.restapi.host.0.backlog = 100 ucentral.restapi.host.0.security = relaxed ucentral.restapi.host.0.rootca = $UCENTRALSEC_ROOT/certs/restapi-ca.pem ucentral.restapi.host.0.address = * -ucentral.restapi.host.0.port = 16001 +ucentral.restapi.host.0.port = 16002 ucentral.restapi.host.0.cert = $UCENTRALSEC_ROOT/certs/restapi-cert.pem ucentral.restapi.host.0.key = $UCENTRALSEC_ROOT/certs/restapi-key.pem ucentral.restapi.host.0.key.password = mypassword @@ -27,14 +27,12 @@ authentication.default.username = tip@ucentral.com authentication.default.password = openwifi authentication.default.access = master authentication.service.type = internal -firmware.autoupdate.policy.default = auto system.directory.data = $UCENTRALSEC_ROOT/data ucentral.service.key = $UCENTRALSEC_ROOT/certs/restapi-key.pem ucentral.system.debug = true -ucentral.system.uri = https://localhost:16001 -ucentral.system.id = 1 -ucentral.system.commandchannel = /tmp/app.ucentralgw +ucentral.system.uri = https://localhost:16002 +ucentral.system.commandchannel = /tmp/app.ucentralsec mailer.hostname = smtp.gmail.com mailer.username = no-reply@arilia.com @@ -45,8 +43,9 @@ mailer.port = 587 # # Kafka # -ucentral.kafka.enable = false -ucentral.kafka.brokerlist = 127.0.0.1:9092 +ucentral.kafka.group.id = 3 +ucentral.kafka.enable = true +ucentral.kafka.brokerlist = a1.arilia.com:9092 ucentral.kafka.auto.commit = false ucentral.kafka.queue.buffering.max.ms = 50 @@ -60,7 +59,7 @@ storage.type = sqlite #storage.type = mysql #storage.type = odbc -storage.type.sqlite.db = $UCENTRALSEC_ROOT/security.db +storage.type.sqlite.db = security.db storage.type.sqlite.idletime = 120 storage.type.sqlite.maxsessions = 128 @@ -91,11 +90,11 @@ authentication.default.password = 13268b7daa751240369d125e79c873bd8dd3bef7981bdf authentication.default.access = master authentication.service.type = internal -system.directory.data = $UCENTRALSEC_ROOT/data - +ucentral.system.data = $UCENTRALSEC_ROOT/data ucentral.system.debug = true -ucentral.system.uri = https://localhost:16001 -ucentral.system.commandchannel = /tmp/app.ucentralgw +ucentral.system.uri.private = https://localhost:16002 +ucentral.system.uri.public = https://local.dpaas.arilia.com:16002 +ucentral.system.commandchannel = /tmp/app.ucentralsec # email.includeonly = mydomain.com # email.exclude = gmail.com