New router and simplifiedrest handler

This commit is contained in:
stephb9959
2021-06-28 23:45:25 -07:00
parent 89f423b605
commit d15a1a3cc4
32 changed files with 772 additions and 273 deletions

View File

@@ -54,7 +54,6 @@ add_executable( ucentralsec
src/Daemon.h src/Daemon.cpp src/Daemon.h src/Daemon.cpp
src/MicroService.cpp src/MicroService.h src/MicroService.cpp src/MicroService.h
src/SubSystemServer.cpp src/SubSystemServer.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_oauth2Handler.h src/RESTAPI_oauth2Handler.cpp
src/RESTAPI_handler.h src/RESTAPI_handler.cpp src/RESTAPI_handler.h src/RESTAPI_handler.cpp
src/RESTAPI_server.cpp src/RESTAPI_server.h 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/storage_tables.cpp src/SMTPMailerService.cpp src/SMTPMailerService.h
src/RESTAPI_users_handler.cpp src/RESTAPI_users_handler.h src/RESTAPI_users_handler.cpp src/RESTAPI_users_handler.h
src/RESTAPI_user_handler.cpp src/RESTAPI_user_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 target_link_libraries(ucentralsec PUBLIC
${Poco_LIBRARIES} ${Boost_LIBRARIES} ${ZLIB_LIBRARIES} ${AWSSDK_LINK_LIBRARIES} CppKafka::cppkafka ) ${Poco_LIBRARIES} ${Boost_LIBRARIES} ${ZLIB_LIBRARIES} ${AWSSDK_LINK_LIBRARIES} CppKafka::cppkafka )

View File

@@ -308,6 +308,11 @@ components:
- instagram - instagram
oauthUserInfo: oauthUserInfo:
type: string type: string
securityPolicy:
type: string
securityPolicyChange:
type: integer
format: int64
UserList: UserList:
type: object type: object
@@ -417,6 +422,29 @@ components:
items: items:
$ref: '#/components/schemas/SecurityProfile' $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 ## End of uCentral system wide values
@@ -741,6 +769,24 @@ paths:
404: 404:
$ref: '#/components/responses/NotFound' $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 ## These are endpoints that all services in the uCentral stack must provide

4
set_env.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
export UCENTRALSEC_CONFIG=`pwd`
export UCENTRALSEC_ROOT=`pwd`

View File

@@ -38,9 +38,7 @@ namespace uCentral {
Types::SubSystemVec{ Types::SubSystemVec{
Storage(), Storage(),
RESTAPI_Server(), RESTAPI_Server(),
KafkaManager(), SMTPMailerService()
SMTPMailerService(),
ALBHealthCheckServer()
}); });
} }
return instance_; return instance_;
@@ -49,7 +47,6 @@ namespace uCentral {
void Daemon::initialize(Poco::Util::Application &self) { void Daemon::initialize(Poco::Util::Application &self) {
MicroService::initialize(*this); MicroService::initialize(*this);
} }
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {

View File

@@ -39,14 +39,10 @@ namespace uCentral {
Types::SubSystemVec SubSystems) : Types::SubSystemVec SubSystems) :
MicroService( PropFile, RootEnv, ConfigEnv, AppName, 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); void initialize(Poco::Util::Application &self);
static Daemon *instance(); static Daemon *instance();
private: private:
static Daemon *instance_; static Daemon *instance_;
bool AutoProvisioning_ = false;
Types::StringMapStringSet DeviceTypeIdentifications_;
}; };
inline Daemon * Daemon() { return Daemon::instance(); } inline Daemon * Daemon() { return Daemon::instance(); }

View File

