diff --git a/openapi/ucentralfws.yaml b/openapi/ucentralfws.yaml index 012f1d3..78fbcd9 100644 --- a/openapi/ucentralfws.yaml +++ b/openapi/ucentralfws.yaml @@ -30,25 +30,43 @@ components: responses: NotFound: - description: The specified resource was not found + description: The specified resource was not found. content: application/json: schema: - $ref: '#/components/schemas/GenericErrorResponse' + properties: + ErrorCode: + type: integer + ErrorDetails: + type: string + ErrorDescription: + type: string Unauthorized: - description: Unauthorized + description: The requested does not have sufficient rights to perform the operation. content: application/json: schema: - $ref: '#/components/schemas/GenericErrorResponse' + properties: + ErrorCode: + type: integer + ErrorDetails: + type: string + ErrorDescription: + type: string Success: - description: Success + description: The requested operation was performed. content: application/json: schema: - $ref: '#/components/schemas/GenericGoodAnswer' + properties: + Operation: + type: string + Details: + type: string + Code: + type: integer schemas: FirmwareDetails: @@ -261,69 +279,6 @@ components: Code: type: integer - WebTokenRequest: - description: User Id and password. - type: object - required: - - userId - - password - properties: - userId: - type: string - default: support@example.com - password: - type: string - default: support - refreshToken: - type: string - example: - userId: support@example.com - password: support - - WebTokenResult: - description: Login and Refresh Tokens to be used in subsequent API calls. - type: object - properties: - access_token: - type: string - refresh_token: - type: string - token_type: - type: string - expires_in: - type: integer - format: int32 - idle_timeout: - type: integer - format: int32 - username: - type: string - created: - type: string - format: 'date-time' - aclTemplate: - $ref: '#/components/schemas/WebTokenAclTemplate' - - WebTokenAclTemplate: - type: object - properties: - aclTemplate: - $ref: '#/components/schemas/AclTemplate' - - AclTemplate: - type: object - properties: - Read: - type: boolean - ReadWrite: - type: boolean - ReadWriteCreate: - type: boolean - Delete: - type: boolean - PortalLogin: - type: boolean - ######################################################################################### ## ## These are endpoints that all services in the uCentral stack must provide @@ -387,56 +342,6 @@ components: ######################################################################################### paths: - /oauth2: - post: - tags: - - Authentication - summary: Get access token - to be used as Bearer token header for all other API requests. - operationId: getAccessToken - requestBody: - description: User id and password - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/WebTokenRequest' - responses: - 200: - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/WebTokenResult' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - - /oauth2/{token}: - delete: - tags: - - Authentication - summary: Revoke a token. - operationId: removeAccessToken - parameters: - - in: path - name: token - schema: - type: - string - required: true - responses: - 200: - description: successful operation - content: - application/json: - schema: - $ref: '#/components/schemas/GenericGoodAnswer' - 403: - $ref: '#/components/responses/Unauthorized' - 404: - $ref: '#/components/responses/NotFound' - /firmwares: get: tags: diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..8315b83 --- /dev/null +++ b/run.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +daemon=ucentralsec + +if [[ "$1" == "aws" ]] +then + cp ${daemon}.properties.aws ${daemon}.properties + . ./set_env.sh + cd cmake-build + ./${daemon} --daemon + echo "Running AWS version as daemon..." +fi + +if [[ "$1" == "priv" ]] +then + cp ${daemon}.properties.priv ${daemon}.properties + . ./set_env.sh + cd cmake-build + ./${daemon} --daemon + echo "Running private version as daemon..." +fi + + + diff --git a/src/MicroService.cpp b/src/MicroService.cpp index 8d38982..4508404 100644 --- a/src/MicroService.cpp +++ b/src/MicroService.cpp @@ -451,12 +451,25 @@ namespace uCentral { return false; } + void MicroService::SavePID() { + try { + std::ofstream O; + O.open(Daemon()->DataDir() + "/pidfile",std::ios::binary | std::ios::trunc); + O << Poco::Process::id(); + O.close(); + } catch (...) + { + std::cout << "Could not save system ID" << std::endl; + } + } + int MicroService::main(const ArgVec &args) { MyErrorHandler ErrorHandler(*this); Poco::ErrorHandler::set(&ErrorHandler); if (!HelpRequested_) { + SavePID(); Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME); logger.notice(Poco::format("Starting %s version %s.",DAEMON_APP_NAME, Version())); diff --git a/src/MicroService.h b/src/MicroService.h index 26654f7..8bed9f3 100644 --- a/src/MicroService.h +++ b/src/MicroService.h @@ -22,6 +22,7 @@ #include "Poco/Crypto/Cipher.h" #include "Poco/SHA2Engine.h" #include "Poco/Net/HTTPServerRequest.h" +#include "Poco/Process.h" #include "uCentralTypes.h" #include "SubSystemServer.h" @@ -129,6 +130,9 @@ namespace uCentral { [[nodiscard]] MicroServiceMetaVec GetServices(); [[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); + void SavePID(); + inline uint64_t GetPID() { return Poco::Process::id(); }; + private: bool HelpRequested_ = false; std::string LogDir_; diff --git a/src/RESTAPI_SecurityObjects.cpp b/src/RESTAPI_SecurityObjects.cpp index 74891c3..8c70e6b 100644 --- a/src/RESTAPI_SecurityObjects.cpp +++ b/src/RESTAPI_SecurityObjects.cpp @@ -25,6 +25,56 @@ namespace uCentral::SecurityObjects { field_to_json(Obj,"PortalLogin",PortalLogin_); } + ResourceAccessType ResourceAccessTypeFromString(const std::string &s) { + if(s=="READ") return READ; + if(s=="MODIFY") return MODIFY; + if(s=="DELETE") return DELETE; + if(s=="CREATE") return CREATE; + if(s=="TEST") return TEST; + if(s=="MOVE") return MOVE; + return NONE; + } + + std::string ResourceAccessTypeToString(const ResourceAccessType & T) { + switch(T) { + case READ: return "READ"; + case MODIFY: return "MODIFY"; + case DELETE: return "DELETE"; + case CREATE: return "CREATE"; + case TEST: return "TEST"; + case MOVE: return "MOVE"; + default: return "NONE"; + } + } + + USER_ROLE UserTypeFromString(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; + } + + std::string UserTypeToString(USER_ROLE U) { + switch(U) { + case UNKNOWN: return "unknown"; + case ROOT: return "root"; + case SUBSCRIBER: return "subscriber"; + case CSR: return "csr"; + case SYSTEM: return "system"; + case SPECIAL: return "special"; + default: return "unknown"; + } + } + bool AclTemplate::from_json(const Poco::JSON::Object::Ptr &Obj) { try { field_from_json(Obj, "Read", Read_); @@ -48,6 +98,8 @@ namespace uCentral::SecurityObjects { field_to_json(Obj,"idle_timeout",idle_timeout_); field_to_json(Obj,"created",created_); field_to_json(Obj,"username",username_); + field_to_json(Obj,"userMustChangePassword",userMustChangePassword); + field_to_json(Obj,"errorCode", errorCode); Obj.set("aclTemplate",AclTemplateObj); } @@ -64,6 +116,7 @@ namespace uCentral::SecurityObjects { field_from_json(Obj, "idle_timeout", idle_timeout_); field_from_json(Obj, "created", created_); field_from_json(Obj, "username", username_); + field_from_json(Obj, "userMustChangePassword",userMustChangePassword); return true; } catch (...) { @@ -94,7 +147,7 @@ namespace uCentral::SecurityObjects { field_to_json(Obj,"owner", owner); field_to_json(Obj,"suspended", suspended); field_to_json(Obj,"blackListed", blackListed); - field_to_json(Obj,"userRole", userRole); + field_to_json(Obj,"userRole", userRole, UserTypeToString); field_to_json(Obj,"userTypeProprietaryInfo", userTypeProprietaryInfo); field_to_json(Obj,"securityPolicy", securityPolicy); field_to_json(Obj,"securityPolicyChange", securityPolicyChange); @@ -116,7 +169,7 @@ namespace uCentral::SecurityObjects { field_from_json(Obj,"currentLoginURI",currentLoginURI); field_from_json(Obj,"locale",locale); field_from_json(Obj,"notes",notes); - field_from_json(Obj,"userRole",userRole); + field_from_json(Obj,"userRole",userRole, UserTypeFromString); field_from_json(Obj,"securityPolicy",securityPolicy); field_from_json(Obj,"userTypeProprietaryInfo",userTypeProprietaryInfo); field_from_json(Obj,"validationDate",validationDate); @@ -232,5 +285,72 @@ namespace uCentral::SecurityObjects { return false; } + void NoteInfo::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"created", created); + field_to_json(Obj,"createdBy", createdBy); + field_to_json(Obj,"note", note); + } + + bool NoteInfo::from_json(Poco::JSON::Object::Ptr Obj) { + try { + field_from_json(Obj,"created",created); + field_from_json(Obj,"createdBy",createdBy); + field_from_json(Obj,"note",note); + } catch(...) { + + } + return false; + } + + void ProfileAction::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"resource", resource); + field_to_json(Obj,"access", access, ResourceAccessTypeToString); + } + + bool ProfileAction::from_json(Poco::JSON::Object::Ptr Obj) { + try { + field_from_json(Obj,"resource",resource); + field_from_json(Obj,"access",access,ResourceAccessTypeFromString ); + } catch(...) { + + } + return false; + } + + void SecurityProfile::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj,"id", id); + field_to_json(Obj,"name", name); + field_to_json(Obj,"description", description); + field_to_json(Obj,"policy", policy); + field_to_json(Obj,"role", role); + field_to_json(Obj,"notes", notes); + } + + bool SecurityProfile::from_json(Poco::JSON::Object::Ptr Obj) { + try { + field_from_json(Obj,"id",id); + field_from_json(Obj,"name",name); + field_from_json(Obj,"description",description); + field_from_json(Obj,"policy",policy); + field_from_json(Obj,"role",role); + field_from_json(Obj,"notes",notes); + } catch(...) { + + } + return false; + } + + void SecurityProfileList::to_json(Poco::JSON::Object &Obj) const { + field_to_json(Obj, "profiles", profiles); + } + + bool SecurityProfileList::from_json(Poco::JSON::Object::Ptr Obj) { + try { + field_from_json(Obj,"profiles",profiles); + } catch(...) { + + } + return false; + } } diff --git a/src/RESTAPI_SecurityObjects.h b/src/RESTAPI_SecurityObjects.h index f90257f..db6f521 100644 --- a/src/RESTAPI_SecurityObjects.h +++ b/src/RESTAPI_SecurityObjects.h @@ -30,17 +30,45 @@ namespace uCentral::SecurityObjects { std::string id_token_; std::string token_type_; std::string username_; - uint64_t expires_in_; - uint64_t idle_timeout_; + bool userMustChangePassword=false; + uint64_t errorCode=0; + uint64_t expires_in_=0; + uint64_t idle_timeout_=0; AclTemplate acl_template_; - uint64_t created_; + uint64_t created_=0; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; + enum AUTH_ERROR { + SUCCESS, + PASSWORD_CHANGE_REQUIRED, + PASSWORD_DOES_NOT_MATCH, + PASSWORD_ALREADY_USED, + USERNAME_PENDING_VERIFICATION, + PASSWORD_INVALID, + INTERNAL_ERROR + }; + + enum USER_ROLE { + UNKNOWN, ROOT, ADMIN, SUBSCRIBER, CSR, SYSTEM, SPECIAL + }; + + USER_ROLE UserTypeFromString(const std::string &U); + std::string UserTypeToString(USER_ROLE U); + + struct NoteInfo { + uint64_t created = std::time(nullptr); + std::string createdBy; + std::string note; + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(Poco::JSON::Object::Ptr Obj); + }; + typedef std::vector NoteInfoVec; + struct UserInfo { - uint64_t Id = 0; + std::string Id; std::string name; std::string description; std::string avatar; @@ -50,31 +78,31 @@ namespace uCentral::SecurityObjects { uint64_t validationDate = 0; uint64_t creationDate = 0; std::string validationURI; - bool changePassword = true; + bool changePassword = false; uint64_t lastLogin = 0; std::string currentLoginURI; uint64_t lastPasswordChange = 0; uint64_t lastEmailCheck = 0; bool waitingForEmailCheck = false; std::string locale; - std::string notes; + NoteInfoVec notes; std::string location; std::string owner; bool suspended = false; bool blackListed = false; - std::string userRole; + USER_ROLE userRole; std::string userTypeProprietaryInfo; std::string securityPolicy; - uint64_t securityPolicyChange; - + uint64_t securityPolicyChange = 0 ; std::string currentPassword; - Types::StringVec lastPasswords; + Types::StringVec lastPasswords; std::string oauthType; std::string oauthUserInfo; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; + typedef std::vector UserInfoVec; struct InternalServiceInfo { std::string privateURI; @@ -83,27 +111,29 @@ namespace uCentral::SecurityObjects { void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; + typedef std::vector InternalServiceInfoVec; struct InternalSystemServices { std::string key; std::string version; - std::vector services; + InternalServiceInfoVec services; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; struct SystemEndpoint { std::string type; - uint64_t id; + uint64_t id = 0; std::string vendor; std::string uri; std::string authenticationType; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; + typedef std::vector SystemEndpointVec; struct SystemEndpointList { - std::vector endpoints; + SystemEndpointVec endpoints; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; @@ -114,9 +144,46 @@ namespace uCentral::SecurityObjects { void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; - typedef std::map UserInfoCache; + enum ResourceAccessType { + NONE, + READ, + MODIFY, + DELETE, + CREATE, + TEST, + MOVE + }; + + ResourceAccessType ResourceAccessTypeFromString(const std::string &s); + std::string ResourceAccessTypeToString(const ResourceAccessType & T); + + struct ProfileAction { + std::string resource; + ResourceAccessType access; + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(Poco::JSON::Object::Ptr Obj); + }; + typedef std::vector ProfileActionVec; + + struct SecurityProfile { + uint64_t id; + std::string name; + std::string description; + ProfileActionVec policy; + std::string role; + NoteInfoVec notes; + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(Poco::JSON::Object::Ptr Obj); + }; + typedef std::vector SecurityProfileVec; + + struct SecurityProfileList { + SecurityProfileVec profiles; + void to_json(Poco::JSON::Object &Obj) const; + bool from_json(Poco::JSON::Object::Ptr Obj); + }; } -#endif //UCENTRAL_RESTAPI_SECURITYOBJECTS_H +#endif //UCENTRAL_RESTAPI_SECURITYOBJECTS_H \ No newline at end of file diff --git a/src/RESTAPI_handler.cpp b/src/RESTAPI_handler.cpp index 07db7b2..f697e4e 100644 --- a/src/RESTAPI_handler.cpp +++ b/src/RESTAPI_handler.cpp @@ -45,7 +45,7 @@ namespace uCentral { if (PathItems[i] != ParamItems[i]) { if (ParamItems[i][0] == '{') { auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2); - bindings[ParamName] = PathItems[i]; + bindings[Poco::toLower(ParamName)] = PathItems[i]; } else { Matched = false; } @@ -111,8 +111,7 @@ namespace uCentral { } const std::string &RESTAPIHandler::GetBinding(const std::string &Name, const std::string &Default) { - auto E = Bindings_.find(Name); - + auto E = Bindings_.find(Poco::toLower(Name)); if (E == Bindings_.end()) return Default; @@ -121,7 +120,6 @@ namespace uCentral { static std::string MakeList(const std::vector &L) { std::string Return; - for (const auto &i : L) if (Return.empty()) Return = i; @@ -184,27 +182,53 @@ namespace uCentral { } void RESTAPIHandler::BadRequest(Poco::Net::HTTPServerRequest &Request, - Poco::Net::HTTPServerResponse &Response) { + Poco::Net::HTTPServerResponse &Response, + const std::string & Reason) { PrepareResponse(Request, Response, Poco::Net::HTTPResponse::HTTP_BAD_REQUEST); - Response.send(); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",500); + ErrorObject.set("ErrorDetails",Request.getMethod()); + ErrorObject.set("ErrorDescription",Reason.empty() ? "Command is missing parameters or wrong values." : Reason) ; + std::ostream &Answer = Response.send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); } void RESTAPIHandler::UnAuthorized(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { PrepareResponse(Request, Response, Poco::Net::HTTPResponse::HTTP_FORBIDDEN); - Response.send(); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",403); + ErrorObject.set("ErrorDetails",Request.getMethod()); + ErrorObject.set("ErrorDescription","You do not have access to this resource."); + std::ostream &Answer = Response.send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); } void RESTAPIHandler::NotFound(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { PrepareResponse(Request, Response, Poco::Net::HTTPResponse::HTTP_NOT_FOUND); - Response.send(); + Poco::JSON::Object ErrorObject; + ErrorObject.set("ErrorCode",404); + ErrorObject.set("ErrorDetails",Request.getMethod()); + ErrorObject.set("ErrorDescription","This resource does not exist."); + std::ostream &Answer = Response.send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); } void RESTAPIHandler::OK(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { PrepareResponse(Request, Response); - Response.send(); + if( Request.getMethod()==Poco::Net::HTTPRequest::HTTP_DELETE || + Request.getMethod()==Poco::Net::HTTPRequest::HTTP_OPTIONS) { + Response.send(); + } else { + Poco::JSON::Object ErrorObject; + ErrorObject.set("Code", 0); + ErrorObject.set("Operation", Request.getMethod()); + ErrorObject.set("Details", "Command completed."); + std::ostream &Answer = Response.send(); + Poco::JSON::Stringifier::stringify(ErrorObject, Answer); + } } void RESTAPIHandler::SendFile(Poco::File & File, const std::string & UUID, Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) { diff --git a/src/RESTAPI_handler.h b/src/RESTAPI_handler.h index c60e054..a85d5f0 100644 --- a/src/RESTAPI_handler.h +++ b/src/RESTAPI_handler.h @@ -62,7 +62,7 @@ namespace uCentral { std::string GetParameter(const std::string &Name, const std::string &Default); bool GetBoolParameter(const std::string &Name, bool Default); - void BadRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response); + void BadRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response, const std::string &Reason = ""); void UnAuthorized(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response); void ReturnObject(Poco::Net::HTTPServerRequest &Request, Poco::JSON::Object &Object, diff --git a/src/RESTAPI_utils.h b/src/RESTAPI_utils.h index 1578ac7..752a04f 100644 --- a/src/RESTAPI_utils.h +++ b/src/RESTAPI_utils.h @@ -4,10 +4,14 @@ #ifndef UCENTRALGW_RESTAPI_UTILS_H #define UCENTRALGW_RESTAPI_UTILS_H +#include #include "Poco/JSON/Object.h" #include "Poco/JSON/Parser.h" +#include "Poco/Net/HTTPServerRequest.h" + #include "uCentralTypes.h" +#include "Utils.h" namespace uCentral::RESTAPI_utils { @@ -36,6 +40,20 @@ namespace uCentral::RESTAPI_utils { Obj.set(Field,A); } + template void field_to_json(Poco::JSON::Object &Obj, + const char *Field, + const T &V, + std::function F) { + Obj.set(Field, F(V)); + } + + template bool field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, T & V, + std::function F) { + if(Obj->has(Field)) + V = F(Obj->get(Field).toString()); + return true; + } + inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, std::string &S) { if(Obj->has(Field)) S = Obj->get(Field).toString(); @@ -89,6 +107,99 @@ namespace uCentral::RESTAPI_utils { Value.from_json(A); } } + + inline std::string to_string(const Types::StringVec & ObjectArray) { + Poco::JSON::Array OutputArr; + if(ObjectArray.empty()) + return "[]"; + for(auto const &i:ObjectArray) { + OutputArr.add(i); + } + std::ostringstream OS; + Poco::JSON::Stringifier::condense(OutputArr,OS); + return OS.str(); + } + + template std::string to_string(const std::vector & ObjectArray) { + Poco::JSON::Array OutputArr; + if(ObjectArray.empty()) + return "[]"; + for(auto const &i:ObjectArray) { + Poco::JSON::Object O; + i.to_json(O); + OutputArr.add(O); + } + std::ostringstream OS; + Poco::JSON::Stringifier::condense(OutputArr,OS); + return OS.str(); + } + + template std::string to_string(const T & Object) { + Poco::JSON::Object OutputObj; + Object.to_json(OutputObj); + std::ostringstream OS; + Poco::JSON::Stringifier::condense(OutputObj,OS); + return OS.str(); + } + + inline Types::StringVec to_object_array(const std::string & ObjectString) { + + Types::StringVec Result; + if(ObjectString.empty()) + return Result; + + try { + Poco::JSON::Parser P; + auto Object = P.parse(ObjectString).template extract(); + for (auto const i : *Object) { + Result.push_back(i.toString()); + } + } catch (...) { + + } + return Result; + } + + template std::vector to_object_array(const std::string & ObjectString) { + + std::vector Result; + if(ObjectString.empty()) + return Result; + + try { + Poco::JSON::Parser P; + auto Object = P.parse(ObjectString).template extract(); + for (auto const i : *Object) { + auto InnerObject = i.template extract(); + T Obj; + Obj.from_json(InnerObject); + Result.push_back(Obj); + } + } catch (...) { + + } + return Result; + } + + template T to_object(const std::string & ObjectString) { + T Result; + + if(ObjectString.empty()) + return Result; + + Poco::JSON::Parser P; + auto Object = P.parse(ObjectString).template extract(); + Result.from_json(Object); + + return Result; + } + + template bool from_request(T & Obj, Poco::Net::HTTPServerRequest &Request) { + Poco::JSON::Parser IncomingParser; + auto RawObject = IncomingParser.parse(Request.stream()).extract(); + Obj.from_json(RawObject); + return true; + } } #endif // UCENTRALGW_RESTAPI_UTILS_H diff --git a/src/SubSystemServer.cpp b/src/SubSystemServer.cpp index 99f72b4..1c47522 100644 --- a/src/SubSystemServer.cpp +++ b/src/SubSystemServer.cpp @@ -12,6 +12,8 @@ #include "Poco/Net/X509Certificate.h" #include "Poco/DateTimeFormatter.h" #include "Poco/DateTimeFormat.h" +#include "Poco/Net/PrivateKeyPassphraseHandler.h" +#include "Poco/Net/SSLManager.h" #include "openssl/ssl.h" @@ -87,6 +89,21 @@ void SubSystemServer::reinitialize(Poco::Util::Application &self) { void SubSystemServer::defineOptions(Poco::Util::OptionSet &options) {} +class MyPrivateKeyPassphraseHandler : public Poco::Net::PrivateKeyPassphraseHandler { + public: + explicit MyPrivateKeyPassphraseHandler(const std::string &Password, Poco::Logger & Logger): + PrivateKeyPassphraseHandler(true), + Logger_(Logger), + Password_(Password) {} + void onPrivateKeyRequested(const void * pSender,std::string & privateKey) { + Logger_.information("Returning key passphrase."); + privateKey = Password_; + }; + private: + std::string Password_; + Poco::Logger & Logger_; +}; + Poco::Net::SecureServerSocket PropertiesFileServerEntry::CreateSecureSocket(Poco::Logger &L) const { Poco::Net::Context::Params P; @@ -99,6 +116,11 @@ Poco::Net::SecureServerSocket PropertiesFileServerEntry::CreateSecureSocket(Poco auto Context = new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P); + if(!key_file_password_.empty()) { + auto PassphraseHandler = Poco::SharedPtr( new MyPrivateKeyPassphraseHandler(KeyFilePassword(),L)); + Poco::Net::SSLManager::instance().initializeServer(PassphraseHandler, nullptr,Context); + } + if (!cert_file_.empty() && !key_file_.empty()) { Poco::Crypto::X509Certificate Cert(cert_file_); Poco::Crypto::X509Certificate Root(root_ca_); diff --git a/stop.sh b/stop.sh new file mode 100755 index 0000000..34a64a7 --- /dev/null +++ b/stop.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +pidfilename="${UCENTRALFMS_ROOT}/data/pidfile" + +if [[ -f "${pidfilename}" ]] +then + kill -9 $(cat ${pidfilename}) +fi diff --git a/ucentralfms.properties.aws b/ucentralfms.properties.aws new file mode 100644 index 0000000..76bc54e --- /dev/null +++ b/ucentralfms.properties.aws @@ -0,0 +1,141 @@ +# +# uCentral protocol server for devices. This is where you point +# all your devices. You can replace the * for address by the specific +# address of one of your interfaces +# +# +# REST API access +# +ucentralfws.restapi.host.0.backlog = 100 +ucentralfws.restapi.host.0.security = relaxed +ucentralfws.restapi.host.0.rootca = $UCENTRALFMS_ROOT/certs/restapi-ca.pem +ucentralfws.restapi.host.0.address = * +ucentralfws.restapi.host.0.port = 16004 +ucentralfws.restapi.host.0.cert = $UCENTRALFMS_ROOT/certs/restapi-cert.pem +ucentralfws.restapi.host.0.key = $UCENTRALFMS_ROOT/certs/restapi-key.pem +ucentralfws.restapi.host.0.key.password = mypassword + +ucentral.internal.restapi.host.0.backlog = 100 +ucentral.internal.restapi.host.0.security = relaxed +ucentral.internal.restapi.host.0.rootca = $UCENTRALFMS_ROOT/certs/restapi-ca.pem +ucentral.internal.restapi.host.0.address = * +ucentral.internal.restapi.host.0.port = 17004 +ucentral.internal.restapi.host.0.cert = $UCENTRALFMS_ROOT/certs/restapi-cert.pem +ucentral.internal.restapi.host.0.key = $UCENTRALFMS_ROOT/certs/restapi-key.pem +ucentral.internal.restapi.host.0.key.password = mypassword + +# +# Generic section that all microservices must have +# +ucentral.service.key = $UCENTRALFMS_ROOT/certs/restapi-key.pem +ucentral.system.data = $UCENTRALFMS_ROOT/data +ucentral.system.debug = false +ucentral.system.uri.private = https://localhost:17004 +ucentral.system.uri.public = https://ucentral.dpaas.arilia.com:16004 +# ucentral.system.uri.public = https://local.dpaas.arilia.com:16004 +ucentral.system.commandchannel = /tmp/app.ucentralfms + +# +# Firmware Microservice Specific Section +# +s3.bucketname = ucentral-ap-firmware +s3.region = us-east-1 +s3.secret = ****************************************** +s3.key = *************************** +s3.retry = 60 +s3.bucket.uri = ucentral-ap-firmware.s3.amazonaws.com +manifestnotification.key = 61a50cb02a1fa4af5e89e8d556629e91bc2274b7e8bb8eaf4339e8c18d5931d4 + +############################# +# Generic information for all micro services +############################# +# +# NLB Support +# +alb.enable = true +alb.port = 16104 + +# +# Kafka +# +ucentral.kafka.group.id = firmware +ucentral.kafka.client.id = firmware1 +ucentral.kafka.enable = true +ucentral.kafka.brokerlist = a1.arilia.com:9092 +# ucentral.kafka.brokerlist = debfarm1-node-c.arilia.com:9092 +ucentral.kafka.auto.commit = false +ucentral.kafka.queue.buffering.max.ms = 50 + +# +# This section select which form of persistence you need +# Only one selected at a time. If you select multiple, this service will die if a horrible +# death and might make your beer flat. +# +storage.type = sqlite +#storage.type = postgresql +#storage.type = mysql +#storage.type = odbc + +storage.type.sqlite.db = firmware.db +storage.type.sqlite.idletime = 120 +storage.type.sqlite.maxsessions = 128 + +storage.type.postgresql.maxsessions = 64 +storage.type.postgresql.idletime = 60 +storage.type.postgresql.host = localhost +storage.type.postgresql.username = stephb +storage.type.postgresql.password = snoopy99 +storage.type.postgresql.database = ucentral +storage.type.postgresql.port = 5432 +storage.type.postgresql.connectiontimeout = 60 + +storage.type.mysql.maxsessions = 64 +storage.type.mysql.idletime = 60 +storage.type.mysql.host = localhost +storage.type.mysql.username = stephb +storage.type.mysql.password = snoopy99 +storage.type.mysql.database = ucentral +storage.type.mysql.port = 3306 +storage.type.mysql.connectiontimeout = 60 + + +######################################################################## +######################################################################## +# +# Logging: please leave as is for now. +# +######################################################################## +logging.formatters.f1.class = PatternFormatter +logging.formatters.f1.pattern = %s: [%p] %t +logging.formatters.f1.times = UTC +logging.channels.c1.class = ConsoleChannel +logging.channels.c1.formatter = f1 + +# This is where the logs will be written. This path MUST exist +logging.channels.c2.class = FileChannel +logging.channels.c2.path = $UCENTRALFMS_ROOT/logs/log +logging.channels.c2.formatter.class = PatternFormatter +logging.channels.c2.formatter.pattern = %Y-%m-%d %H:%M:%S %s: [%p] %t +logging.channels.c2.rotation = 20 M +logging.channels.c2.archive = timestamp +logging.channels.c2.purgeCount = 20 +logging.channels.c3.class = ConsoleChannel +logging.channels.c3.pattern = %s: [%p] %t + +# External Channel +logging.loggers.root.channel = c2 +logging.loggers.root.level = debug + +# Inline Channel with PatternFormatter +# logging.loggers.l1.name = logger1 +# logging.loggers.l1.channel.class = ConsoleChannel +# logging.loggers.l1.channel.pattern = %s: [%p] %t +# logging.loggers.l1.level = information +# SplitterChannel +# logging.channels.splitter.class = SplitterChannel +# logging.channels.splitter.channels = l1,l2 +# logging.loggers.l2.name = logger2 +# logging.loggers.l2.channel = splitter + + + diff --git a/ucentralfms.properties.priv b/ucentralfms.properties.priv new file mode 100644 index 0000000..13b51d4 --- /dev/null +++ b/ucentralfms.properties.priv @@ -0,0 +1,140 @@ +# +# uCentral protocol server for devices. This is where you point +# all your devices. You can replace the * for address by the specific +# address of one of your interfaces +# +# +# REST API access +# +ucentralfws.restapi.host.0.backlog = 100 +ucentralfws.restapi.host.0.security = relaxed +ucentralfws.restapi.host.0.rootca = $UCENTRALFMS_ROOT/certs/restapi-ca.pem +ucentralfws.restapi.host.0.address = * +ucentralfws.restapi.host.0.port = 16004 +ucentralfws.restapi.host.0.cert = $UCENTRALFMS_ROOT/certs/restapi-cert.pem +ucentralfws.restapi.host.0.key = $UCENTRALFMS_ROOT/certs/restapi-key.pem +ucentralfws.restapi.host.0.key.password = mypassword + +ucentral.internal.restapi.host.0.backlog = 100 +ucentral.internal.restapi.host.0.security = relaxed +ucentral.internal.restapi.host.0.rootca = $UCENTRALFMS_ROOT/certs/restapi-ca.pem +ucentral.internal.restapi.host.0.address = * +ucentral.internal.restapi.host.0.port = 17004 +ucentral.internal.restapi.host.0.cert = $UCENTRALFMS_ROOT/certs/restapi-cert.pem +ucentral.internal.restapi.host.0.key = $UCENTRALFMS_ROOT/certs/restapi-key.pem +ucentral.internal.restapi.host.0.key.password = mypassword + +# +# Generic section that all microservices must have +# +ucentral.service.key = $UCENTRALFMS_ROOT/certs/restapi-key.pem +ucentral.system.data = $UCENTRALFMS_ROOT/data +ucentral.system.debug = false +ucentral.system.uri.private = https://localhost:17004 +ucentral.system.uri.public = https://local.dpaas.arilia.com:16004 +ucentral.system.commandchannel = /tmp/app.ucentralfms + +# +# Firmware Microservice Specific Section +# +s3.bucketname = ucentral-ap-firmware +s3.region = us-east-1 +s3.secret = ****************************************** +s3.key = *************************** +s3.retry = 60 +s3.bucket.uri = ucentral-ap-firmware.s3.amazonaws.com +manifestnotification.key = 61a50cb02a1fa4af5e89e8d556629e91bc2274b7e8bb8eaf4339e8c18d5931d4 + +############################# +# Generic information for all micro services +############################# +# +# NLB Support +# +alb.enable = true +alb.port = 16104 + +# +# Kafka +# +ucentral.kafka.group.id = firmware +ucentral.kafka.client.id = firmware1 +ucentral.kafka.enable = true +# ucentral.kafka.brokerlist = a1.arilia.com:9092 +ucentral.kafka.brokerlist = debfarm1-node-c.arilia.com:9092 +ucentral.kafka.auto.commit = false +ucentral.kafka.queue.buffering.max.ms = 50 + +# +# This section select which form of persistence you need +# Only one selected at a time. If you select multiple, this service will die if a horrible +# death and might make your beer flat. +# +storage.type = sqlite +#storage.type = postgresql +#storage.type = mysql +#storage.type = odbc + +storage.type.sqlite.db = firmware.db +storage.type.sqlite.idletime = 120 +storage.type.sqlite.maxsessions = 128 + +storage.type.postgresql.maxsessions = 64 +storage.type.postgresql.idletime = 60 +storage.type.postgresql.host = localhost +storage.type.postgresql.username = stephb +storage.type.postgresql.password = snoopy99 +storage.type.postgresql.database = ucentral +storage.type.postgresql.port = 5432 +storage.type.postgresql.connectiontimeout = 60 + +storage.type.mysql.maxsessions = 64 +storage.type.mysql.idletime = 60 +storage.type.mysql.host = localhost +storage.type.mysql.username = stephb +storage.type.mysql.password = snoopy99 +storage.type.mysql.database = ucentral +storage.type.mysql.port = 3306 +storage.type.mysql.connectiontimeout = 60 + + +######################################################################## +######################################################################## +# +# Logging: please leave as is for now. +# +######################################################################## +logging.formatters.f1.class = PatternFormatter +logging.formatters.f1.pattern = %s: [%p] %t +logging.formatters.f1.times = UTC +logging.channels.c1.class = ConsoleChannel +logging.channels.c1.formatter = f1 + +# This is where the logs will be written. This path MUST exist +logging.channels.c2.class = FileChannel +logging.channels.c2.path = $UCENTRALFMS_ROOT/logs/log +logging.channels.c2.formatter.class = PatternFormatter +logging.channels.c2.formatter.pattern = %Y-%m-%d %H:%M:%S %s: [%p] %t +logging.channels.c2.rotation = 20 M +logging.channels.c2.archive = timestamp +logging.channels.c2.purgeCount = 20 +logging.channels.c3.class = ConsoleChannel +logging.channels.c3.pattern = %s: [%p] %t + +# External Channel +logging.loggers.root.channel = c2 +logging.loggers.root.level = debug + +# Inline Channel with PatternFormatter +# logging.loggers.l1.name = logger1 +# logging.loggers.l1.channel.class = ConsoleChannel +# logging.loggers.l1.channel.pattern = %s: [%p] %t +# logging.loggers.l1.level = information +# SplitterChannel +# logging.channels.splitter.class = SplitterChannel +# logging.channels.splitter.channels = l1,l2 +# logging.loggers.l2.name = logger2 +# logging.loggers.l2.channel = splitter + + +