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/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 )

View File

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

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

View File

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

View File

@@ -38,93 +38,104 @@ namespace uCentral {
int KafkaManager::Start() {
if(!KafkaEnabled_)
return 0;
Running_ = true;
ProducerThr_ = std::make_unique<std::thread>(Producer,this);
ProducerThr_->detach();
ConsumerThr_ = std::make_unique<std::thread>(Consumer,this);
ConsumerThr_->detach();
ProducerThr_ = std::make_unique<std::thread>([this]() { this->Producer(); });
ConsumerThr_ = std::make_unique<std::thread>([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 <<std::endl;
SubMutexGuard G(ProducerMutex_);
while (!Queue_.empty()) {
const auto M = Queue_.front();
Producer.produce(
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({
{ "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<std::string> 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()) {

View File

@@ -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<KMessage> Queue_;
std::string SystemInfoWrapper_;
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/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<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) {
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;
}
}

View File

@@ -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<uint64_t, MicroServiceMeta> MicroServiceMetaMap;
typedef std::vector<MicroServiceMeta> 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<Poco::Crypto::RSAKey> & 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;

View File

@@ -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<const char *> PathName() { return std::list<const char *>{"/api/v1/actions"}; };
};
}

View File

@@ -11,40 +11,46 @@
#include <iostream>
#include <iterator>
#include <future>
#include <numeric>
#include <chrono>
#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<const char *> & EndPoints, BindingMap &bindings) {
std::string Param, Value;
bindings.clear();
std::vector<std::string> PathItems = uCentral::Utils::Split(Path,'/');
std::vector<std::string> ParamItems = uCentral::Utils::Split(Request,'/');
std::vector<std::string> PathItems = uCentral::Utils::Split(Request, '/');
if(PathItems.size()!=ParamItems.size())
return false;
for(const auto &EndPoint:EndPoints) {
std::vector<std::string> 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 {

View File

@@ -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<std::string, std::string> BindingMap;
RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector<std::string> 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 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<std::string> Methods_;
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

View File

@@ -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<Poco::JSON::Object::Ptr>();
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::JSON::Object::Ptr>();
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);
}
}

View File

@@ -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<std::string>
{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<std::string>{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<const char *> PathName() { return std::list<const char *>{"/api/v1/oauth2/{token}","/api/v1/oauth2"}; };
};
}
#endif //UCENTRAL_RESTAPI_OAUTH2HANDLER_H

View File

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

View File

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

View File

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

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}) {}
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/system"}; };
};
}
#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_OPTIONS}) {}
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:
};

View File

@@ -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<const char *> PathName() { return std::list<const char *>{"/api/v1/users"}; };
};
};

View File

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

View File

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

View File

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

View File

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

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<SubSystemServer*> SubSystemVec;
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::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.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