diff --git a/CMakeLists.txt b/CMakeLists.txt index 432df7a..9d6eb7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,7 +203,7 @@ add_executable(owprov src/ProvWebSocketClient.cpp src/ProvWebSocketClient.h src/Tasks/VenueRebooter.h src/Tasks/VenueUpgrade.h src/sdks/SDK_fms.cpp src/sdks/SDK_fms.h - ) + src/storage/storage_overrides.cpp src/storage/storage_overrides.h src/RESTAPI/RESTAPI_overrides_handler.cpp src/RESTAPI/RESTAPI_overrides_handler.h) target_link_libraries(owprov PUBLIC ${Poco_LIBRARIES} diff --git a/build b/build index f11c82a..3f10ffe 100644 --- a/build +++ b/build @@ -1 +1 @@ -9 \ No newline at end of file +15 \ No newline at end of file diff --git a/openapi/owprov.yaml b/openapi/owprov.yaml index 24536ff..27ec82f 100644 --- a/openapi/owprov.yaml +++ b/openapi/owprov.yaml @@ -1220,6 +1220,40 @@ components: items: $ref: '#/components/schemas/SubscriberDevice' + ConfigurationOverride: + type: object + properties: + source: + type: string + reason: + type: string + parameterName: + type: string + parameterType: + enum: + - string + - integer + - boolean + parameterValue: + type: string + modified: + type: integer + format: int64 + + ConfigurationOverrideList: + type: object + properties: + serialNumber: + type: string + managementPolicy: + type: string + format: uuid + overrides: + type: array + items: + $ref: '#/components/schemas/ConfigurationOverride' + + ######################################################################################### ## ## These are endpoints that all services in the OPenWiFI stack must provide @@ -2211,6 +2245,94 @@ paths: 404: $ref: '#/components/responses/NotFound' + /configurationOverrides/{serialNumber}: + get: + tags: + - Configuration Overrides + operationId: getCponfigurationOverrides + summary: retrieve a list of configuration overrides for a given device + parameters: + - in: path + name: serialNumber + schema: + type: string + required: true + responses: + 200: + description: Return a list of configuration overrides. + content: + application/json: + schema: + $ref: '#/components/schemas/ConfigurationOverrideList' + 400: + $ref: '#/components/responses/BadRequest' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + delete: + tags: + - Configuration Overrides + operationId: deleteCponfigurationOverrides + summary: delete all configuration overrides for a given device from a given source + parameters: + - in: path + name: serialNumber + schema: + type: string + required: true + - in: query + name: source + schema: + type: string + required: true + responses: + 200: + $ref: '#/components/responses/Success' + 400: + $ref: '#/components/responses/BadRequest' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + put: + tags: + - Configuration Overrides + operationId: modifyConfigurationOverrides + summary: modify configuration overrides for a given device for a given source + parameters: + - in: path + name: serialNumber + schema: + type: string + required: true + - in: query + name: source + schema: + type: string + required: true + requestBody: + description: Information used to modify the override list + content: + application/json: + schema: + $ref: '#/components/schemas/ConfigurationOverrideList' + responses: + 200: + description: Return the modified configuration overrides. + content: + application/json: + schema: + $ref: '#/components/schemas/ConfigurationOverrideList' + 400: + $ref: '#/components/responses/BadRequest' + 403: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + + + /venue: get: tags: diff --git a/src/RESTAPI/RESTAPI_overrides_handler.cpp b/src/RESTAPI/RESTAPI_overrides_handler.cpp new file mode 100644 index 0000000..4ed522e --- /dev/null +++ b/src/RESTAPI/RESTAPI_overrides_handler.cpp @@ -0,0 +1,106 @@ +// +// Created by stephane bourque on 2022-11-03. +// + +#include + +#include "RESTAPI_overrides_handler.h" +#include "framework/utils.h" + +namespace OpenWifi { + void RESTAPI_overrides_handler::DoGet() { + std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, ""); + + if(!Utils::NormalizeMac(SerialNumber)) { + return BadRequest(RESTAPI::Errors::InvalidSerialNumber); + } + + ProvObjects::ConfigurationOverrideList ExistingObject; + if(!DB_.GetRecord("serialNumber", SerialNumber, ExistingObject)) { + return NotFound(); + } + Poco::JSON::Object Answer; + ExistingObject.to_json(Answer); + + return ReturnObject(Answer); + } + + void RESTAPI_overrides_handler::DoDelete() { + std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, ""); + + if(!Utils::NormalizeMac(SerialNumber)) { + return BadRequest(RESTAPI::Errors::InvalidSerialNumber); + } + + auto Source = GetParameter("source",""); + if(Source.empty()) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + ProvObjects::ConfigurationOverrideList ExistingObject; + if(!DB_.GetRecord("serialNumber", SerialNumber, ExistingObject)) { + return NotFound(); + } + + ExistingObject.overrides.erase( std::remove_if( ExistingObject.overrides.begin(), ExistingObject.overrides.end(), + [Source](const ProvObjects::ConfigurationOverride &O) ->bool { + return O.source==Source; + }),ExistingObject.overrides.end()); + + if(DB_.UpdateRecord("serialNumber", SerialNumber, ExistingObject)) { + return OK(); + } + return BadRequest(RESTAPI::Errors::NoRecordsDeleted); + } + + void RESTAPI_overrides_handler::DoPut() { + std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, ""); + + if(!Utils::NormalizeMac(SerialNumber)) { + return BadRequest(RESTAPI::Errors::InvalidSerialNumber); + } + + auto Source = GetParameter("source",""); + if(Source.empty()) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + + ProvObjects::ConfigurationOverrideList NewObject; + if(!NewObject.from_json(ParsedBody_)) { + return BadRequest(RESTAPI::Errors::InvalidJSONDocument); + } + + ProvObjects::ConfigurationOverrideList ExistingObject; + if(!DB_.GetRecord("serialNumber", SerialNumber, ExistingObject)) { + ExistingObject.serialNumber = SerialNumber; + DB_.CreateRecord(ExistingObject); + } else { + // remove all the old records with that source. + ExistingObject.overrides.erase( std::remove_if( ExistingObject.overrides.begin(), ExistingObject.overrides.end(), + [Source](const ProvObjects::ConfigurationOverride &O) ->bool { + return O.source==Source; + }),ExistingObject.overrides.end()); + } + + for(auto & override:NewObject.overrides) { + if(override.parameterName.empty()) { + continue; + } + if(override.parameterType!="string" && override.parameterType!="boolean" && override.parameterType!="integer") { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + override.source = Source; + override.modified = Utils::Now(); + ExistingObject.overrides.emplace_back(override); + } + + if(DB_.UpdateRecord("serialNumber",SerialNumber,ExistingObject)) { + Poco::JSON::Object Answer; + ExistingObject.to_json(Answer); + return ReturnObject(Answer); + } + + return BadRequest(RESTAPI::Errors::RecordNotUpdated); + } + +} // OpenWifi \ No newline at end of file diff --git a/src/RESTAPI/RESTAPI_overrides_handler.h b/src/RESTAPI/RESTAPI_overrides_handler.h new file mode 100644 index 0000000..5643ce9 --- /dev/null +++ b/src/RESTAPI/RESTAPI_overrides_handler.h @@ -0,0 +1,31 @@ +// +// Created by stephane bourque on 2022-11-03. +// + + +#pragma once + +#include "framework/RESTAPI_Handler.h" +#include "StorageService.h" + +namespace OpenWifi { + class RESTAPI_overrides_handler : public RESTAPIHandler { + public: + RESTAPI_overrides_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) + : RESTAPIHandler(bindings, L, + std::vector{ + Poco::Net::HTTPRequest::HTTP_GET, + Poco::Net::HTTPRequest::HTTP_PUT, Poco::Net::HTTPRequest::HTTP_DELETE, + Poco::Net::HTTPRequest::HTTP_OPTIONS}, + Server, + TransactionId, + Internal){} + static auto PathName() { return std::list{"/api/v1/configurationOverrides/{serialNumber}"}; }; + private: + OverridesDB &DB_=StorageService()->OverridesDB(); + void DoGet() final ; + void DoPost() final {} ; + void DoPut() final ; + void DoDelete() final ; + }; +} diff --git a/src/RESTAPI/RESTAPI_routers.cpp b/src/RESTAPI/RESTAPI_routers.cpp index 3b7cb70..63d563a 100644 --- a/src/RESTAPI/RESTAPI_routers.cpp +++ b/src/RESTAPI/RESTAPI_routers.cpp @@ -34,6 +34,7 @@ #include "RESTAPI/RESTAPI_op_contact_list_handler.h" #include "RESTAPI/RESTAPI_op_location_handler.h" #include "RESTAPI/RESTAPI_op_location_list_handler.h" +#include "RESTAPI/RESTAPI_overrides_handler.h" #include "framework/RESTAPI_SystemCommand.h" #include "framework/RESTAPI_WebSocketServer.h" @@ -76,7 +77,8 @@ namespace OpenWifi { RESTAPI_op_contact_list_handler, RESTAPI_op_location_handler, RESTAPI_op_location_list_handler, - RESTAPI_asset_server + RESTAPI_asset_server, + RESTAPI_overrides_handler >(Path,Bindings,L, S, TransactionId); } @@ -115,7 +117,8 @@ namespace OpenWifi { RESTAPI_op_contact_handler, RESTAPI_op_contact_list_handler, RESTAPI_op_location_handler, - RESTAPI_op_location_list_handler + RESTAPI_op_location_list_handler, + RESTAPI_overrides_handler >(Path, Bindings, L, S, TransactionId); } } \ No newline at end of file diff --git a/src/RESTObjects/RESTAPI_ProvObjects.cpp b/src/RESTObjects/RESTAPI_ProvObjects.cpp index 50f4038..f5fa14f 100644 --- a/src/RESTObjects/RESTAPI_ProvObjects.cpp +++ b/src/RESTObjects/RESTAPI_ProvObjects.cpp @@ -1195,6 +1195,48 @@ namespace OpenWifi::ProvObjects { return false; } + void ConfigurationOverride::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"source",source); + field_to_json(Obj,"reason",reason); + field_to_json(Obj,"parameterName",parameterName); + field_to_json(Obj,"parameterType",parameterType); + field_to_json(Obj,"parameterValue",parameterValue); + field_to_json(Obj,"modified",modified); + } + + bool ConfigurationOverride::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"source",source); + field_from_json(Obj,"reason",reason); + field_from_json(Obj,"parameterName",parameterName); + field_from_json(Obj,"parameterType",parameterType); + field_from_json(Obj,"parameterValue",parameterValue); + field_from_json(Obj,"modified",modified); + return true; + } catch(...) { + + } + return false; + } + + void ConfigurationOverrideList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"serialNumber",serialNumber); + field_to_json(Obj,"managementPolicy",managementPolicy); + field_to_json(Obj,"overrides",overrides); + } + + bool ConfigurationOverrideList::from_json(const Poco::JSON::Object::Ptr &Obj) { + try { + field_from_json(Obj,"serialNumber",serialNumber); + field_from_json(Obj,"managementPolicy",managementPolicy); + field_from_json(Obj,"overrides",overrides); + return true; + } catch(...) { + + } + return false; + } + } diff --git a/src/RESTObjects/RESTAPI_ProvObjects.h b/src/RESTObjects/RESTAPI_ProvObjects.h index 59f59ed..0f3834e 100644 --- a/src/RESTObjects/RESTAPI_ProvObjects.h +++ b/src/RESTObjects/RESTAPI_ProvObjects.h @@ -693,6 +693,27 @@ namespace OpenWifi::ProvObjects { bool from_json(const Poco::JSON::Object::Ptr &Obj); }; + struct ConfigurationOverride { + std::string source; + std::string reason; + std::string parameterName; + std::string parameterType; + std::string parameterValue; + std::uint64_t modified; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + + struct ConfigurationOverrideList { + std::string serialNumber; + Types::UUID_t managementPolicy; + std::vector overrides; + + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(const Poco::JSON::Object::Ptr &Obj); + }; + bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I); bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I); bool CreateObjectInfo(const SecurityObjects::UserInfo &U, ObjectInfo &I); diff --git a/src/StorageService.cpp b/src/StorageService.cpp index d5e0198..dd39f44 100644 --- a/src/StorageService.cpp +++ b/src/StorageService.cpp @@ -37,6 +37,7 @@ namespace OpenWifi { SubscriberDeviceDB_ = std::make_unique(dbType_, *Pool_, Logger()); OpLocationDB_ = std::make_unique(dbType_, *Pool_, Logger()); OpContactDB_ = std::make_unique(dbType_, *Pool_, Logger()); + OverridesDB_ = std::make_unique(dbType_, *Pool_, Logger()); EntityDB_->Create(); PolicyDB_->Create(); @@ -56,6 +57,7 @@ namespace OpenWifi { SubscriberDeviceDB_->Create(); OpLocationDB_->Create(); OpContactDB_->Create(); + OverridesDB_->Create(); ExistFunc_[EntityDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return EntityDB_->Exists(F,V); }; ExistFunc_[PolicyDB_->Prefix()] = [=](const char *F, std::string &V) -> bool { return PolicyDB_->Exists(F,V); }; @@ -75,6 +77,7 @@ namespace OpenWifi { ExistFunc_[SubscriberDeviceDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return SubscriberDeviceDB_->Exists(F,V); }; ExistFunc_[OpLocationDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return OpLocationDB_->Exists(F,V); }; ExistFunc_[OpContactDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return OpContactDB_->Exists(F,V); }; + ExistFunc_[OverridesDB_->Prefix()] = [=](const char *F, std::string &V) ->bool { return OverridesDB_->Exists(F,V); }; ExpandFunc_[EntityDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return EntityDB_->GetNameAndDescription(F,V, Name, Description); }; ExpandFunc_[PolicyDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) -> bool { return PolicyDB_->GetNameAndDescription(F,V, Name, Description); }; @@ -94,6 +97,7 @@ namespace OpenWifi { ExpandFunc_[SubscriberDeviceDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) ->bool { return SubscriberDeviceDB_->GetNameAndDescription(F,V, Name, Description); }; ExpandFunc_[OpLocationDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) ->bool { return OpLocationDB_->GetNameAndDescription(F,V, Name, Description); }; ExpandFunc_[OpContactDB_->Prefix()] = [=](const char *F, std::string &V, std::string &Name, std::string & Description) ->bool { return OpContactDB_->GetNameAndDescription(F,V, Name, Description); }; + ExpandFunc_[OverridesDB_->Prefix()] = [=]( [[maybe_unused]] const char *F, [[maybe_unused]] std::string &V, [[maybe_unused]] std::string &Name, [[maybe_unused]] std::string & Description) ->bool { return false; }; InventoryDB_->InitializeSerialCache(); diff --git a/src/StorageService.h b/src/StorageService.h index c9c5e6e..e0b62b6 100644 --- a/src/StorageService.h +++ b/src/StorageService.h @@ -27,6 +27,7 @@ #include "storage/storage_operataor.h" #include "storage/storage_service_class.h" #include "storage/storage_sub_devices.h" +#include "storage/storage_overrides.h" #include "Poco/URI.h" #include "framework/ow_constants.h" @@ -64,6 +65,7 @@ namespace OpenWifi { OpenWifi::SubscriberDeviceDB & SubscriberDeviceDB() { return *SubscriberDeviceDB_; }; OpenWifi::OpLocationDB & OpLocationDB() { return *OpLocationDB_; }; OpenWifi::OpContactDB & OpContactDB() { return *OpContactDB_; }; + OpenWifi::OverridesDB & OverridesDB() { return *OverridesDB_; }; bool Validate(const Poco::URI::QueryParameters &P, RESTAPI::Errors::msg &Error); bool Validate(const Types::StringVec &P, std::string &Error); @@ -116,6 +118,7 @@ namespace OpenWifi { std::unique_ptr SubscriberDeviceDB_; std::unique_ptr OpLocationDB_; std::unique_ptr OpContactDB_; + std::unique_ptr OverridesDB_; std::string DefaultOperator_; diff --git a/src/storage/storage_overrides.cpp b/src/storage/storage_overrides.cpp new file mode 100644 index 0000000..d19f24d --- /dev/null +++ b/src/storage/storage_overrides.cpp @@ -0,0 +1,50 @@ +// +// Created by stephane bourque on 2022-11-03. +// + +#include "storage_overrides.h" +#include "framework/OpenWifiTypes.h" +#include "framework/RESTAPI_utils.h" +#include "SerialNumberCache.h" + +namespace OpenWifi { + static ORM::FieldVec OverridesDB_Fields{ + // object info + ORM::Field{"serialNumber",64, true}, + ORM::Field{"managementPolicy", ORM::FieldType::FT_TEXT}, + ORM::Field{"overrides", ORM::FieldType::FT_TEXT} + }; + + static ORM::IndexVec OverridesDB_Indexes{ + }; + + OverridesDB::OverridesDB(OpenWifi::DBType T, Poco::Data::SessionPool &P, Poco::Logger &L) : + DB(T, "overrides", OverridesDB_Fields, OverridesDB_Indexes, P, L, "ovr") {} + + bool OverridesDB::Upgrade([[maybe_unused]] uint32_t from, uint32_t &to) { + to = Version(); + std::vector Script{ + }; + + for (const auto &i: Script) { + try { + auto Session = Pool_.get(); + Session << i, Poco::Data::Keywords::now; + } catch (...) { + + } + } + return true; + } +} +template<> void ORM::DB::Convert(const OpenWifi::OverridesDBRecordType &In, OpenWifi::ProvObjects::ConfigurationOverrideList &Out) { + Out.serialNumber = In.get<0>(); + Out.managementPolicy = In.get<1>(); + Out.overrides = OpenWifi::RESTAPI_utils::to_object_array(In.get<2>()); +} + +template<> void ORM::DB< OpenWifi::OverridesDBRecordType, OpenWifi::ProvObjects::ConfigurationOverrideList>::Convert(const OpenWifi::ProvObjects::ConfigurationOverrideList &In, OpenWifi::OverridesDBRecordType &Out) { + Out.set<0>(In.serialNumber); + Out.set<1>(In.managementPolicy); + Out.set<2>(OpenWifi::RESTAPI_utils::to_string(In.overrides)); +} diff --git a/src/storage/storage_overrides.h b/src/storage/storage_overrides.h new file mode 100644 index 0000000..34ad297 --- /dev/null +++ b/src/storage/storage_overrides.h @@ -0,0 +1,32 @@ +// +// Created by stephane bourque on 2022-11-03. +// + +#pragma once + +#include "framework/orm.h" +#include "RESTObjects/RESTAPI_ProvObjects.h" + +namespace OpenWifi { + + typedef Poco::Tuple< + std::string, + std::string, + std::string + > OverridesDBRecordType; + + class OverridesDB : public ORM::DB { + public: + explicit OverridesDB( OpenWifi::DBType T, Poco::Data::SessionPool & P, Poco::Logger &L); + virtual ~OverridesDB() {}; + inline uint32_t Version() override { + return 1; + } + + bool Upgrade(uint32_t from, uint32_t &to) override; + + private: + }; + +} // OpenWifi +