@@ -38,93 +38,104 @@ namespace uCentral {
int KafkaManager::Start() { int KafkaManager::Start() {
if(!KafkaEnabled_) if(!KafkaEnabled_)
return 0; return 0;
Running_ = true; ProducerThr_ = std::make_unique<std::thread>([this]() { this->Producer(); });
ProducerThr_ = std::make_unique<std::thread>(Producer,this); ConsumerThr_ = std::make_unique<std::thread>([this]() { this->Consumer(); });
ProducerThr_->detach();
ConsumerThr_ = std::make_unique<std::thread>(Consumer,this);
ConsumerThr_->detach();
return 0; return 0;
} }
void KafkaManager::Stop() { void KafkaManager::Stop() {
if(KafkaEnabled_) { if(KafkaEnabled_) {
Running_ = false; ProducerRunning_ = ConsumerRunning_ = false;
ConsumerThr_->join();
ProducerThr_->join(); ProducerThr_->join();
ConsumerThr_->join();
return; return;
} }
} }
void KafkaManager::Producer(KafkaManager *Mgr) { void KafkaManager::Producer() {
cppkafka::Configuration Config({ cppkafka::Configuration Config({
{ "metadata.broker.list", Daemon()->ConfigGetString("ucentral.kafka.brokerlist") } , { "metadata.broker.list", Daemon()->ConfigGetString("ucentral.kafka.brokerlist") }
{ "enable.auto.commit", Daemon()->ConfigGetBool("ucentral.kafka.auto.commit", false)}
}); });
Mgr->SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" + SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" +
std::to_string(Daemon()->ConfigGetInt("ucentral.system.id")) + std::to_string(Daemon()->ID()) +
R"lit( , "host" : ")lit" + Daemon()->ConfigGetString("ucentral.system.uri") + R"lit( , "host" : ")lit" + Daemon()->PrivateEndPoint() +
R"lit(" } , "payload" : ")lit" ; R"lit(" } , "payload" : ")lit" ;
cppkafka::Producer Producer(Config); cppkafka::Producer Producer(Config);
ProducerRunning_ = true;
while(Mgr->Running_) { while(ProducerRunning_) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000)); std::this_thread::sleep_for(std::chrono::milliseconds(200));
if(!Mgr->Running_) try
break;
{ {
SubMutexGuard G(Mgr->ProducerMutex_); SubMutexGuard G(ProducerMutex_);
while (!Mgr->Queue_.empty() && Mgr->Running_) { while (!Queue_.empty()) {
const auto M = Mgr->Queue_.front(); const auto M = Queue_.front();
// std::cout << "Producing Topic: " << M.Topic << " Key: " << M.Key <<std::endl;
Producer.produce( Producer.produce(
cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad)); cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad));
Mgr->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({ cppkafka::Configuration Config({
{ "group.id", 1 }, { "group.id", Daemon()->ConfigGetString("ucentral.kafka.group.id") },
{ "enable.auto.commit", Daemon()->ConfigGetBool("ucentral.kafka.auto.commit",false) }, { "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); cppkafka::Consumer Consumer(Config);
Consumer.set_assignment_callback([=](const cppkafka::TopicPartitionList& partitions) {
Consumer.set_assignment_callback([Mgr](const cppkafka::TopicPartitionList& partitions) { std::cout << "Partition assigned: " << partitions.front().get_partition() << std::endl;
Mgr->Logger_.information(Poco::format("Got assigned: %Lu...",(uint64_t )partitions.front().get_partition())); Logger_.information(Poco::format("Got assigned: %Lu...",(uint64_t )partitions.front().get_partition()));
}); });
Consumer.set_revocation_callback([Mgr](const cppkafka::TopicPartitionList& partitions) { Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) {
Mgr->Logger_.information(Poco::format("Got revoked: %Lu...",(uint64_t )partitions.front().get_partition())); 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<std::string> Topics; Types::StringVec Topics;
for(const auto &i:Mgr->Notifiers_) for(const auto &i:Notifiers_)
Topics.push_back(i.first); Topics.push_back(i.first);
Consumer.subscribe(Topics); Consumer.subscribe(Topics);
while(Mgr->Running_) {
cppkafka::Message Msg = Consumer.poll(std::chrono::milliseconds(2000)); ConsumerRunning_ = true;
if (Msg) { while(ConsumerRunning_) {
try {
cppkafka::Message Msg = Consumer.poll(std::chrono::milliseconds(200));
if (!Msg)
continue;;
if (Msg.get_error()) { if (Msg.get_error()) {
if (!Msg.is_eof()) { if (!Msg.is_eof()) {
Mgr->Logger_.error( Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string()));
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());
} }
Consumer.commit(Msg); 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 + "}"); return std::move( SystemInfoWrapper_ + PayLoad + "}");
} }
void KafkaManager::PostMessage(std::string topic, std::string key, std::string PayLoad) { void KafkaManager::PostMessage(std::string topic, std::string key, std::string PayLoad, bool WrapMessage ) {
if(KafkaEnabled_ && Running_) { if(KafkaEnabled_) {
SubMutexGuard G(Mutex_); SubMutexGuard G(Mutex_);
KMessage M{ KMessage M{
.Topic = std::move(topic), .Key = std::move(key), .PayLoad = std::move(WrapSystemId(PayLoad))}; .Topic = std::move(topic),
// std::cout << "Posting Topic: " << M.Topic << " Key: " << M.Key << " Payload: " << M.PayLoad << std::endl; .Key = std::move(key),
.PayLoad = std::move(WrapMessage ? WrapSystemId(PayLoad) : PayLoad )};
Queue_.push(std::move(M)); Queue_.push(std::move(M));
} }
} }
int KafkaManager::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { int KafkaManager::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) {
if(!Running_) { if(KafkaEnabled_) {
SubMutexGuard G(Mutex_); SubMutexGuard G(Mutex_);
auto It = Notifiers_.find(Topic); auto It = Notifiers_.find(Topic);
if(It == Notifiers_.end()) { if(It == Notifiers_.end()) {
@@ -162,7 +173,7 @@ namespace uCentral {
} }
void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, int Id) { void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, int Id) {
if(!Running_) { if(KafkaEnabled_) {
SubMutexGuard G(Mutex_); SubMutexGuard G(Mutex_);
auto It = Notifiers_.find(Topic); auto It = Notifiers_.find(Topic);
if(It != Notifiers_.end()) { if(It != Notifiers_.end()) {

View File

@@ -35,24 +35,26 @@ namespace uCentral {
return instance_; return instance_;
} }
static void Producer(KafkaManager *); void Producer();
static void Consumer(KafkaManager *); void Consumer();
int Start() override; int Start() override;
void Stop() 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]] 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); int RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction & F);
void UnregisterTopicWatcher(const std::string &Topic, int FunctionId); void UnregisterTopicWatcher(const std::string &Topic, int FunctionId);
void WakeUp();
private: private:
static KafkaManager *instance_; static KafkaManager *instance_;
SubMutex ProducerMutex_; SubMutex ProducerMutex_;
SubMutex ConsumerMutex_; SubMutex ConsumerMutex_;
bool KafkaEnabled_ = false; bool KafkaEnabled_ = false;
std::atomic_bool Running_ = false; std::atomic_bool ProducerRunning_ = false;
std::atomic_bool ConsumerRunning_ = false;
std::queue<KMessage> Queue_; std::queue<KMessage> Queue_;
std::string SystemInfoWrapper_; std::string SystemInfoWrapper_;
std::unique_ptr<std::thread> ConsumerThr_; std::unique_ptr<std::thread> ConsumerThr_;

17
src/Kafka_topics.h Normal file
View File

@@ -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

View File

@@ -17,10 +17,22 @@
#include "Poco/Path.h" #include "Poco/Path.h"
#include "Poco/File.h" #include "Poco/File.h"
#include "Poco/String.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 "MicroService.h"
#include "Utils.h" #include "Utils.h"
#undef DBGLINE
#define DBGLINE
namespace uCentral { namespace uCentral {
void MyErrorHandler::exception(const Poco::Exception & E) { void MyErrorHandler::exception(const Poco::Exception & E) {
@@ -43,7 +55,84 @@ namespace uCentral {
std::exit(Reason); 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<Poco::JSON::Object::Ptr>();
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) { 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::initializeSSL();
Poco::Net::HTTPStreamFactory::registerFactory(); Poco::Net::HTTPStreamFactory::registerFactory();
Poco::Net::HTTPSStreamFactory::registerFactory(); Poco::Net::HTTPSStreamFactory::registerFactory();
@@ -87,9 +176,14 @@ namespace uCentral {
ID_ = Utils::GetSystemId(); ID_ = Utils::GetSystemId();
if(!DebugMode_) if(!DebugMode_)
DebugMode_ = ConfigGetBool("ucentral.system.debug",false); DebugMode_ = ConfigGetBool("ucentral.system.debug",false);
MyPrivateEndPoint_ = ConfigGetString("ucentral.system.uri.private");
MyPublicEndPoint_ = ConfigGetString("ucentral.system.uri.public");
MyHash_ = CreateHash(MyPrivateEndPoint_);
InitializeSubSystemServers(); InitializeSubSystemServers();
ServerApplication::initialize(self); 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() { 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) { void MicroService::handleHelp(const std::string &name, const std::string &value) {
HelpRequested_ = true; HelpRequested_ = true;
displayHelp(); displayHelp();
@@ -186,9 +274,11 @@ namespace uCentral {
void MicroService::StartSubSystemServers() { void MicroService::StartSubSystemServers() {
for(auto i:SubSystems_) for(auto i:SubSystems_)
i->Start(); i->Start();
BusEventManager_.Start();
} }
void MicroService::StopSubSystemServers() { void MicroService::StopSubSystemServers() {
BusEventManager_.Stop();
for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i)
(*i)->Stop(); (*i)->Stop();
} }
@@ -286,6 +376,58 @@ namespace uCentral {
return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; 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) { int MicroService::main(const ArgVec &args) {
MyErrorHandler ErrorHandler(*this); MyErrorHandler ErrorHandler(*this);
@@ -313,7 +455,4 @@ namespace uCentral {
return Application::EXIT_OK; return Application::EXIT_OK;
} }
} }

View File

@@ -20,6 +20,7 @@
#include "Poco/Crypto/RSAKey.h" #include "Poco/Crypto/RSAKey.h"
#include "Poco/Crypto/CipherFactory.h" #include "Poco/Crypto/CipherFactory.h"
#include "Poco/Crypto/Cipher.h" #include "Poco/Crypto/Cipher.h"
#include "Poco/SHA2Engine.h"
#include "uCentralTypes.h" #include "uCentralTypes.h"
#include "SubSystemServer.h" #include "SubSystemServer.h"
@@ -36,6 +37,29 @@ namespace uCentral {
Poco::Util::Application &App_; 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<uint64_t, MicroServiceMeta> MicroServiceMetaMap;
typedef std::vector<MicroServiceMeta> MicroServiceMetaVec;
class MicroService : public Poco::Util::ServerApplication { class MicroService : public Poco::Util::ServerApplication {
public: public:
explicit MicroService( std::string PropFile, explicit MicroService( std::string PropFile,
@@ -66,7 +90,7 @@ namespace uCentral {
void StopSubSystemServers(); void StopSubSystemServers();
void Exit(int Reason); void Exit(int Reason);
bool SetSubsystemLogLevel(const std::string & SubSystem, const std::string & Level); 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<Poco::Crypto::RSAKey> & Key() { return AppKey_; } [[nodiscard]] const Poco::SharedPtr<Poco::Crypto::RSAKey> & Key() { return AppKey_; }
[[nodiscard]] inline const std::string & DataDir() { return DataDir_; } [[nodiscard]] inline const std::string & DataDir() { return DataDir_; }
[[nodiscard]] std::string CreateUUID(); [[nodiscard]] std::string CreateUUID();
@@ -85,6 +109,15 @@ namespace uCentral {
[[nodiscard]] uint64_t ConfigGetBool(const std::string &Key); [[nodiscard]] uint64_t ConfigGetBool(const std::string &Key);
[[nodiscard]] std::string Encrypt(const std::string &S); [[nodiscard]] std::string Encrypt(const std::string &S);
[[nodiscard]] std::string Decrypt(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: private:
bool HelpRequested_ = false; bool HelpRequested_ = false;
@@ -98,6 +131,14 @@ namespace uCentral {
Types::SubSystemVec SubSystems_; Types::SubSystemVec SubSystems_;
Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory(); Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory();
Poco::Crypto::Cipher * Cipher_ = nullptr; 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_PROPERTIES_FILENAME;
std::string DAEMON_ROOT_ENV_VAR; std::string DAEMON_ROOT_ENV_VAR;

View File

@@ -19,6 +19,7 @@ namespace uCentral {
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
void handleRequest(Poco::Net::HTTPServerRequest &Request, void handleRequest(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response) override; Poco::Net::HTTPServerResponse &Response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/actions"}; };
}; };
} }

View File

@@ -11,40 +11,46 @@
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <future> #include <future>
#include <numeric>
#include <chrono> #include <chrono>
#include "Poco/URI.h" #include "Poco/URI.h"
#include "Poco/DateTimeParser.h"
#include "RESTAPI_handler.h"
#include "AuthService.h" #include "AuthService.h"
#include "RESTAPI_handler.h"
#include "RESTAPI_protocol.h" #include "RESTAPI_protocol.h"
#include "Utils.h" #include "Utils.h"
#define DBG std::cout << __LINE__ << " " __FILE__ << std::endl; #define DBG std::cout << __LINE__ << " " __FILE__ << std::endl;
namespace uCentral { 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<const char *> & EndPoints, BindingMap &bindings) {
std::string Param, Value; std::string Param, Value;
bindings.clear(); bindings.clear();
std::vector<std::string> PathItems = uCentral::Utils::Split(Path,'/'); std::vector<std::string> PathItems = uCentral::Utils::Split(Request, '/');
std::vector<std::string> ParamItems = uCentral::Utils::Split(Request,'/');
if(PathItems.size()!=ParamItems.size()) for(const auto &EndPoint:EndPoints) {
return false; std::vector<std::string> ParamItems = uCentral::Utils::Split(EndPoint, '/');
if (PathItems.size() != ParamItems.size())
continue;
for(auto i=0;i!=PathItems.size();i++) { bool Matched = true;
if (PathItems[i] != ParamItems[i]) { for (auto i = 0; i != PathItems.size() && Matched; i++) {
if (PathItems[i][0] == '{') { // std::cout << "PATH:" << PathItems[i] << " ENDPOINT:" << ParamItems[i] << std::endl;
auto ParamName = PathItems[i].substr(1, PathItems[i].size() - 2); if (PathItems[i] != ParamItems[i]) {
bindings[ParamName] = ParamItems[i]; if (ParamItems[i][0] == '{') {
} else auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2);
return false; bindings[ParamName] = PathItems[i];
} else {
Matched = false;
}
}
} }
if(Matched)
return true;
} }
return true; return false;
} }
void RESTAPIHandler::PrintBindings() { void RESTAPIHandler::PrintBindings() {
@@ -242,7 +248,7 @@ namespace uCentral {
bool RESTAPIHandler::IsAuthorized(Poco::Net::HTTPServerRequest &Request, bool RESTAPIHandler::IsAuthorized(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response) { Poco::Net::HTTPServerResponse &Response) {
if (AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) { if (uCentral::AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) {
return true; return true;
} else { } else {
UnAuthorized(Request, Response); UnAuthorized(Request, Response);
@@ -253,7 +259,7 @@ namespace uCentral {
bool RESTAPIHandler::IsAuthorized(Poco::Net::HTTPServerRequest &Request, bool RESTAPIHandler::IsAuthorized(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response, std::string &UserName) { Poco::Net::HTTPServerResponse &Response, std::string &UserName) {
if (AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) { if (uCentral::AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) {
UserName = UserInfo_.username_; UserName = UserInfo_.username_;
return true; return true;
} else { } else {

View File

@@ -19,25 +19,24 @@
#include "Poco/File.h" #include "Poco/File.h"
#include "Poco/JSON/Object.h" #include "Poco/JSON/Object.h"
#include "RESTAPI_objects.h"
#include "AuthService.h" #include "AuthService.h"
#include "RESTAPI_objects.h"
namespace uCentral { 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 { class RESTAPIHandler : public Poco::Net::HTTPRequestHandler {
public: 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<std::string, std::string> BindingMap; typedef std::map<std::string, std::string> BindingMap;
RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector<std::string> Methods) RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector<std::string> Methods)
: Bindings_(std::move(map)), Logger_(l), Methods_(std::move(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<const char *> & EndPoints, BindingMap &Keys);
void PrintBindings(); void PrintBindings();
void ParseParameters(Poco::Net::HTTPServerRequest &request); void ParseParameters(Poco::Net::HTTPServerRequest &request);
@@ -94,14 +93,52 @@ namespace uCentral {
[[nodiscard]] static uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj); [[nodiscard]] static uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj);
protected: protected:
BindingMap Bindings_; BindingMap Bindings_;
Poco::URI::QueryParameters Parameters_; Poco::URI::QueryParameters Parameters_;
Poco::Logger &Logger_; Poco::Logger &Logger_;
std::string SessionToken_; std::string SessionToken_;
struct uCentral::Objects::WebToken UserInfo_; struct uCentral::Objects::WebToken UserInfo_;
std::vector<std::string> Methods_; std::vector<std::string> Methods_;
QueryBlock QB_; QueryBlock QB_;
}; };
class RESTAPI_UnknownRequestHandler : public RESTAPIHandler {
public:
RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
: RESTAPIHandler(bindings, L, std::vector<std::string>{}) {}
void handleRequest(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response) override {
if (!IsAuthorized(Request, Response))
return;
BadRequest(Request, Response);
}
};
template<class T>
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<typename T, typename... Args>
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<Args...>(RequestedPath, Bindings, Logger);
}
}
} }
#endif //UCENTRAL_RESTAPI_HANDLER_H #endif //UCENTRAL_RESTAPI_HANDLER_H

View File

@@ -8,55 +8,57 @@
#include "Poco/JSON/Parser.h" #include "Poco/JSON/Parser.h"
#include "AuthService.h"
#include "RESTAPI_oauth2Handler.h" #include "RESTAPI_oauth2Handler.h"
#include "RESTAPI_protocol.h" #include "RESTAPI_protocol.h"
#include "AuthService.h"
namespace uCentral { namespace uCentral {
void RESTAPI_oauth2Handler::handleRequest(Poco::Net::HTTPServerRequest &Request, void RESTAPI_oauth2Handler::handleRequest(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response) { Poco::Net::HTTPServerResponse &Response) {
if (!ContinueProcessing(Request, Response))
return;
try { if (!ContinueProcessing(Request, Response))
if (Request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_POST) { return;
// Extract the info for login... try {
Poco::JSON::Parser parser; if (Request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_POST) {
Poco::JSON::Object::Ptr Obj = parser.parse(Request.stream()).extract<Poco::JSON::Object::Ptr>();
auto userId = GetS(uCentral::RESTAPI::Protocol::USERID, Obj); // Extract the info for login...
auto password = GetS(uCentral::RESTAPI::Protocol::PASSWORD, Obj); Poco::JSON::Parser parser;
Poco::JSON::Object::Ptr Obj =
parser.parse(Request.stream()).extract<Poco::JSON::Object::Ptr>();
Poco::toLowerInPlace(userId); auto userId = GetS(uCentral::RESTAPI::Protocol::USERID, Obj);
uCentral::Objects::WebToken Token; auto password = GetS(uCentral::RESTAPI::Protocol::PASSWORD, Obj);
if (AuthService()->Authorize(userId, password, Token)) { Poco::toLowerInPlace(userId);
Poco::JSON::Object ReturnObj; uCentral::Objects::WebToken Token;
Token.to_json(ReturnObj);
ReturnObject(Request, ReturnObj, Response); if (AuthService()->Authorize(userId, password, Token)) {
} else { Poco::JSON::Object ReturnObj;
UnAuthorized(Request, Response); Token.to_json(ReturnObj);
} ReturnObject(Request, ReturnObj, Response);
} else if (Request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_DELETE) { } else {
if (!IsAuthorized(Request, Response)) { UnAuthorized(Request, Response);
return; }
} } else if (Request.getMethod() == Poco::Net::HTTPServerRequest::HTTP_DELETE) {
auto Token = GetBinding(uCentral::RESTAPI::Protocol::TOKEN, "..."); if (!IsAuthorized(Request, Response)) {
if (Token == SessionToken_) { return;
AuthService()->Logout(Token); }
ReturnStatus(Request, Response, Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true); auto Token = GetBinding(uCentral::RESTAPI::Protocol::TOKEN, "...");
} else { if (Token == SessionToken_) {
NotFound(Request, Response); AuthService()->Logout(Token);
} ReturnStatus(Request, Response, Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true);
} else { } else {
BadRequest(Request, Response); NotFound(Request, Response);
} }
return; } else {
} BadRequest(Request, Response);
catch (const Poco::Exception &E) { }
Logger_.warning(Poco::format("%s: Failed with: %s", std::string(__func__), E.displayText())); return;
} } catch (const Poco::Exception &E) {
BadRequest(Request, Response); Logger_.warning(
} Poco::format("%s: Failed with: %s", std::string(__func__), E.displayText()));
}
BadRequest(Request, Response);
}
} }

View File

@@ -12,17 +12,16 @@
#include "RESTAPI_handler.h" #include "RESTAPI_handler.h"
namespace uCentral { namespace uCentral {
class RESTAPI_oauth2Handler : public uCentral::RESTAPIHandler { class RESTAPI_oauth2Handler : public RESTAPIHandler {
public: public:
RESTAPI_oauth2Handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L) RESTAPI_oauth2Handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
: RESTAPIHandler(bindings, L, : RESTAPIHandler(bindings, L,
std::vector<std::string> std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
{Poco::Net::HTTPRequest::HTTP_POST, Poco::Net::HTTPRequest::HTTP_DELETE,
Poco::Net::HTTPRequest::HTTP_DELETE, Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} void handleRequest(Poco::Net::HTTPServerRequest &request,
Poco::Net::HTTPServerResponse &response) override;
void handleRequest(Poco::Net::HTTPServerRequest &request, Poco::Net::HTTPServerResponse &response) override; static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/oauth2/{token}","/api/v1/oauth2"}; };
}; };
} }
#endif //UCENTRAL_RESTAPI_OAUTH2HANDLER_H #endif //UCENTRAL_RESTAPI_OAUTH2HANDLER_H

View File

@@ -8,11 +8,7 @@
#include "Poco/JSON/Parser.h" #include "Poco/JSON/Parser.h"
#include "Poco/JSON/Stringifier.h" #include "Poco/JSON/Stringifier.h"
#include "Daemon.h"
#include "RESTAPI_handler.h"
#include "RESTAPI_objects.h" #include "RESTAPI_objects.h"
#include "Utils.h"
namespace uCentral::Objects { namespace uCentral::Objects {
@@ -44,5 +40,94 @@ namespace uCentral::Objects {
Obj.set("username",username_); Obj.set("username",username_);
Obj.set("aclTemplate",AclTemplateObj); 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;
}
} }

View File

@@ -13,27 +13,61 @@
namespace uCentral::Objects { namespace uCentral::Objects {
struct AclTemplate { struct AclTemplate {
bool Read_ = true ; bool Read_ = true;
bool ReadWrite_ = true ; bool ReadWrite_ = true;
bool ReadWriteCreate_ = true ; bool ReadWriteCreate_ = true;
bool Delete_ = true ; bool Delete_ = true;
bool PortalLogin_ = true ; bool PortalLogin_ = true;
void to_json(Poco::JSON::Object &Obj) const ;
};
struct WebToken { void to_json(Poco::JSON::Object &Obj) const;
std::string access_token_; };
std::string refresh_token_;
std::string id_token_; struct WebToken {
std::string token_type_; std::string access_token_;
std::string username_; std::string refresh_token_;
unsigned int expires_in_; std::string id_token_;
unsigned int idle_timeout_; std::string token_type_;
AclTemplate acl_template_; std::string username_;
uint64_t created_; unsigned int expires_in_;
void to_json(Poco::JSON::Object &Obj) const ; 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 #endif //UCENTRAL_RESTAPI_OBJECTS_H

View File

@@ -10,7 +10,6 @@
#include "RESTAPI_server.h" #include "RESTAPI_server.h"
#include "RESTAPI_oauth2Handler.h" #include "RESTAPI_oauth2Handler.h"
#include "RESTAPI_unknownRequestHandler.h"
#include "RESTAPI_system_command.h" #include "RESTAPI_system_command.h"
#include "RESTAPI_user_handler.h" #include "RESTAPI_user_handler.h"
#include "RESTAPI_users_handler.h" #include "RESTAPI_users_handler.h"
@@ -58,24 +57,15 @@ namespace uCentral {
Poco::URI uri(Request.getURI()); Poco::URI uri(Request.getURI());
const auto & Path = uri.getPath(); const auto & Path = uri.getPath();
RESTAPIHandler::BindingMap bindings; RESTAPIHandler::BindingMap Bindings;
if (RESTAPIHandler::ParseBindings(Path, "/api/v1/oauth2/{token}", bindings)) { return RESTAPI_Router<
return new RESTAPI_oauth2Handler(bindings, Logger_); RESTAPI_oauth2Handler,
} else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/oauth2", bindings)) { RESTAPI_users_handler,
return new RESTAPI_oauth2Handler(bindings, Logger_); RESTAPI_user_handler,
} else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/users", bindings)) { RESTAPI_system_command,
return new RESTAPI_users_handler(bindings, Logger_); RESTAPI_action_links
} else if (RESTAPIHandler::ParseBindings(Path, "/api/v1/user", bindings)) { >(Path,Bindings,Logger_);
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_);
} }
void RESTAPI_Server::Stop() { void RESTAPI_Server::Stop() {

View File

@@ -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) {
}
}

View File

@@ -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<std::string>
{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

View File

@@ -20,6 +20,7 @@ class RESTAPI_system_command : public RESTAPIHandler {
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
void handleRequest(Poco::Net::HTTPServerRequest &request, void handleRequest(Poco::Net::HTTPServerRequest &request,
Poco::Net::HTTPServerResponse &response) override; Poco::Net::HTTPServerResponse &response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/system"}; };
}; };
} }
#endif // UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H #endif // UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H

View File

@@ -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);
}

View File

@@ -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<std::string>{}) {}
void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response) override;
};
#endif //UCENTRAL_RESTAPI_UNKNOWNREQUESTHANDLER_H

View File

@@ -19,6 +19,8 @@ namespace uCentral {
Poco::Net::HTTPRequest::HTTP_DELETE, Poco::Net::HTTPRequest::HTTP_DELETE,
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override; void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/user/{id}"}; };
private: private:
}; };

View File

@@ -16,8 +16,7 @@ namespace uCentral {
{Poco::Net::HTTPRequest::HTTP_GET, {Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {} Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override; void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override;
private: static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/users"}; };
}; };
}; };

View File

@@ -45,6 +45,39 @@ namespace uCentral {
PASSWORD_INVALID 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() { static Storage *instance() {
if (instance_ == nullptr) { if (instance_ == nullptr) {
instance_ = new Storage; instance_ = new Storage;
@@ -55,9 +88,25 @@ namespace uCentral {
int Start() override; int Start() override;
void Stop() override; void Stop() override;
int CreateUser(const std::string & Admin, const std::string &UserName, const std::string &Password); // all passwords passed here are all plaintext
bool DeleteUser(const std::string & Admin, const std::string &UserName); bool CreateUser(const std::string & Admin, uCentral::Objects::UserInfo & NewUser);
bool ChangePassword(const std::string & Admin, const std::string &UserName, const std::string &OldPassword, const std::string &NewPassword); 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 IdentityExists(std::string & Identity, AuthService::ACCESS_TYPE Type);
bool AddIdentity(std::string & Identity, std::string & Password, AuthService::ACCESS_TYPE Type, uCentral::Objects::AclTemplate & ACL); bool AddIdentity(std::string & Identity, std::string & Password, AuthService::ACCESS_TYPE Type, uCentral::Objects::AclTemplate & ACL);

View File

@@ -360,12 +360,13 @@ namespace uCentral::Utils {
} }
} }
uint64_t InitializeSystemId() { uint64_t InitializeSystemId() {
uint64_t R = ~ std::rand(); std::srand(std::time(nullptr));
auto S = GetDefaultMacAsInt64() ^ R; auto S = GetDefaultMacAsInt64() ^ std::rand();
SaveSystemId(S); SaveSystemId(S);
return S; std::cout << "ID: " << S << std::endl;
} return S;
}
uint64_t GetSystemId() { uint64_t GetSystemId() {
uint64_t ID=0; uint64_t ID=0;

View File

@@ -14,6 +14,8 @@
#include "Poco/Net/NetworkInterface.h" #include "Poco/Net/NetworkInterface.h"
#define DBGLINE { std::cout << __FILE__ << ":" << __func__ << ":" << __LINE__ << std::endl; };
namespace uCentral::Utils { namespace uCentral::Utils {
[[nodiscard]] std::vector<std::string> Split(const std::string &List, char Delimiter=','); [[nodiscard]] std::vector<std::string> Split(const std::string &List, char Delimiter=',');

View File

@@ -41,7 +41,9 @@ namespace uCentral {
"owner varchar, " "owner varchar, "
"suspended int, " "suspended int, "
"blackListed int, " "blackListed int, "
"userType varchar, " "userRole varchar, "
"securityPolicy text, "
"securityPolicyChange bigint, "
"userTypeProprietaryInfo text" "userTypeProprietaryInfo text"
" ,INDEX emailindex (email ASC)" " ,INDEX emailindex (email ASC)"
" ,INDEX nameindex (name ASC))", " ,INDEX nameindex (name ASC))",
@@ -72,7 +74,9 @@ namespace uCentral {
"owner varchar, " "owner varchar, "
"suspended int, " "suspended int, "
"blackListed int, " "blackListed int, "
"userType varchar, " "userRole varchar, "
"securityPolicy text, "
"securityPolicyChange bigint, "
"userTypeProprietaryInfo text" "userTypeProprietaryInfo text"
")", ")",
Poco::Data::Keywords::now; Poco::Data::Keywords::now;

38
src/storage_users.cpp Normal file
View File

@@ -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;
}
}

View File

@@ -19,7 +19,7 @@ namespace uCentral::Types {
typedef std::vector<std::string> StringVec; typedef std::vector<std::string> StringVec;
typedef std::vector<SubSystemServer*> SubSystemVec; typedef std::vector<SubSystemServer*> SubSystemVec;
typedef std::map<std::string,std::set<std::string>> StringMapStringSet; typedef std::map<std::string,std::set<std::string>> StringMapStringSet;
typedef std::function<void(std::string,std::string)> TopicNotifyFunction; typedef std::function<void(std::string, std::string)> TopicNotifyFunction;
typedef std::list<std::pair<TopicNotifyFunction,int>> TopicNotifyFunctionList; typedef std::list<std::pair<TopicNotifyFunction,int>> TopicNotifyFunctionList;
typedef std::map<std::string, TopicNotifyFunctionList> NotifyTable; typedef std::map<std::string, TopicNotifyFunctionList> NotifyTable;
}; };

View File

@@ -11,7 +11,7 @@ ucentral.restapi.host.0.backlog = 100
ucentral.restapi.host.0.security = relaxed ucentral.restapi.host.0.security = relaxed
ucentral.restapi.host.0.rootca = $UCENTRALSEC_ROOT/certs/restapi-ca.pem ucentral.restapi.host.0.rootca = $UCENTRALSEC_ROOT/certs/restapi-ca.pem
ucentral.restapi.host.0.address = * 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.cert = $UCENTRALSEC_ROOT/certs/restapi-cert.pem
ucentral.restapi.host.0.key = $UCENTRALSEC_ROOT/certs/restapi-key.pem ucentral.restapi.host.0.key = $UCENTRALSEC_ROOT/certs/restapi-key.pem
ucentral.restapi.host.0.key.password = mypassword ucentral.restapi.host.0.key.password = mypassword
@@ -27,14 +27,12 @@ authentication.default.username = tip@ucentral.com
authentication.default.password = openwifi authentication.default.password = openwifi
authentication.default.access = master authentication.default.access = master
authentication.service.type = internal authentication.service.type = internal
firmware.autoupdate.policy.default = auto
system.directory.data = $UCENTRALSEC_ROOT/data system.directory.data = $UCENTRALSEC_ROOT/data
ucentral.service.key = $UCENTRALSEC_ROOT/certs/restapi-key.pem ucentral.service.key = $UCENTRALSEC_ROOT/certs/restapi-key.pem
ucentral.system.debug = true ucentral.system.debug = true
ucentral.system.uri = https://localhost:16001 ucentral.system.uri = https://localhost:16002
ucentral.system.id = 1 ucentral.system.commandchannel = /tmp/app.ucentralsec
ucentral.system.commandchannel = /tmp/app.ucentralgw
mailer.hostname = smtp.gmail.com mailer.hostname = smtp.gmail.com
mailer.username = no-reply@arilia.com mailer.username = no-reply@arilia.com
@@ -45,8 +43,9 @@ mailer.port = 587
# #
# Kafka # Kafka
# #
ucentral.kafka.enable = false ucentral.kafka.group.id = 3
ucentral.kafka.brokerlist = 127.0.0.1:9092 ucentral.kafka.enable = true
ucentral.kafka.brokerlist = a1.arilia.com:9092
ucentral.kafka.auto.commit = false ucentral.kafka.auto.commit = false
ucentral.kafka.queue.buffering.max.ms = 50 ucentral.kafka.queue.buffering.max.ms = 50
@@ -60,7 +59,7 @@ storage.type = sqlite
#storage.type = mysql #storage.type = mysql
#storage.type = odbc #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.idletime = 120
storage.type.sqlite.maxsessions = 128 storage.type.sqlite.maxsessions = 128
@@ -91,11 +90,11 @@ authentication.default.password = 13268b7daa751240369d125e79c873bd8dd3bef7981bdf
authentication.default.access = master authentication.default.access = master
authentication.service.type = internal authentication.service.type = internal
system.directory.data = $UCENTRALSEC_ROOT/data ucentral.system.data = $UCENTRALSEC_ROOT/data
ucentral.system.debug = true ucentral.system.debug = true
ucentral.system.uri = https://localhost:16001 ucentral.system.uri.private = https://localhost:16002
ucentral.system.commandchannel = /tmp/app.ucentralgw ucentral.system.uri.public = https://local.dpaas.arilia.com:16002
ucentral.system.commandchannel = /tmp/app.ucentralsec
# email.includeonly = mydomain.com # email.includeonly = mydomain.com
# email.exclude = gmail.com # email.exclude = gmail.com