diff --git a/CMakeLists.txt b/CMakeLists.txt index 843a6f5..dd9da75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,7 +88,7 @@ add_executable(owprov src/storage_setup.cpp src/storage_configurations.cpp src/storage_configurations.h src/RESTAPI_configurations_handler.cpp src/RESTAPI_configurations_handler.h - src/RESTAPI_webSocketServer.h src/RESTAPI_webSocketServer.cpp src/RESTAPI_contact_list_handler.cpp src/RESTAPI_contact_list_handler.h src/RESTAPI_location_list_handler.cpp src/RESTAPI_location_list_handler.h src/RESTAPI_venue_list_handler.cpp src/RESTAPI_venue_list_handler.h src/RESTAPI_managementPolicy_list_handler.cpp src/RESTAPI_managementPolicy_list_handler.h src/RESTAPI_managementRole_handler.cpp src/RESTAPI_managementRole_handler.h src/RESTAPI_managementRole_list_handler.cpp src/RESTAPI_managementRole_list_handler.h src/RESTAPI_configurations_list_handler.cpp src/RESTAPI_configurations_list_handler.h src/SecurityDBProxy.cpp src/SecurityDBProxy.h) + src/RESTAPI_webSocketServer.h src/RESTAPI_webSocketServer.cpp src/RESTAPI_contact_list_handler.cpp src/RESTAPI_contact_list_handler.h src/RESTAPI_location_list_handler.cpp src/RESTAPI_location_list_handler.h src/RESTAPI_venue_list_handler.cpp src/RESTAPI_venue_list_handler.h src/RESTAPI_managementPolicy_list_handler.cpp src/RESTAPI_managementPolicy_list_handler.h src/RESTAPI_managementRole_handler.cpp src/RESTAPI_managementRole_handler.h src/RESTAPI_managementRole_list_handler.cpp src/RESTAPI_managementRole_list_handler.h src/RESTAPI_configurations_list_handler.cpp src/RESTAPI_configurations_list_handler.h src/SecurityDBProxy.cpp src/SecurityDBProxy.h src/APConfig.cpp src/APConfig.h) target_link_libraries(owprov PUBLIC ${Poco_LIBRARIES} ${MySQL_LIBRARIES} diff --git a/build b/build index afbe847..6d3e9dc 100644 --- a/build +++ b/build @@ -1 +1 @@ -126 \ No newline at end of file +129 \ No newline at end of file diff --git a/src/APConfig.cpp b/src/APConfig.cpp new file mode 100644 index 0000000..ed25ab7 --- /dev/null +++ b/src/APConfig.cpp @@ -0,0 +1,94 @@ +// +// Created by stephane bourque on 2021-09-07. +// + +#include "APConfig.h" +#include "StorageService.h" + +namespace OpenWifi { + + APConfig::APConfig(const std::string &SerialNumber, const std::string &DeviceType, Poco::Logger &L) + : SerialNumber_(SerialNumber), + DeviceType_(DeviceType), + Logger_(L) + {} + + bool APConfig::Get(std::string &Config) { + + if(Config_.empty()) { + try { + ProvObjects::InventoryTag D; + if(Storage()->InventoryDB().GetRecord("serialNumber", SerialNumber_, D)) { + + if(!D.deviceConfiguration.empty()) { + AddConfiguration(D.deviceConfiguration); + } + if(!D.entity.empty()) { + AddEntityConfig(D.entity); + } else if(!D.venue.empty()) { + AddVenueConfig(D.venue); + } + } + + // Now we have all the config we need. + } catch (const Poco::Exception &E ) { + Logger_.log(E); + } + } + + // So we have sections... + // interfaces + // metrics + // radios + // services + // + + Poco::JSON::Object CFG; + for(const auto &i:Config_) { + for(const auto &ConfigElement:i.configuration) { + Poco::JSON::Parser P; + auto O = P.parse(ConfigElement.configuration).extract(); + for(const auto &j:*O) { + CFG.set(j.first,j.second); + } + } + } + + if(Config_.empty()) + return false; + + return true; + } + + void APConfig::AddConfiguration(const std::string &UUID) { + ProvObjects::DeviceConfiguration Config; + + if(UUID.empty()) + return; + + if(Storage()->ConfigurationDB().GetRecord("id", UUID,Config)) { + Config_.push_back(Config); + } + } + + void APConfig::AddEntityConfig(const std::string &UUID) { + ProvObjects::Entity E; + if(Storage()->EntityDB().GetRecord("id",UUID,E)) { + AddConfiguration(E.deviceConfiguration); + if(!E.parent.empty()) + AddEntityConfig(E.parent); + } + } + + void APConfig::AddVenueConfig(const std::string &UUID) { + ProvObjects::Venue V; + if(Storage()->VenueDB().GetRecord("id",UUID,V)) { + AddConfiguration(V.deviceConfiguration); + if(!V.entity.empty()) { + AddEntityConfig(V.entity); + } else if(!V.parent.empty()) { + AddVenueConfig(V.parent); + } + } + } +} \ No newline at end of file diff --git a/src/APConfig.h b/src/APConfig.h new file mode 100644 index 0000000..feb847d --- /dev/null +++ b/src/APConfig.h @@ -0,0 +1,38 @@ +// +// Created by stephane bourque on 2021-09-07. +// + +#ifndef OWPROV_APCONFIG_H +#define OWPROV_APCONFIG_H + +#include +#include "Poco/Logger.h" +#include "RESTAPI_ProvObjects.h" + +namespace OpenWifi { + + typedef std::vector ConfigVec; + + class APConfig { + public: + explicit APConfig(const std::string & SerialNumber, const std::string & DeviceType, Poco::Logger & L); + + + [[nodiscard]] bool Get(std::string &Config); + + void AddConfiguration(const std::string &UUID); + void AddVenueConfig(const std::string &UUID); + void AddEntityConfig(const std::string &UUID); + + private: + std::string SerialNumber_; + std::string DeviceType_; + Poco::Logger & Logger_; + std::string CompleteConfig_; + ConfigVec Config_; + Types::StringPairVec Errors; + }; +} + + +#endif //OWPROV_APCONFIG_H diff --git a/src/RESTAPI_ProvObjects.cpp b/src/RESTAPI_ProvObjects.cpp index 5ce67c6..0b5b519 100644 --- a/src/RESTAPI_ProvObjects.cpp +++ b/src/RESTAPI_ProvObjects.cpp @@ -271,22 +271,6 @@ namespace OpenWifi::ProvObjects { return false; } - void ServiceConfiguration::to_json(Poco::JSON::Object &Obj) const { - info.to_json(Obj); - managementPolicy.to_json(Obj); - } - - bool ServiceConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) { - try { - info.from_json(Obj); - managementPolicy.from_json(Obj); - return true; - } catch(...) { - - } - return false; - } - void InventoryTag::to_json(Poco::JSON::Object &Obj) const { info.to_json(Obj); RESTAPI_utils::field_to_json(Obj, "serialNumber", serialNumber); @@ -322,6 +306,26 @@ namespace OpenWifi::ProvObjects { return false; } + void DeviceConfigurationElement::to_json(Poco::JSON::Object &Obj) const { + RESTAPI_utils::field_to_json( Obj,"name", name); + RESTAPI_utils::field_to_json( Obj,"description", description); + RESTAPI_utils::field_to_json( Obj,"weight", weight); + RESTAPI_utils::field_to_json( Obj,"configuration", configuration); + } + + bool DeviceConfigurationElement::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + RESTAPI_utils::field_from_json( Obj,"name",name); + RESTAPI_utils::field_from_json( Obj,"description",description); + RESTAPI_utils::field_from_json( Obj,"weight",weight); + RESTAPI_utils::field_from_json( Obj,"configuration",configuration); + return true; + } catch(...) { + + } + return false; + } + void DeviceConfiguration::to_json(Poco::JSON::Object &Obj) const { info.to_json(Obj); RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); diff --git a/src/RESTAPI_ProvObjects.h b/src/RESTAPI_ProvObjects.h index f95d399..33eb9e8 100644 --- a/src/RESTAPI_ProvObjects.h +++ b/src/RESTAPI_ProvObjects.h @@ -227,21 +227,24 @@ namespace OpenWifi::ProvObjects { }; typedef std::vector ContactVec; - struct ServiceConfiguration { - ObjectInfo info; - ManagementPolicy managementPolicy; + struct DeviceConfigurationElement { + std::string name; + std::string description; + uint64_t weight; + std::string configuration; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; + typedef std::vector DeviceConfigurationElementVec; struct DeviceConfiguration { - ObjectInfo info; - Types::UUID_t managementPolicy; - Types::StringVec deviceTypes; - std::string configuration; - Types::StringVec inUse; - Types::StringPairVec variables; + ObjectInfo info; + Types::UUID_t managementPolicy; + Types::StringVec deviceTypes; + DeviceConfigurationElementVec configuration; + Types::StringVec inUse; + Types::StringPairVec variables; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); diff --git a/src/RESTAPI_configurations_handler.cpp b/src/RESTAPI_configurations_handler.cpp index ba3e03f..a07401d 100644 --- a/src/RESTAPI_configurations_handler.cpp +++ b/src/RESTAPI_configurations_handler.cpp @@ -7,6 +7,9 @@ // #include "RESTAPI_configurations_handler.h" +#include "RESTAPI_ProvObjects.h" +#include "StorageService.h" +#include "Daemon.h" namespace OpenWifi{ void RESTAPI_configurations_handler::handleRequest(Poco::Net::HTTPServerRequest &Request, @@ -32,14 +35,213 @@ namespace OpenWifi{ void RESTAPI_configurations_handler::DoGet(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { + try { + auto UUID = GetBinding("uuid",""); + if(UUID.empty()) { + BadRequest(Request, Response, "Missing UUID."); + return; + } + + ProvObjects::DeviceConfiguration C; + if(Storage()->ConfigurationDB().GetRecord("id", UUID, C)) { + Poco::JSON::Object Answer; + + C.to_json(Answer); + ReturnObject(Request, Answer, Response); + return; + } + NotFound(Request, Response); + return; + } catch (const Poco::Exception &E) { + Logger_.log(E); + } + BadRequest(Request, Response, "Internal error. Consult documentation and try again."); } void RESTAPI_configurations_handler::DoDelete(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) {} + Poco::Net::HTTPServerResponse &Response) { + try { + auto UUID = GetBinding("uuid",""); + if(UUID.empty()) { + BadRequest(Request, Response, "Missing UUID."); + return; + } + + ProvObjects::DeviceConfiguration C; + if(Storage()->ConfigurationDB().GetRecord("id", UUID, C)) { + if(!C.inUse.empty()) { + BadRequest(Request, Response, "Configuration still in use."); + return; + } + + if(Storage()->ConfigurationDB().DeleteRecord("id", UUID)) { + OK(Request, Response); + return; + } + BadRequest(Request,Response,"Internal error. Please try again"); + return; + } + NotFound(Request, Response); + return; + } catch (const Poco::Exception &E) { + Logger_.log(E); + } + BadRequest(Request, Response, "Internal error. Consult documentation and try again."); + } void RESTAPI_configurations_handler::DoPost(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) {} + Poco::Net::HTTPServerResponse &Response) { + try { + auto UUID = GetBinding("uuid",""); + if(UUID.empty()) { + BadRequest(Request, Response, "Missing UUID."); + return; + } + + ProvObjects::DeviceConfiguration C; + Poco::JSON::Parser IncomingParser; + Poco::JSON::Object::Ptr Obj = IncomingParser.parse(Request.stream()).extract(); + if (!C.from_json(Obj)) { + BadRequest(Request, Response); + return; + } + + if(C.info.name.empty()) { + BadRequest(Request, Response, "Missing name."); + return; + } + + if(!Storage()->PolicyDB().Exists("id",C.managementPolicy)) { + BadRequest(Request, Response, "Unknown management policy."); + return; + } + + C.info.modified = C.info.created = std::time(nullptr); + C.info.id = Daemon()->CreateUUID(); + for(auto &i:C.info.notes) + i.createdBy = UserInfo_.userinfo.email; + + C.inUse.clear(); + if(C.deviceTypes.empty() || !Storage()->AreAcceptableDeviceTypes(C.deviceTypes, true)) { + BadRequest(Request, Response, "Missing valid device types."); + return; + } + + try { + for(const auto &i:C.configuration) { + Poco::JSON::Parser P; + auto T = P.parse(i.configuration).extract(); + } + } catch (const Poco::Exception &E) { + BadRequest(Request, Response, "Invalid configuration portion."); + return; + } + + if(Storage()->ConfigurationDB().CreateRecord(C)) { + Storage()->ConfigurationDB().GetRecord("id", C.info.id, C); + Poco::JSON::Object Answer; + + if(!C.managementPolicy.empty()) + Storage()->PolicyDB().AddInUse("id",C.managementPolicy,Storage()->PolicyDB().Prefix(), C.info.id); + + C.to_json(Answer); + ReturnObject(Request, Answer, Response); + return; + } + } catch (const Poco::Exception &E) { + Logger_.log(E); + } + BadRequest(Request, Response, "Internal error. Consult documentation and try again."); + } void RESTAPI_configurations_handler::DoPut(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) {} + Poco::Net::HTTPServerResponse &Response) { + try { + auto UUID = GetBinding("uuid",""); + if(UUID.empty()) { + BadRequest(Request, Response, "Missing UUID."); + return; + } + + ProvObjects::DeviceConfiguration Existing; + if(!Storage()->ConfigurationDB().GetRecord("id", UUID, Existing)) { + NotFound(Request, Response); + return; + } + + ProvObjects::DeviceConfiguration NewConfig; + Poco::JSON::Parser IncomingParser; + Poco::JSON::Object::Ptr Obj = IncomingParser.parse(Request.stream()).extract(); + if (!NewConfig.from_json(Obj)) { + BadRequest(Request, Response, "Illegal JSON posted document."); + return; + } + + for(auto &i:NewConfig.info.notes) + i.createdBy = UserInfo_.userinfo.email; + + if(NewConfig.managementPolicy.empty() || (NewConfig.managementPolicy!=Existing.managementPolicy && !Storage()->PolicyDB().Exists("id",NewConfig.managementPolicy))) { + BadRequest(Request, Response, "Management policy is not valid."); + return; + } + + if(!NewConfig.deviceTypes.empty() && !Storage()->AreAcceptableDeviceTypes(NewConfig.deviceTypes, true)) { + BadRequest(Request, Response, "Invalid device types."); + return; + } + + if(!NewConfig.deviceTypes.empty()) + Existing.deviceTypes = NewConfig.deviceTypes; + + if(!NewConfig.info.name.empty()) + Existing.info.name = NewConfig.info.name; + + if(!NewConfig.info.description.empty()) + Existing.info.description = NewConfig.info.description; + + NewConfig.info.modified = std::time(nullptr); + + if(!NewConfig.configuration.empty()) { + try { + for(const auto &i:NewConfig.configuration) { + Poco::JSON::Parser P; + auto T = P.parse(i.configuration).extract(); + } + } catch (const Poco::Exception &E) { + BadRequest(Request, Response, "Invalid configuration portion."); + return; + } + } + + if(!NewConfig.variables.empty()) + Existing.variables = NewConfig.variables; + + std::string OldPolicy; + OldPolicy = Existing.managementPolicy; + if(!NewConfig.managementPolicy.empty() && Existing.managementPolicy!=NewConfig.managementPolicy) { + OldPolicy = Existing.managementPolicy; + Existing.managementPolicy = NewConfig.managementPolicy; + } + + if(Storage()->ConfigurationDB().UpdateRecord("id",UUID,Existing)) { + if(!OldPolicy.empty()) { + Storage()->PolicyDB().DeleteInUse("id",OldPolicy,Storage()->ConfigurationDB().Prefix(),Existing.info.id); + } + + if(!Existing.managementPolicy.empty()) { + Storage()->PolicyDB().AddInUse("id",Existing.managementPolicy,Storage()->ConfigurationDB().Prefix(),Existing.info.id); + } + + ProvObjects::DeviceConfiguration D; + Storage()->ConfigurationDB().GetRecord("id",UUID,D); + Poco::JSON::Object Answer; + D.to_json(Answer); + ReturnObject(Request, Answer, Response); + return; + } + } catch (const Poco::Exception &E) { + Logger_.log(E); + } + BadRequest(Request, Response, "Internal error. Consult documentation and try again."); + } } \ No newline at end of file diff --git a/src/StorageService.h b/src/StorageService.h index af5f0d0..e5c2faa 100644 --- a/src/StorageService.h +++ b/src/StorageService.h @@ -60,6 +60,15 @@ class Storage : public SubSystemServer, Poco::Runnable { inline bool ValidatePrefix(const std::string &P) const { return ExistFunc_.find(P)!=ExistFunc_.end(); } inline bool IsAcceptableDeviceType(const std::string &D) const { return (DeviceTypes_.find(D)!=DeviceTypes_.end());}; + inline bool AreAcceptableDeviceTypes(const Types::StringVec &S, bool WildCardAllowed=true) const { + for(const auto &i:S) { + if(WildCardAllowed && i=="*") { + // We allow wildcards + } else if(DeviceTypes_.find(i)==DeviceTypes_.end()) + return false; + } + return true; + } void run() final; diff --git a/src/storage_configurations.cpp b/src/storage_configurations.cpp index 7d63780..4dc4be8 100644 --- a/src/storage_configurations.cpp +++ b/src/storage_configurations.cpp @@ -50,7 +50,7 @@ template<> void ORM::DB< OpenWifi::ConfigurationDBRecordType, OpenWifi::ProvO Out.info.modified = In.get<5>(); Out.managementPolicy = In.get<6>(); OpenWifi::Types::from_string(In.get<7>(), Out.deviceTypes); - Out.configuration = In.get<8>(); + Out.configuration = OpenWifi::RESTAPI_utils::to_object_array(In.get<8>()); OpenWifi::Types::from_string(In.get<9>(), Out.inUse); OpenWifi::Types::from_string(In.get<10>(), Out.variables); } @@ -64,7 +64,7 @@ template<> void ORM::DB< OpenWifi::ConfigurationDBRecordType, OpenWifi::ProvO Out.set<5>(In.info.modified); Out.set<6>(In.managementPolicy); Out.set<7>(OpenWifi::Types::to_string(In.deviceTypes)); - Out.set<8>(In.configuration); + Out.set<8>(OpenWifi::RESTAPI_utils::to_string(In.configuration)); Out.set<9>(OpenWifi::Types::to_string(In.inUse)); Out.set<10>(OpenWifi::Types::to_string(In.variables)); }