From 9a08234dd6eedb1588479a1d80a66b18ed54951c Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Wed, 2 Oct 2019 13:25:20 +0200 Subject: [PATCH 01/32] Updated unit tests with logger * Increased Boost version to 1.67 as it compiles without any error * Fixed sign missmatch in tests Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index cf411fe..ed00aac 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -61,7 +61,7 @@ set(Boost_USE_STATIC_LIBS ON) include_directories(${Boost_INCLUDE_DIRS}) message(STATUS " boost includes ${Boost_INCLUDE_DIRS} ") -find_package(Boost 1.64.0 COMPONENTS system thread program_options REQUIRED) +find_package(Boost 1.67.0 COMPONENTS system thread program_options REQUIRED) target_link_libraries(simple-websocket-server INTERFACE ${Boost_LIBRARIES}) target_include_directories(simple-websocket-server INTERFACE ${Boost_INCLUDE_DIR}) From 07e8f38bb4ac2c4024f1e7f425b3fc2e10c6117b Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 10:52:49 +0200 Subject: [PATCH 02/32] Renamed WsServer for later refactor Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 2 +- .../include/{wsserver.hpp => WsServer.hpp} | 8 +-- .../include/subscriptionhandler.hpp | 8 +-- .../src/{wsserver.cpp => WsServer.cpp} | 67 +++++++++---------- w3c-visserver-api/src/main.cpp | 5 +- w3c-visserver-api/src/subscriptionhandler.cpp | 6 +- w3c-visserver-api/unit-test/w3cunittest.cpp | 6 +- w3c-visserver-api/unit-test/w3cunittest.hpp | 2 +- 8 files changed, 52 insertions(+), 52 deletions(-) rename w3c-visserver-api/include/{wsserver.hpp => WsServer.hpp} (91%) rename w3c-visserver-api/src/{wsserver.cpp => WsServer.cpp} (77%) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index ed00aac..37fc328 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -113,7 +113,7 @@ if(UNIT_TEST) ${CMAKE_CURRENT_SOURCE_DIR}/src/authenticator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/subscriptionhandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/signing.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/wsserver.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/WsServer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/BasicLogger.cpp # ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/vssdatabase_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/w3cunittest.cpp diff --git a/w3c-visserver-api/include/wsserver.hpp b/w3c-visserver-api/include/WsServer.hpp similarity index 91% rename from w3c-visserver-api/include/wsserver.hpp rename to w3c-visserver-api/include/WsServer.hpp index 5ac5a40..6653765 100644 --- a/w3c-visserver-api/include/wsserver.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -26,7 +26,7 @@ class vssdatabase; class accesschecker; class ILogger; -class wsserver { +class WsServer { private: SimpleWeb::SocketServer *secureServer_; SimpleWeb::SocketServer *insecureServer_; @@ -41,10 +41,10 @@ class wsserver { vssdatabase* database; accesschecker* accessCheck; - wsserver(std::shared_ptr loggerUtil, int port, std::string configFileName, bool secure); - ~wsserver(); + WsServer(std::shared_ptr loggerUtil, int port, std::string configFileName, bool secure); + ~WsServer(); void startServer(std::string endpointName); void sendToConnection(uint32_t connID, std::string message); - vssdatabase* start(); + void start(); }; #endif diff --git a/w3c-visserver-api/include/subscriptionhandler.hpp b/w3c-visserver-api/include/subscriptionhandler.hpp index d7b7d67..eb0aebf 100644 --- a/w3c-visserver-api/include/subscriptionhandler.hpp +++ b/w3c-visserver-api/include/subscriptionhandler.hpp @@ -28,7 +28,7 @@ class accesschecker; class authenticator; class vssdatabase; class wschannel; -class wsserver; +class WsServer; class ILogger; // Subscription ID: Client ID @@ -41,7 +41,7 @@ class subscriptionhandler { private: std::shared_ptr logger; std::unordered_map subscribeHandle; - wsserver* server; + WsServer* server; authenticator* validator; accesschecker* checkAccess; std::mutex subMutex; @@ -51,7 +51,7 @@ class subscriptionhandler { public: subscriptionhandler(std::shared_ptr loggerUtil, - wsserver* wserver, + WsServer* wserver, authenticator* authenticate, accesschecker* checkAccess); ~subscriptionhandler(); @@ -62,7 +62,7 @@ class subscriptionhandler { int unsubscribeAll(uint32_t connectionID); int updateByUUID(std::string signalUUID, jsoncons::json value); int updateByPath(std::string path, jsoncons::json value); - wsserver* getServer(); + WsServer* getServer(); int startThread(); int stopThread(); bool isThreadRunning(); diff --git a/w3c-visserver-api/src/wsserver.cpp b/w3c-visserver-api/src/WsServer.cpp similarity index 77% rename from w3c-visserver-api/src/wsserver.cpp rename to w3c-visserver-api/src/WsServer.cpp index 23c8118..2b06125 100644 --- a/w3c-visserver-api/src/wsserver.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -11,7 +11,7 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#include "wsserver.hpp" +#include "WsServer.hpp" #include "ILogger.hpp" #include "accesschecker.hpp" @@ -25,11 +25,11 @@ using namespace std; -using WssServer = SimpleWeb::SocketServer; -using WsServer = SimpleWeb::SocketServer; +using SecuredServer = SimpleWeb::SocketServer; +using SimpleServer = SimpleWeb::SocketServer; uint16_t connections[MAX_CLIENTS + 1] = {0}; -wsserver *wserver; +WsServer *wserver; uint32_t generateConnID() { uint32_t retValueValue = 0; @@ -43,7 +43,7 @@ uint32_t generateConnID() { return retValueValue; } -wsserver::wsserver(std::shared_ptr loggerUtil, int port, string configFileName, bool secure) { +WsServer::WsServer(std::shared_ptr loggerUtil, int port, string configFileName, bool secure) { logger = loggerUtil; isSecure_ = secure; secureServer_ = nullptr; @@ -51,10 +51,10 @@ wsserver::wsserver(std::shared_ptr loggerUtil, int port, string configF configFileName_ = configFileName; if (isSecure_) { - secureServer_ = new WssServer("Server.pem", "Server.key"); + secureServer_ = new SecuredServer("Server.pem", "Server.key"); secureServer_->config.port = port; } else { - insecureServer_ = new WsServer(); + insecureServer_ = new SimpleServer(); insecureServer_->config.port = port; } @@ -66,7 +66,7 @@ wsserver::wsserver(std::shared_ptr loggerUtil, int port, string configF wserver = this; } -wsserver::~wsserver() { +WsServer::~WsServer() { delete cmdProcessor; delete database; delete subHandler; @@ -81,7 +81,7 @@ wsserver::~wsserver() { } static void onMessage(std::weak_ptr wLogger, - shared_ptr connection, + shared_ptr connection, string message) { auto logger = wLogger.lock(); if (logger) { @@ -92,13 +92,13 @@ static void onMessage(std::weak_ptr wLogger, string response = wserver->cmdProcessor->processQuery(message, connection->channel); - auto send_stream = make_shared(); + auto send_stream = make_shared(); *send_stream << response; connection->send(send_stream); } static void onMessage(std::weak_ptr wLogger, - shared_ptr connection, + shared_ptr connection, string message) { auto logger = wLogger.lock(); if (logger) { @@ -109,12 +109,12 @@ static void onMessage(std::weak_ptr wLogger, string response = wserver->cmdProcessor->processQuery(message, connection->channel); - auto send_stream = make_shared(); + auto send_stream = make_shared(); *send_stream << response; connection->send(send_stream); } -void wsserver::startServer(string endpointName) { +void WsServer::startServer(string endpointName) { (void) endpointName; auto wLogger = std::weak_ptr(logger); @@ -122,19 +122,19 @@ void wsserver::startServer(string endpointName) { if (isSecure_) { auto &vssEndpoint = secureServer_->endpoint["^/vss/?$"]; - vssEndpoint.on_message = [wLogger](shared_ptr connection, - shared_ptr message) { + vssEndpoint.on_message = [wLogger](shared_ptr connection, + shared_ptr message) { auto message_str = message->string(); onMessage(wLogger, connection, message_str); }; - vssEndpoint.on_open = [wLogger](shared_ptr connection) { + vssEndpoint.on_open = [wLogger](shared_ptr connection) { connection->channel.setConnID(generateConnID()); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::INFO, std::string("wsserver: Opened connection " + logger->Log(LogLevel::INFO, std::string("WsServer: Opened connection " + connection->remote_endpoint_address() + "conn ID " + to_string(connection->channel.getConnID()))); @@ -142,7 +142,7 @@ void wsserver::startServer(string endpointName) { }; // See RFC 6455 7.4.1. for status codes - vssEndpoint.on_close = [wLogger](shared_ptr connection, + vssEndpoint.on_close = [wLogger](shared_ptr connection, int status, const string & /*reason*/) { uint32_t clientID = connection->channel.getConnID() / CLIENT_MASK; connections[clientID] = 0; @@ -150,7 +150,7 @@ void wsserver::startServer(string endpointName) { auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::INFO, "wsserver: Closed connection " + logger->Log(LogLevel::INFO, "WsServer: Closed connection " + connection->remote_endpoint_address() + " with status code " + to_string(status)); @@ -160,14 +160,14 @@ void wsserver::startServer(string endpointName) { // See // http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, // Error Codes for error code meanings - vssEndpoint.on_error = [wLogger](shared_ptr connection, + vssEndpoint.on_error = [wLogger](shared_ptr connection, const SimpleWeb::error_code &ec) { uint32_t clientID = connection->channel.getConnID() / CLIENT_MASK; connections[clientID] = 0; // removeAllSubscriptions(clientID); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::ERROR, "wsserver: Connection " + logger->Log(LogLevel::ERROR, "WsServer: Connection " + connection->remote_endpoint_address() + " with con ID " + to_string(connection->channel.getConnID()) + ". " + "Error: " + to_string(ec.value()) + ", error message: " + ec.message()); @@ -179,33 +179,33 @@ void wsserver::startServer(string endpointName) { } else { auto &vssEndpoint = insecureServer_->endpoint["^/vss/?$"]; - vssEndpoint.on_message = [wLogger](shared_ptr connection, - shared_ptr message) { + vssEndpoint.on_message = [wLogger](shared_ptr connection, + shared_ptr message) { auto message_str = message->string(); onMessage(wLogger, connection, message_str); }; - vssEndpoint.on_open = [wLogger](shared_ptr connection) { + vssEndpoint.on_open = [wLogger](shared_ptr connection) { connection->channel.setConnID(generateConnID()); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::VERBOSE, "wsserver: Opened connection " + logger->Log(LogLevel::VERBOSE, "WsServer: Opened connection " + connection->remote_endpoint_address() + "conn ID " + to_string(connection->channel.getConnID())); } }; // See RFC 6455 7.4.1. for status codes - vssEndpoint.on_close = [wLogger](shared_ptr connection, + vssEndpoint.on_close = [wLogger](shared_ptr connection, int status, const string & /*reason*/) { uint32_t clientID = connection->channel.getConnID() / CLIENT_MASK; connections[clientID] = 0; // removeAllSubscriptions(clientID); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::INFO, "wsserver: Closed connection " + logger->Log(LogLevel::INFO, "WsServer: Closed connection " + connection->remote_endpoint_address() + " with status code " + to_string(status)); } @@ -214,14 +214,14 @@ void wsserver::startServer(string endpointName) { // See // http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, // Error Codes for error code meanings - vssEndpoint.on_error = [wLogger](shared_ptr connection, + vssEndpoint.on_error = [wLogger](shared_ptr connection, const SimpleWeb::error_code &ec) { uint32_t clientID = connection->channel.getConnID() / CLIENT_MASK; connections[clientID] = 0; // removeAllSubscriptions(clientID); auto logger = wLogger.lock(); if (logger) { - logger->Log(LogLevel::ERROR, "wsserver: Connection " + logger->Log(LogLevel::ERROR, "WsServer: Connection " + connection->remote_endpoint_address() + " with con ID " + to_string(connection->channel.getConnID()) + ". " + "Error: " + to_string(ec.value()) + ", error message: " + ec.message()); @@ -233,9 +233,9 @@ void wsserver::startServer(string endpointName) { } } -void wsserver::sendToConnection(uint32_t connectionID, string message) { +void WsServer::sendToConnection(uint32_t connectionID, string message) { if (isSecure_) { - auto send_stream = make_shared(); + auto send_stream = make_shared(); *send_stream << message; for (auto &a_connection : secureServer_->get_connections()) { if (a_connection->channel.getConnID() == connectionID) { @@ -244,7 +244,7 @@ void wsserver::sendToConnection(uint32_t connectionID, string message) { } } } else { - auto send_stream = make_shared(); + auto send_stream = make_shared(); *send_stream << message; for (auto &a_connection : insecureServer_->get_connections()) { if (a_connection->channel.getConnID() == connectionID) { @@ -264,7 +264,7 @@ void *startWSServer(void *arg) { return NULL; } -vssdatabase* wsserver::start() { +void WsServer::start() { this->database->initJsonTree(configFileName_); pthread_t startWSServer_thread; @@ -272,5 +272,4 @@ vssdatabase* wsserver::start() { if (pthread_create(&startWSServer_thread, NULL, &startWSServer, NULL)) { logger->Log(LogLevel::ERROR, "main: Error creating websocket server run thread"); } - return this->database; } diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index 1023398..ae2fce5 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -15,11 +15,12 @@ #include #include #include + #include #include #include -#include "wsserver.hpp" +#include "WsServer.hpp" #include "vssdatabase.hpp" #include "exception.hpp" @@ -282,7 +283,7 @@ int main(int argc, const char *argv[]) { #endif std::shared_ptr logger = std::make_shared(logLevelsActive); - wsserver server(logger, port, vss_filename, secure); + WsServer server(logger, port, vss_filename, secure); server.start(); } catch (const program_options::error &ex) { diff --git a/w3c-visserver-api/src/subscriptionhandler.cpp b/w3c-visserver-api/src/subscriptionhandler.cpp index c5d57e1..2d6e9cb 100644 --- a/w3c-visserver-api/src/subscriptionhandler.cpp +++ b/w3c-visserver-api/src/subscriptionhandler.cpp @@ -23,7 +23,7 @@ #include "exception.hpp" #include "visconf.hpp" #include "vssdatabase.hpp" -#include "wsserver.hpp" +#include "WsServer.hpp" #include "ILogger.hpp" using namespace std; @@ -32,7 +32,7 @@ using namespace jsoncons::jsonpath; // using jsoncons::jsoncons::jsoncons::json; subscriptionhandler::subscriptionhandler(std::shared_ptr loggerUtil, - wsserver* wserver, + WsServer* wserver, authenticator* authenticate, accesschecker* checkAcc) { logger = loggerUtil; @@ -139,7 +139,7 @@ int subscriptionhandler::updateByUUID(string UUID, jsoncons::json value) { return 0; } -wsserver* subscriptionhandler::getServer() { +WsServer* subscriptionhandler::getServer() { return server; } diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index 4db3c76..5383132 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -28,7 +28,7 @@ #include "subscriptionhandler.hpp" #include "vssdatabase.hpp" #include "vsscommandprocessor.hpp" -#include "wsserver.hpp" +#include "WsServer.hpp" #include "ILogger.hpp" #include "BasicLogger.hpp" @@ -62,7 +62,7 @@ namespace bt = boost::unit_test; #define PORT 8090 std::shared_ptr logger; -wsserver* webSocket; +WsServer* webSocket; subscriptionhandler* subhandler; authenticator* authhandler; accesschecker* accesshandler; @@ -74,7 +74,7 @@ w3cunittest unittestObj(false); w3cunittest::w3cunittest(bool secure) { logger = std::make_shared(static_cast(LogLevel::ALL)); - webSocket = new wsserver(logger, PORT, "vss_rel_2.0.json", secure); + webSocket = new WsServer(logger, PORT, "vss_rel_2.0.json", secure); authhandler = new authenticator(logger, "",""); accesshandler = new accesschecker(authhandler); subhandler = new subscriptionhandler(logger, webSocket, authhandler, accesshandler); diff --git a/w3c-visserver-api/unit-test/w3cunittest.hpp b/w3c-visserver-api/unit-test/w3cunittest.hpp index 6241972..74e672a 100644 --- a/w3c-visserver-api/unit-test/w3cunittest.hpp +++ b/w3c-visserver-api/unit-test/w3cunittest.hpp @@ -20,7 +20,7 @@ #include // #include "vssdatabase.hpp" -// #include "wsserver.hpp" +// #include "WsServer.hpp" // #include "subscriptionhandler.hpp" // #include "authenticator.hpp" // #include "accesschecker.hpp" From 8bc961ed82f031c27cd712f93cbd515de92d5250 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 11:27:51 +0200 Subject: [PATCH 03/32] Renamed Authenticator for later refactor Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 2 +- .../{authenticator.hpp => Authenticator.hpp} | 4 ++-- w3c-visserver-api/include/WsServer.hpp | 4 ++-- w3c-visserver-api/include/accesschecker.hpp | 6 +++--- .../include/subscriptionhandler.hpp | 6 +++--- .../include/vsscommandprocessor.hpp | 6 +++--- w3c-visserver-api/include/vssdatabase.hpp | 2 +- .../src/{authenticator.cpp => Authenticator.cpp} | 16 ++++++++-------- w3c-visserver-api/src/WsServer.cpp | 4 ++-- w3c-visserver-api/src/accesschecker.cpp | 2 +- w3c-visserver-api/src/subscriptionhandler.cpp | 4 ++-- w3c-visserver-api/src/vsscommandprocessor.cpp | 2 +- w3c-visserver-api/unit-test/w3cunittest.cpp | 6 +++--- w3c-visserver-api/unit-test/w3cunittest.hpp | 2 +- 14 files changed, 33 insertions(+), 33 deletions(-) rename w3c-visserver-api/include/{authenticator.hpp => Authenticator.hpp} (93%) rename w3c-visserver-api/src/{authenticator.cpp => Authenticator.cpp} (89%) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index 37fc328..51f2b5d 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -110,7 +110,7 @@ if(UNIT_TEST) ${CMAKE_CURRENT_SOURCE_DIR}/src/vssdatabase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/vsscommandprocessor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/accesschecker.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/authenticator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Authenticator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/subscriptionhandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/signing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/WsServer.cpp diff --git a/w3c-visserver-api/include/authenticator.hpp b/w3c-visserver-api/include/Authenticator.hpp similarity index 93% rename from w3c-visserver-api/include/authenticator.hpp rename to w3c-visserver-api/include/Authenticator.hpp index 1c3b8b3..53033c0 100644 --- a/w3c-visserver-api/include/authenticator.hpp +++ b/w3c-visserver-api/include/Authenticator.hpp @@ -23,7 +23,7 @@ class wschannel; class vssdatabase; class ILogger; -class authenticator { +class Authenticator { private: string pubkey = "secret"; string algorithm = "RS256"; @@ -32,7 +32,7 @@ class authenticator { int validateToken(wschannel& channel, string authToken); public: - authenticator(std::shared_ptr loggerUtil, string secretkey, string algorithm); + Authenticator(std::shared_ptr loggerUtil, string secretkey, string algorithm); int validate(wschannel &channel, vssdatabase *database, string authToken); diff --git a/w3c-visserver-api/include/WsServer.hpp b/w3c-visserver-api/include/WsServer.hpp index 6653765..ac21d1c 100644 --- a/w3c-visserver-api/include/WsServer.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -21,7 +21,7 @@ class vsscommandprocessor; class vsscommandprocessor; class subscriptionhandler; -class authenticator; +class Authenticator; class vssdatabase; class accesschecker; class ILogger; @@ -37,7 +37,7 @@ class WsServer { public: vsscommandprocessor* cmdProcessor; subscriptionhandler* subHandler; - authenticator* tokenValidator; + Authenticator* tokenValidator; vssdatabase* database; accesschecker* accessCheck; diff --git a/w3c-visserver-api/include/accesschecker.hpp b/w3c-visserver-api/include/accesschecker.hpp index c377e75..5cd0b83 100644 --- a/w3c-visserver-api/include/accesschecker.hpp +++ b/w3c-visserver-api/include/accesschecker.hpp @@ -16,7 +16,7 @@ #include #include -#include "authenticator.hpp" +#include "Authenticator.hpp" #include "wschannel.hpp" using namespace std; @@ -25,10 +25,10 @@ using jsoncons::json; class accesschecker { private: - authenticator *tokenValidator; + Authenticator *tokenValidator; public: - accesschecker(authenticator *vdator); + accesschecker(Authenticator *vdator); bool checkReadAccess(wschannel &channel, string path); bool checkWriteAccess(wschannel &channel, string path); bool checkPathWriteAccess(wschannel &channel, json paths); diff --git a/w3c-visserver-api/include/subscriptionhandler.hpp b/w3c-visserver-api/include/subscriptionhandler.hpp index eb0aebf..7448b08 100644 --- a/w3c-visserver-api/include/subscriptionhandler.hpp +++ b/w3c-visserver-api/include/subscriptionhandler.hpp @@ -25,7 +25,7 @@ #include class accesschecker; -class authenticator; +class Authenticator; class vssdatabase; class wschannel; class WsServer; @@ -42,7 +42,7 @@ class subscriptionhandler { std::shared_ptr logger; std::unordered_map subscribeHandle; WsServer* server; - authenticator* validator; + Authenticator* validator; accesschecker* checkAccess; std::mutex subMutex; std::thread subThread; @@ -52,7 +52,7 @@ class subscriptionhandler { public: subscriptionhandler(std::shared_ptr loggerUtil, WsServer* wserver, - authenticator* authenticate, + Authenticator* authenticate, accesschecker* checkAccess); ~subscriptionhandler(); diff --git a/w3c-visserver-api/include/vsscommandprocessor.hpp b/w3c-visserver-api/include/vsscommandprocessor.hpp index fb492eb..cf9e118 100644 --- a/w3c-visserver-api/include/vsscommandprocessor.hpp +++ b/w3c-visserver-api/include/vsscommandprocessor.hpp @@ -21,7 +21,7 @@ class vssdatabase; class subscriptionhandler; -class authenticator; +class Authenticator; class accesschecker; class wschannel; class ILogger; @@ -31,7 +31,7 @@ class vsscommandprocessor { std::shared_ptr logger; vssdatabase* database = NULL; subscriptionhandler* subHandler = NULL; - authenticator* tokenValidator = NULL; + Authenticator* tokenValidator = NULL; accesschecker* accessValidator = NULL; #ifdef JSON_SIGNING_ON signing* signer = NULL; @@ -53,7 +53,7 @@ class vsscommandprocessor { public: vsscommandprocessor(std::shared_ptr loggerUtil, vssdatabase* database, - authenticator* vdator, + Authenticator* vdator, subscriptionhandler* subhandler); ~vsscommandprocessor(); std::string processQuery(std::string req_json, wschannel& channel); diff --git a/w3c-visserver-api/include/vssdatabase.hpp b/w3c-visserver-api/include/vssdatabase.hpp index beb7f34..ca1b1e1 100644 --- a/w3c-visserver-api/include/vssdatabase.hpp +++ b/w3c-visserver-api/include/vssdatabase.hpp @@ -28,7 +28,7 @@ class ILogger; class vssdatabase { friend class subscriptionhandler; - friend class authenticator; + friend class Authenticator; #ifdef UNIT_TEST friend class w3cunittest; #endif diff --git a/w3c-visserver-api/src/authenticator.cpp b/w3c-visserver-api/src/Authenticator.cpp similarity index 89% rename from w3c-visserver-api/src/authenticator.cpp rename to w3c-visserver-api/src/Authenticator.cpp index bdec023..3c92bee 100644 --- a/w3c-visserver-api/src/authenticator.cpp +++ b/w3c-visserver-api/src/Authenticator.cpp @@ -11,7 +11,7 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#include "authenticator.hpp" +#include "Authenticator.hpp" #include "ILogger.hpp" #include #include @@ -39,14 +39,14 @@ namespace { } -void authenticator::updatePubKey(string key) { +void Authenticator::updatePubKey(string key) { pubkey = key; if(pubkey == "") pubkey = getPublicKeyFromFile("jwt.pub.key"); } // utility method to validate token. -int authenticator::validateToken(wschannel& channel, string authToken) { +int Authenticator::validateToken(wschannel& channel, string authToken) { auto decoded = jwt::decode(authToken); json claims; (void) channel; @@ -60,7 +60,7 @@ int authenticator::validateToken(wschannel& channel, string authToken) { try { verifier.verify(decoded); } catch (const std::runtime_error& e) { - logger->Log(LogLevel::ERROR, "authenticator::validate: " + string(e.what()) + logger->Log(LogLevel::ERROR, "Authenticator::validate: " + string(e.what()) + " Exception occured while authentication. Token is not valid!"); return -1; } @@ -71,7 +71,7 @@ int authenticator::validateToken(wschannel& channel, string authToken) { return ttl; } -authenticator::authenticator(std::shared_ptr loggerUtil, string secretkey, string algo) { +Authenticator::Authenticator(std::shared_ptr loggerUtil, string secretkey, string algo) { logger = loggerUtil; algorithm = algo; pubkey = secretkey; @@ -79,7 +79,7 @@ authenticator::authenticator(std::shared_ptr loggerUtil, string secretk // validates the token against expiry date/time. should be extended to check // some other claims. -int authenticator::validate(wschannel& channel, vssdatabase* db, +int Authenticator::validate(wschannel& channel, vssdatabase* db, string authToken) { int ttl = validateToken(channel, authToken); if (ttl > 0) { @@ -92,7 +92,7 @@ int authenticator::validate(wschannel& channel, vssdatabase* db, // Checks if the token is still valid for the requests from the channel(client). // Internally check this before publishing messages for previously subscribed // signals. -bool authenticator::isStillValid(wschannel& channel) { +bool Authenticator::isStillValid(wschannel& channel) { string token = channel.getAuthToken(); int ret = validateToken(channel, token); @@ -107,7 +107,7 @@ bool authenticator::isStillValid(wschannel& channel) { // **Do this only once for authenticate request** // resolves the permission in the JWT token and store the absolute path to the // signals in permissions JSON in wschannel. -void authenticator::resolvePermissions(wschannel& channel, +void Authenticator::resolvePermissions(wschannel& channel, vssdatabase* database) { string authToken = channel.getAuthToken(); auto decoded = jwt::decode(authToken); diff --git a/w3c-visserver-api/src/WsServer.cpp b/w3c-visserver-api/src/WsServer.cpp index 2b06125..cd51884 100644 --- a/w3c-visserver-api/src/WsServer.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -15,7 +15,7 @@ #include "ILogger.hpp" #include "accesschecker.hpp" -#include "authenticator.hpp" +#include "Authenticator.hpp" #include "subscriptionhandler.hpp" #include "visconf.hpp" #include "vsscommandprocessor.hpp" @@ -58,7 +58,7 @@ WsServer::WsServer(std::shared_ptr loggerUtil, int port, string configF insecureServer_->config.port = port; } - tokenValidator = new authenticator(logger, "appstacle", "RS256"); + tokenValidator = new Authenticator(logger, "appstacle", "RS256"); accessCheck = new accesschecker(tokenValidator); subHandler = new subscriptionhandler(logger, this, tokenValidator, accessCheck); database = new vssdatabase(logger, subHandler, accessCheck); diff --git a/w3c-visserver-api/src/accesschecker.cpp b/w3c-visserver-api/src/accesschecker.cpp index 9991e9a..bced2e3 100644 --- a/w3c-visserver-api/src/accesschecker.cpp +++ b/w3c-visserver-api/src/accesschecker.cpp @@ -16,7 +16,7 @@ using namespace std; -accesschecker::accesschecker(authenticator *vdator) { +accesschecker::accesschecker(Authenticator *vdator) { tokenValidator = vdator; } diff --git a/w3c-visserver-api/src/subscriptionhandler.cpp b/w3c-visserver-api/src/subscriptionhandler.cpp index 2d6e9cb..5275b07 100644 --- a/w3c-visserver-api/src/subscriptionhandler.cpp +++ b/w3c-visserver-api/src/subscriptionhandler.cpp @@ -19,7 +19,7 @@ #include #include "accesschecker.hpp" -#include "authenticator.hpp" +#include "Authenticator.hpp" #include "exception.hpp" #include "visconf.hpp" #include "vssdatabase.hpp" @@ -33,7 +33,7 @@ using namespace jsoncons::jsonpath; subscriptionhandler::subscriptionhandler(std::shared_ptr loggerUtil, WsServer* wserver, - authenticator* authenticate, + Authenticator* authenticate, accesschecker* checkAcc) { logger = loggerUtil; server = wserver; diff --git a/w3c-visserver-api/src/vsscommandprocessor.cpp b/w3c-visserver-api/src/vsscommandprocessor.cpp index 529b530..9d31923 100644 --- a/w3c-visserver-api/src/vsscommandprocessor.cpp +++ b/w3c-visserver-api/src/vsscommandprocessor.cpp @@ -115,7 +115,7 @@ string valueOutOfBoundsResponse(uint32_t request_id, const string action, vsscommandprocessor::vsscommandprocessor( std::shared_ptr loggerUtil, vssdatabase *dbase, - authenticator *vdator, + Authenticator *vdator, subscriptionhandler *subhandler) { logger = loggerUtil; database = dbase; diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index 5383132..204f5b7 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -23,7 +23,7 @@ // #include #include "accesschecker.hpp" -#include "authenticator.hpp" +#include "Authenticator.hpp" #include "signing.hpp" #include "subscriptionhandler.hpp" #include "vssdatabase.hpp" @@ -64,7 +64,7 @@ namespace bt = boost::unit_test; std::shared_ptr logger; WsServer* webSocket; subscriptionhandler* subhandler; -authenticator* authhandler; +Authenticator* authhandler; accesschecker* accesshandler; vssdatabase* database; signing* json_signer; @@ -75,7 +75,7 @@ w3cunittest unittestObj(false); w3cunittest::w3cunittest(bool secure) { logger = std::make_shared(static_cast(LogLevel::ALL)); webSocket = new WsServer(logger, PORT, "vss_rel_2.0.json", secure); - authhandler = new authenticator(logger, "",""); + authhandler = new Authenticator(logger, "",""); accesshandler = new accesschecker(authhandler); subhandler = new subscriptionhandler(logger, webSocket, authhandler, accesshandler); database = new vssdatabase(logger, subhandler, accesshandler); diff --git a/w3c-visserver-api/unit-test/w3cunittest.hpp b/w3c-visserver-api/unit-test/w3cunittest.hpp index 74e672a..f855d49 100644 --- a/w3c-visserver-api/unit-test/w3cunittest.hpp +++ b/w3c-visserver-api/unit-test/w3cunittest.hpp @@ -22,7 +22,7 @@ // #include "vssdatabase.hpp" // #include "WsServer.hpp" // #include "subscriptionhandler.hpp" -// #include "authenticator.hpp" +// #include "Authenticator.hpp" // #include "accesschecker.hpp" // #include "signing.hpp" From 2e6d00b3a929ea388bedaf425d9b7cc630157213 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 11:28:47 +0200 Subject: [PATCH 04/32] Renamed AccessChecker for later refactor Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 2 +- .../include/{accesschecker.hpp => AccessChecker.hpp} | 4 ++-- w3c-visserver-api/include/WsServer.hpp | 4 ++-- w3c-visserver-api/include/subscriptionhandler.hpp | 6 +++--- w3c-visserver-api/include/vsscommandprocessor.hpp | 4 ++-- w3c-visserver-api/include/vssdatabase.hpp | 6 +++--- .../src/{accesschecker.cpp => AccessChecker.cpp} | 10 +++++----- w3c-visserver-api/src/WsServer.cpp | 4 ++-- w3c-visserver-api/src/subscriptionhandler.cpp | 4 ++-- w3c-visserver-api/src/vsscommandprocessor.cpp | 4 ++-- w3c-visserver-api/src/vssdatabase.cpp | 4 ++-- w3c-visserver-api/unit-test/w3cunittest.cpp | 6 +++--- w3c-visserver-api/unit-test/w3cunittest.hpp | 2 +- 13 files changed, 30 insertions(+), 30 deletions(-) rename w3c-visserver-api/include/{accesschecker.hpp => AccessChecker.hpp} (94%) rename w3c-visserver-api/src/{accesschecker.cpp => AccessChecker.cpp} (84%) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index 51f2b5d..bd93472 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -109,7 +109,7 @@ if(UNIT_TEST) set(TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/vssdatabase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/vsscommandprocessor.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/accesschecker.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/AccessChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Authenticator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/subscriptionhandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/signing.cpp diff --git a/w3c-visserver-api/include/accesschecker.hpp b/w3c-visserver-api/include/AccessChecker.hpp similarity index 94% rename from w3c-visserver-api/include/accesschecker.hpp rename to w3c-visserver-api/include/AccessChecker.hpp index 5cd0b83..bbe8abe 100644 --- a/w3c-visserver-api/include/accesschecker.hpp +++ b/w3c-visserver-api/include/AccessChecker.hpp @@ -23,12 +23,12 @@ using namespace std; using namespace jsoncons; using jsoncons::json; -class accesschecker { +class AccessChecker { private: Authenticator *tokenValidator; public: - accesschecker(Authenticator *vdator); + AccessChecker(Authenticator *vdator); bool checkReadAccess(wschannel &channel, string path); bool checkWriteAccess(wschannel &channel, string path); bool checkPathWriteAccess(wschannel &channel, json paths); diff --git a/w3c-visserver-api/include/WsServer.hpp b/w3c-visserver-api/include/WsServer.hpp index ac21d1c..e0b8413 100644 --- a/w3c-visserver-api/include/WsServer.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -23,7 +23,7 @@ class vsscommandprocessor; class subscriptionhandler; class Authenticator; class vssdatabase; -class accesschecker; +class AccessChecker; class ILogger; class WsServer { @@ -39,7 +39,7 @@ class WsServer { subscriptionhandler* subHandler; Authenticator* tokenValidator; vssdatabase* database; - accesschecker* accessCheck; + AccessChecker* accessCheck; WsServer(std::shared_ptr loggerUtil, int port, std::string configFileName, bool secure); ~WsServer(); diff --git a/w3c-visserver-api/include/subscriptionhandler.hpp b/w3c-visserver-api/include/subscriptionhandler.hpp index 7448b08..06b61d4 100644 --- a/w3c-visserver-api/include/subscriptionhandler.hpp +++ b/w3c-visserver-api/include/subscriptionhandler.hpp @@ -24,7 +24,7 @@ #include -class accesschecker; +class AccessChecker; class Authenticator; class vssdatabase; class wschannel; @@ -43,7 +43,7 @@ class subscriptionhandler { std::unordered_map subscribeHandle; WsServer* server; Authenticator* validator; - accesschecker* checkAccess; + AccessChecker* checkAccess; std::mutex subMutex; std::thread subThread; bool threadRun; @@ -53,7 +53,7 @@ class subscriptionhandler { subscriptionhandler(std::shared_ptr loggerUtil, WsServer* wserver, Authenticator* authenticate, - accesschecker* checkAccess); + AccessChecker* checkAccess); ~subscriptionhandler(); uint32_t subscribe(wschannel& channel, vssdatabase* db, diff --git a/w3c-visserver-api/include/vsscommandprocessor.hpp b/w3c-visserver-api/include/vsscommandprocessor.hpp index cf9e118..fadf0c7 100644 --- a/w3c-visserver-api/include/vsscommandprocessor.hpp +++ b/w3c-visserver-api/include/vsscommandprocessor.hpp @@ -22,7 +22,7 @@ class vssdatabase; class subscriptionhandler; class Authenticator; -class accesschecker; +class AccessChecker; class wschannel; class ILogger; @@ -32,7 +32,7 @@ class vsscommandprocessor { vssdatabase* database = NULL; subscriptionhandler* subHandler = NULL; Authenticator* tokenValidator = NULL; - accesschecker* accessValidator = NULL; + AccessChecker* accessValidator = NULL; #ifdef JSON_SIGNING_ON signing* signer = NULL; #endif diff --git a/w3c-visserver-api/include/vssdatabase.hpp b/w3c-visserver-api/include/vssdatabase.hpp index ca1b1e1..217ccd8 100644 --- a/w3c-visserver-api/include/vssdatabase.hpp +++ b/w3c-visserver-api/include/vssdatabase.hpp @@ -22,7 +22,7 @@ #include class subscriptionhandler; -class accesschecker; +class AccessChecker; class wschannel; class ILogger; @@ -39,7 +39,7 @@ class vssdatabase { jsoncons::json data_tree; jsoncons::json meta_tree; subscriptionhandler* subHandler; - accesschecker* accessValidator; + AccessChecker* accessValidator; std::string getVSSSpecificPath(std::string path, bool& isBranch, jsoncons::json& tree); std::string getPathForMetadata(std::string path, bool& isBranch); std::list getPathForGet(std::string path, bool& isBranch); @@ -50,7 +50,7 @@ class vssdatabase { public: vssdatabase(std::shared_ptr loggerUtil, subscriptionhandler* subHandle, - accesschecker* accValidator); + AccessChecker* accValidator); ~vssdatabase(); void initJsonTree(std::string fileName); jsoncons::json getMetaData(std::string path); diff --git a/w3c-visserver-api/src/accesschecker.cpp b/w3c-visserver-api/src/AccessChecker.cpp similarity index 84% rename from w3c-visserver-api/src/accesschecker.cpp rename to w3c-visserver-api/src/AccessChecker.cpp index bced2e3..e0b1fc7 100644 --- a/w3c-visserver-api/src/accesschecker.cpp +++ b/w3c-visserver-api/src/AccessChecker.cpp @@ -12,16 +12,16 @@ * ***************************************************************************** */ -#include "accesschecker.hpp" +#include "AccessChecker.hpp" using namespace std; -accesschecker::accesschecker(Authenticator *vdator) { +AccessChecker::AccessChecker(Authenticator *vdator) { tokenValidator = vdator; } // check the permissions json in wschannel if path has read access -bool accesschecker::checkReadAccess(wschannel &channel, string path) { +bool AccessChecker::checkReadAccess(wschannel &channel, string path) { json permissions = channel.getPermissions(); string perm = permissions.get_with_default(path, ""); @@ -32,7 +32,7 @@ bool accesschecker::checkReadAccess(wschannel &channel, string path) { } // check the permissions json in wschannel if path has write access -bool accesschecker::checkWriteAccess(wschannel &channel, string path) { +bool AccessChecker::checkWriteAccess(wschannel &channel, string path) { json permissions = channel.getPermissions(); string perm = permissions.get_with_default(path, ""); @@ -44,7 +44,7 @@ bool accesschecker::checkWriteAccess(wschannel &channel, string path) { // Checks if all the paths have write access.If even 1 path in the list does not // have write access, this method returns false. -bool accesschecker::checkPathWriteAccess(wschannel &channel, json paths) { +bool AccessChecker::checkPathWriteAccess(wschannel &channel, json paths) { for (size_t i = 0; i < paths.size(); i++) { json item = paths[i]; string jPath = item["path"].as(); diff --git a/w3c-visserver-api/src/WsServer.cpp b/w3c-visserver-api/src/WsServer.cpp index cd51884..191aaf4 100644 --- a/w3c-visserver-api/src/WsServer.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -14,7 +14,7 @@ #include "WsServer.hpp" #include "ILogger.hpp" -#include "accesschecker.hpp" +#include "AccessChecker.hpp" #include "Authenticator.hpp" #include "subscriptionhandler.hpp" #include "visconf.hpp" @@ -59,7 +59,7 @@ WsServer::WsServer(std::shared_ptr loggerUtil, int port, string configF } tokenValidator = new Authenticator(logger, "appstacle", "RS256"); - accessCheck = new accesschecker(tokenValidator); + accessCheck = new AccessChecker(tokenValidator); subHandler = new subscriptionhandler(logger, this, tokenValidator, accessCheck); database = new vssdatabase(logger, subHandler, accessCheck); cmdProcessor = new vsscommandprocessor(logger, database, tokenValidator, subHandler); diff --git a/w3c-visserver-api/src/subscriptionhandler.cpp b/w3c-visserver-api/src/subscriptionhandler.cpp index 5275b07..8c77a56 100644 --- a/w3c-visserver-api/src/subscriptionhandler.cpp +++ b/w3c-visserver-api/src/subscriptionhandler.cpp @@ -18,7 +18,7 @@ #include -#include "accesschecker.hpp" +#include "AccessChecker.hpp" #include "Authenticator.hpp" #include "exception.hpp" #include "visconf.hpp" @@ -34,7 +34,7 @@ using namespace jsoncons::jsonpath; subscriptionhandler::subscriptionhandler(std::shared_ptr loggerUtil, WsServer* wserver, Authenticator* authenticate, - accesschecker* checkAcc) { + AccessChecker* checkAcc) { logger = loggerUtil; server = wserver; validator = authenticate; diff --git a/w3c-visserver-api/src/vsscommandprocessor.cpp b/w3c-visserver-api/src/vsscommandprocessor.cpp index 9d31923..1b0a1d9 100644 --- a/w3c-visserver-api/src/vsscommandprocessor.cpp +++ b/w3c-visserver-api/src/vsscommandprocessor.cpp @@ -24,7 +24,7 @@ #include "server_ws.hpp" #include "visconf.hpp" #include "vssdatabase.hpp" -#include "accesschecker.hpp" +#include "AccessChecker.hpp" #include "subscriptionhandler.hpp" #include "ILogger.hpp" @@ -121,7 +121,7 @@ vsscommandprocessor::vsscommandprocessor( database = dbase; tokenValidator = vdator; subHandler = subhandler; - accessValidator = new accesschecker(tokenValidator); + accessValidator = new AccessChecker(tokenValidator); #ifdef JSON_SIGNING_ON signer = new signing(); #endif diff --git a/w3c-visserver-api/src/vssdatabase.cpp b/w3c-visserver-api/src/vssdatabase.cpp index 2200041..920a892 100644 --- a/w3c-visserver-api/src/vssdatabase.cpp +++ b/w3c-visserver-api/src/vssdatabase.cpp @@ -21,7 +21,7 @@ #include -#include "accesschecker.hpp" +#include "AccessChecker.hpp" #include "subscriptionhandler.hpp" using namespace std; @@ -168,7 +168,7 @@ namespace { // Constructor vssdatabase::vssdatabase(std::shared_ptr loggerUtil, subscriptionhandler* subHandle, - accesschecker* accValidator) { + AccessChecker* accValidator) { logger = loggerUtil; subHandler = subHandle; accessValidator = accValidator; diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index 204f5b7..c4195f1 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -22,7 +22,7 @@ #include "exception.hpp" // #include -#include "accesschecker.hpp" +#include "AccessChecker.hpp" #include "Authenticator.hpp" #include "signing.hpp" #include "subscriptionhandler.hpp" @@ -65,7 +65,7 @@ std::shared_ptr logger; WsServer* webSocket; subscriptionhandler* subhandler; Authenticator* authhandler; -accesschecker* accesshandler; +AccessChecker* accesshandler; vssdatabase* database; signing* json_signer; vsscommandprocessor* commandProc; @@ -76,7 +76,7 @@ w3cunittest::w3cunittest(bool secure) { logger = std::make_shared(static_cast(LogLevel::ALL)); webSocket = new WsServer(logger, PORT, "vss_rel_2.0.json", secure); authhandler = new Authenticator(logger, "",""); - accesshandler = new accesschecker(authhandler); + accesshandler = new AccessChecker(authhandler); subhandler = new subscriptionhandler(logger, webSocket, authhandler, accesshandler); database = new vssdatabase(logger, subhandler, accesshandler); commandProc = new vsscommandprocessor(logger, database, authhandler , subhandler); diff --git a/w3c-visserver-api/unit-test/w3cunittest.hpp b/w3c-visserver-api/unit-test/w3cunittest.hpp index f855d49..f319194 100644 --- a/w3c-visserver-api/unit-test/w3cunittest.hpp +++ b/w3c-visserver-api/unit-test/w3cunittest.hpp @@ -23,7 +23,7 @@ // #include "WsServer.hpp" // #include "subscriptionhandler.hpp" // #include "Authenticator.hpp" -// #include "accesschecker.hpp" +// #include "AccessChecker.hpp" // #include "signing.hpp" // using namespace std; From c42b4cff3f5149c3af9073eae3d200f66d9e679a Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 11:29:52 +0200 Subject: [PATCH 05/32] Renamed SubscriptionHandler for later refactor Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 2 +- ...ionhandler.hpp => SubscriptionHandler.hpp} | 6 +-- w3c-visserver-api/include/WsServer.hpp | 4 +- .../include/vsscommandprocessor.hpp | 6 +-- w3c-visserver-api/include/vssdatabase.hpp | 8 ++-- ...ionhandler.cpp => SubscriptionHandler.cpp} | 38 +++++++++---------- w3c-visserver-api/src/WsServer.cpp | 4 +- w3c-visserver-api/src/vsscommandprocessor.cpp | 4 +- w3c-visserver-api/src/vssdatabase.cpp | 4 +- w3c-visserver-api/unit-test/w3cunittest.cpp | 6 +-- w3c-visserver-api/unit-test/w3cunittest.hpp | 2 +- 11 files changed, 42 insertions(+), 42 deletions(-) rename w3c-visserver-api/include/{subscriptionhandler.hpp => SubscriptionHandler.hpp} (94%) rename w3c-visserver-api/src/{subscriptionhandler.cpp => SubscriptionHandler.cpp} (82%) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index bd93472..ac30d00 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -111,7 +111,7 @@ if(UNIT_TEST) ${CMAKE_CURRENT_SOURCE_DIR}/src/vsscommandprocessor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/AccessChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Authenticator.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/subscriptionhandler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/SubscriptionHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/signing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/WsServer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/BasicLogger.cpp diff --git a/w3c-visserver-api/include/subscriptionhandler.hpp b/w3c-visserver-api/include/SubscriptionHandler.hpp similarity index 94% rename from w3c-visserver-api/include/subscriptionhandler.hpp rename to w3c-visserver-api/include/SubscriptionHandler.hpp index 06b61d4..a70f03f 100644 --- a/w3c-visserver-api/include/subscriptionhandler.hpp +++ b/w3c-visserver-api/include/SubscriptionHandler.hpp @@ -37,7 +37,7 @@ typedef std::unordered_map subscriptions_t; // Subscription UUID typedef std::string uuid_t; -class subscriptionhandler { +class SubscriptionHandler { private: std::shared_ptr logger; std::unordered_map subscribeHandle; @@ -50,11 +50,11 @@ class subscriptionhandler { std::queue> buffer; public: - subscriptionhandler(std::shared_ptr loggerUtil, + SubscriptionHandler(std::shared_ptr loggerUtil, WsServer* wserver, Authenticator* authenticate, AccessChecker* checkAccess); - ~subscriptionhandler(); + ~SubscriptionHandler(); uint32_t subscribe(wschannel& channel, vssdatabase* db, uint32_t channelID, std::string path); diff --git a/w3c-visserver-api/include/WsServer.hpp b/w3c-visserver-api/include/WsServer.hpp index e0b8413..32cb11d 100644 --- a/w3c-visserver-api/include/WsServer.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -20,7 +20,7 @@ class vsscommandprocessor; class vsscommandprocessor; -class subscriptionhandler; +class SubscriptionHandler; class Authenticator; class vssdatabase; class AccessChecker; @@ -36,7 +36,7 @@ class WsServer { public: vsscommandprocessor* cmdProcessor; - subscriptionhandler* subHandler; + SubscriptionHandler* subHandler; Authenticator* tokenValidator; vssdatabase* database; AccessChecker* accessCheck; diff --git a/w3c-visserver-api/include/vsscommandprocessor.hpp b/w3c-visserver-api/include/vsscommandprocessor.hpp index fadf0c7..1e5f053 100644 --- a/w3c-visserver-api/include/vsscommandprocessor.hpp +++ b/w3c-visserver-api/include/vsscommandprocessor.hpp @@ -20,7 +20,7 @@ #include class vssdatabase; -class subscriptionhandler; +class SubscriptionHandler; class Authenticator; class AccessChecker; class wschannel; @@ -30,7 +30,7 @@ class vsscommandprocessor { private: std::shared_ptr logger; vssdatabase* database = NULL; - subscriptionhandler* subHandler = NULL; + SubscriptionHandler* subHandler = NULL; Authenticator* tokenValidator = NULL; AccessChecker* accessValidator = NULL; #ifdef JSON_SIGNING_ON @@ -54,7 +54,7 @@ class vsscommandprocessor { vsscommandprocessor(std::shared_ptr loggerUtil, vssdatabase* database, Authenticator* vdator, - subscriptionhandler* subhandler); + SubscriptionHandler* subhandler); ~vsscommandprocessor(); std::string processQuery(std::string req_json, wschannel& channel); }; diff --git a/w3c-visserver-api/include/vssdatabase.hpp b/w3c-visserver-api/include/vssdatabase.hpp index 217ccd8..95283a4 100644 --- a/w3c-visserver-api/include/vssdatabase.hpp +++ b/w3c-visserver-api/include/vssdatabase.hpp @@ -21,13 +21,13 @@ #include -class subscriptionhandler; +class SubscriptionHandler; class AccessChecker; class wschannel; class ILogger; class vssdatabase { - friend class subscriptionhandler; + friend class SubscriptionHandler; friend class Authenticator; #ifdef UNIT_TEST friend class w3cunittest; @@ -38,7 +38,7 @@ class vssdatabase { std::mutex rwMutex; jsoncons::json data_tree; jsoncons::json meta_tree; - subscriptionhandler* subHandler; + SubscriptionHandler* subHandler; AccessChecker* accessValidator; std::string getVSSSpecificPath(std::string path, bool& isBranch, jsoncons::json& tree); std::string getPathForMetadata(std::string path, bool& isBranch); @@ -49,7 +49,7 @@ class vssdatabase { public: vssdatabase(std::shared_ptr loggerUtil, - subscriptionhandler* subHandle, + SubscriptionHandler* subHandle, AccessChecker* accValidator); ~vssdatabase(); void initJsonTree(std::string fileName); diff --git a/w3c-visserver-api/src/subscriptionhandler.cpp b/w3c-visserver-api/src/SubscriptionHandler.cpp similarity index 82% rename from w3c-visserver-api/src/subscriptionhandler.cpp rename to w3c-visserver-api/src/SubscriptionHandler.cpp index 8c77a56..997f44f 100644 --- a/w3c-visserver-api/src/subscriptionhandler.cpp +++ b/w3c-visserver-api/src/SubscriptionHandler.cpp @@ -11,7 +11,7 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#include "subscriptionhandler.hpp" +#include "SubscriptionHandler.hpp" #include // usleep #include @@ -31,7 +31,7 @@ using namespace std; using namespace jsoncons::jsonpath; // using jsoncons::jsoncons::jsoncons::json; -subscriptionhandler::subscriptionhandler(std::shared_ptr loggerUtil, +SubscriptionHandler::SubscriptionHandler(std::shared_ptr loggerUtil, WsServer* wserver, Authenticator* authenticate, AccessChecker* checkAcc) { @@ -42,11 +42,11 @@ subscriptionhandler::subscriptionhandler(std::shared_ptr loggerUtil, startThread(); } -subscriptionhandler::~subscriptionhandler() { +SubscriptionHandler::~SubscriptionHandler() { stopThread(); } -uint32_t subscriptionhandler::subscribe(wschannel& channel, +uint32_t SubscriptionHandler::subscribe(wschannel& channel, vssdatabase* db, uint32_t channelID, string path) { // generate subscribe ID "randomly". @@ -74,7 +74,7 @@ uint32_t subscriptionhandler::subscribe(wschannel& channel, auto handle = subscribeHandle.find(sigUUID); if (handle != subscribeHandle.end()) { - logger->Log(LogLevel::VERBOSE, string("subscriptionhandler::subscribe: Updating the previous subscribe ") + logger->Log(LogLevel::VERBOSE, string("SubscriptionHandler::subscribe: Updating the previous subscribe ") + string("ID with a new one")); } @@ -82,7 +82,7 @@ uint32_t subscriptionhandler::subscribe(wschannel& channel, return subId; } else if (resArray.is_array()) { - logger->Log(LogLevel::INFO, "subscriptionhandler::subscribe :signals found in path" + path + logger->Log(LogLevel::INFO, "SubscriptionHandler::subscribe :signals found in path" + path + "Array size: " + to_string(resArray.size()) + ". Subscribe works for 1 signal at a time"); stringstream msg; @@ -90,7 +90,7 @@ uint32_t subscriptionhandler::subscribe(wschannel& channel, << ". Subscribe works for 1 signal at a time"; throw noPathFoundonTree(msg.str()); } else { - logger->Log(LogLevel::ERROR, string("subscriptionhandler::subscribe: some error occurred while adding ") + logger->Log(LogLevel::ERROR, string("SubscriptionHandler::subscribe: some error occurred while adding ") + string("subscription")); stringstream msg; msg << "some error occured while adding subscription for path = " << path; @@ -98,7 +98,7 @@ uint32_t subscriptionhandler::subscribe(wschannel& channel, } } -int subscriptionhandler::unsubscribe(uint32_t subscribeID) { +int SubscriptionHandler::unsubscribe(uint32_t subscribeID) { for (auto& uuid : subscribeHandle) { auto subscriptions = &(uuid.second); auto subscription = subscriptions->find(subscribeID); @@ -109,7 +109,7 @@ int subscriptionhandler::unsubscribe(uint32_t subscribeID) { return 0; } -int subscriptionhandler::unsubscribeAll(uint32_t connectionID) { +int SubscriptionHandler::unsubscribeAll(uint32_t connectionID) { for (auto& uuid : subscribeHandle) { auto subscriptions = &(uuid.second); for (auto& subscription : *subscriptions) { @@ -121,7 +121,7 @@ int subscriptionhandler::unsubscribeAll(uint32_t connectionID) { return 0; } -int subscriptionhandler::updateByUUID(string UUID, jsoncons::json value) { +int SubscriptionHandler::updateByUUID(string UUID, jsoncons::json value) { auto handle = subscribeHandle.find(UUID); if (handle == subscribeHandle.end()) { // UUID not found @@ -139,11 +139,11 @@ int subscriptionhandler::updateByUUID(string UUID, jsoncons::json value) { return 0; } -WsServer* subscriptionhandler::getServer() { +WsServer* SubscriptionHandler::getServer() { return server; } -int subscriptionhandler::updateByPath(string path, json value) { +int SubscriptionHandler::updateByPath(string path, json value) { /* TODO: Implement */ (void) path; (void) value; @@ -151,8 +151,8 @@ int subscriptionhandler::updateByPath(string path, json value) { return 0; } -void* subscriptionhandler::subThreadRunner() { - // subscriptionhandler* handler = (subscriptionhandler*)instance; +void* SubscriptionHandler::subThreadRunner() { + // SubscriptionHandler* handler = (SubscriptionHandler*)instance; logger->Log(LogLevel::VERBOSE, "SubscribeThread: Started Subscription Thread!"); @@ -187,11 +187,11 @@ void* subscriptionhandler::subThreadRunner() { return NULL; } -int subscriptionhandler::startThread() { - subThread = thread(&subscriptionhandler::subThreadRunner, this); +int SubscriptionHandler::startThread() { + subThread = thread(&SubscriptionHandler::subThreadRunner, this); /* if (pthread_create(&subscription_thread, NULL, &subThread, this)) { - logger->Log(LogLevel::ERROR, "subscriptionhandler::startThread: Error creating subscription " + logger->Log(LogLevel::ERROR, "SubscriptionHandler::startThread: Error creating subscription " + "handler thread"); return 1; } @@ -200,7 +200,7 @@ int subscriptionhandler::startThread() { return 0; } -int subscriptionhandler::stopThread() { +int SubscriptionHandler::stopThread() { subMutex.lock(); threadRun = false; subThread.join(); @@ -208,4 +208,4 @@ int subscriptionhandler::stopThread() { return 0; } -bool subscriptionhandler::isThreadRunning() { return threadRun; } +bool SubscriptionHandler::isThreadRunning() { return threadRun; } diff --git a/w3c-visserver-api/src/WsServer.cpp b/w3c-visserver-api/src/WsServer.cpp index 191aaf4..4a69fb6 100644 --- a/w3c-visserver-api/src/WsServer.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -16,7 +16,7 @@ #include "ILogger.hpp" #include "AccessChecker.hpp" #include "Authenticator.hpp" -#include "subscriptionhandler.hpp" +#include "SubscriptionHandler.hpp" #include "visconf.hpp" #include "vsscommandprocessor.hpp" #include "vssdatabase.hpp" @@ -60,7 +60,7 @@ WsServer::WsServer(std::shared_ptr loggerUtil, int port, string configF tokenValidator = new Authenticator(logger, "appstacle", "RS256"); accessCheck = new AccessChecker(tokenValidator); - subHandler = new subscriptionhandler(logger, this, tokenValidator, accessCheck); + subHandler = new SubscriptionHandler(logger, this, tokenValidator, accessCheck); database = new vssdatabase(logger, subHandler, accessCheck); cmdProcessor = new vsscommandprocessor(logger, database, tokenValidator, subHandler); wserver = this; diff --git a/w3c-visserver-api/src/vsscommandprocessor.cpp b/w3c-visserver-api/src/vsscommandprocessor.cpp index 1b0a1d9..d5ea010 100644 --- a/w3c-visserver-api/src/vsscommandprocessor.cpp +++ b/w3c-visserver-api/src/vsscommandprocessor.cpp @@ -25,7 +25,7 @@ #include "visconf.hpp" #include "vssdatabase.hpp" #include "AccessChecker.hpp" -#include "subscriptionhandler.hpp" +#include "SubscriptionHandler.hpp" #include "ILogger.hpp" #ifdef JSON_SIGNING_ON @@ -116,7 +116,7 @@ vsscommandprocessor::vsscommandprocessor( std::shared_ptr loggerUtil, vssdatabase *dbase, Authenticator *vdator, - subscriptionhandler *subhandler) { + SubscriptionHandler *subhandler) { logger = loggerUtil; database = dbase; tokenValidator = vdator; diff --git a/w3c-visserver-api/src/vssdatabase.cpp b/w3c-visserver-api/src/vssdatabase.cpp index 920a892..1293e08 100644 --- a/w3c-visserver-api/src/vssdatabase.cpp +++ b/w3c-visserver-api/src/vssdatabase.cpp @@ -22,7 +22,7 @@ #include #include "AccessChecker.hpp" -#include "subscriptionhandler.hpp" +#include "SubscriptionHandler.hpp" using namespace std; using namespace jsoncons::jsonpath; @@ -167,7 +167,7 @@ namespace { // Constructor vssdatabase::vssdatabase(std::shared_ptr loggerUtil, - subscriptionhandler* subHandle, + SubscriptionHandler* subHandle, AccessChecker* accValidator) { logger = loggerUtil; subHandler = subHandle; diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index c4195f1..f0879c7 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -25,7 +25,7 @@ #include "AccessChecker.hpp" #include "Authenticator.hpp" #include "signing.hpp" -#include "subscriptionhandler.hpp" +#include "SubscriptionHandler.hpp" #include "vssdatabase.hpp" #include "vsscommandprocessor.hpp" #include "WsServer.hpp" @@ -63,7 +63,7 @@ namespace bt = boost::unit_test; std::shared_ptr logger; WsServer* webSocket; -subscriptionhandler* subhandler; +SubscriptionHandler* subhandler; Authenticator* authhandler; AccessChecker* accesshandler; vssdatabase* database; @@ -77,7 +77,7 @@ w3cunittest::w3cunittest(bool secure) { webSocket = new WsServer(logger, PORT, "vss_rel_2.0.json", secure); authhandler = new Authenticator(logger, "",""); accesshandler = new AccessChecker(authhandler); - subhandler = new subscriptionhandler(logger, webSocket, authhandler, accesshandler); + subhandler = new SubscriptionHandler(logger, webSocket, authhandler, accesshandler); database = new vssdatabase(logger, subhandler, accesshandler); commandProc = new vsscommandprocessor(logger, database, authhandler , subhandler); json_signer = new signing(logger); diff --git a/w3c-visserver-api/unit-test/w3cunittest.hpp b/w3c-visserver-api/unit-test/w3cunittest.hpp index f319194..5f2a985 100644 --- a/w3c-visserver-api/unit-test/w3cunittest.hpp +++ b/w3c-visserver-api/unit-test/w3cunittest.hpp @@ -21,7 +21,7 @@ // #include "vssdatabase.hpp" // #include "WsServer.hpp" -// #include "subscriptionhandler.hpp" +// #include "SubscriptionHandler.hpp" // #include "Authenticator.hpp" // #include "AccessChecker.hpp" // #include "signing.hpp" From 71aabee6c8d5338cb38d2ac42e3141798661a574 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 11:30:41 +0200 Subject: [PATCH 06/32] Renamed VssDatabase for later refactor Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 7 +- w3c-visserver-api/include/Authenticator.hpp | 6 +- .../include/SubscriptionHandler.hpp | 4 +- .../{vssdatabase.hpp => VssDatabase.hpp} | 6 +- w3c-visserver-api/include/WsServer.hpp | 4 +- .../include/vsscommandprocessor.hpp | 6 +- w3c-visserver-api/src/Authenticator.cpp | 6 +- w3c-visserver-api/src/SubscriptionHandler.cpp | 4 +- .../src/{vssdatabase.cpp => VssDatabase.cpp} | 70 +++++++++---------- w3c-visserver-api/src/WsServer.cpp | 4 +- w3c-visserver-api/src/main.cpp | 4 +- w3c-visserver-api/src/vsscommandprocessor.cpp | 4 +- w3c-visserver-api/unit-test/w3cunittest.cpp | 8 +-- w3c-visserver-api/unit-test/w3cunittest.hpp | 2 +- 14 files changed, 68 insertions(+), 67 deletions(-) rename w3c-visserver-api/include/{vssdatabase.hpp => VssDatabase.hpp} (95%) rename w3c-visserver-api/src/{vssdatabase.cpp => VssDatabase.cpp} (91%) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index ac30d00..fc1b263 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 2.8) project(w3c-visserver) -set(UNIT_TEST OFF CACHE STRING "Build unit tests") +set(UNIT_TEST ON CACHE STRING "Build unit tests") # Set this variable to ON build an exe set(BUILD_EXE ON) @@ -107,7 +107,7 @@ target_link_libraries(${PROJECT_NAME} -lgobject-2.0 -lglib-2.0 -lgio-2.0) if(UNIT_TEST) set(TEST_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/src/vssdatabase.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/VssDatabase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/vsscommandprocessor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/AccessChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Authenticator.cpp @@ -115,7 +115,8 @@ if(UNIT_TEST) ${CMAKE_CURRENT_SOURCE_DIR}/src/signing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/WsServer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/BasicLogger.cpp - # ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/vssdatabase_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/permmclient.cpp + # ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/VssDatabase_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/w3cunittest.cpp ) add_executable(w3c-unit-test ${TEST_FILES}) diff --git a/w3c-visserver-api/include/Authenticator.hpp b/w3c-visserver-api/include/Authenticator.hpp index 53033c0..80cc4af 100644 --- a/w3c-visserver-api/include/Authenticator.hpp +++ b/w3c-visserver-api/include/Authenticator.hpp @@ -20,7 +20,7 @@ using namespace std; class wschannel; -class vssdatabase; +class VssDatabase; class ILogger; class Authenticator { @@ -33,11 +33,11 @@ class Authenticator { public: Authenticator(std::shared_ptr loggerUtil, string secretkey, string algorithm); - int validate(wschannel &channel, vssdatabase *database, + int validate(wschannel &channel, VssDatabase *database, string authToken); void updatePubKey(string key); bool isStillValid(wschannel &channel); - void resolvePermissions(wschannel &channel, vssdatabase *database); + void resolvePermissions(wschannel &channel, VssDatabase *database); }; #endif diff --git a/w3c-visserver-api/include/SubscriptionHandler.hpp b/w3c-visserver-api/include/SubscriptionHandler.hpp index a70f03f..396c8aa 100644 --- a/w3c-visserver-api/include/SubscriptionHandler.hpp +++ b/w3c-visserver-api/include/SubscriptionHandler.hpp @@ -26,7 +26,7 @@ class AccessChecker; class Authenticator; -class vssdatabase; +class VssDatabase; class wschannel; class WsServer; class ILogger; @@ -56,7 +56,7 @@ class SubscriptionHandler { AccessChecker* checkAccess); ~SubscriptionHandler(); - uint32_t subscribe(wschannel& channel, vssdatabase* db, + uint32_t subscribe(wschannel& channel, VssDatabase* db, uint32_t channelID, std::string path); int unsubscribe(uint32_t subscribeID); int unsubscribeAll(uint32_t connectionID); diff --git a/w3c-visserver-api/include/vssdatabase.hpp b/w3c-visserver-api/include/VssDatabase.hpp similarity index 95% rename from w3c-visserver-api/include/vssdatabase.hpp rename to w3c-visserver-api/include/VssDatabase.hpp index 95283a4..30d0cdd 100644 --- a/w3c-visserver-api/include/vssdatabase.hpp +++ b/w3c-visserver-api/include/VssDatabase.hpp @@ -26,7 +26,7 @@ class AccessChecker; class wschannel; class ILogger; -class vssdatabase { +class VssDatabase { friend class SubscriptionHandler; friend class Authenticator; #ifdef UNIT_TEST @@ -48,10 +48,10 @@ class vssdatabase { void checkSetPermission(wschannel& channel, jsoncons::json valueJson); public: - vssdatabase(std::shared_ptr loggerUtil, + VssDatabase(std::shared_ptr loggerUtil, SubscriptionHandler* subHandle, AccessChecker* accValidator); - ~vssdatabase(); + ~VssDatabase(); void initJsonTree(std::string fileName); jsoncons::json getMetaData(std::string path); void setSignal(wschannel& channel, std::string path, jsoncons::json value); diff --git a/w3c-visserver-api/include/WsServer.hpp b/w3c-visserver-api/include/WsServer.hpp index 32cb11d..1ce3677 100644 --- a/w3c-visserver-api/include/WsServer.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -22,7 +22,7 @@ class vsscommandprocessor; class vsscommandprocessor; class SubscriptionHandler; class Authenticator; -class vssdatabase; +class VssDatabase; class AccessChecker; class ILogger; @@ -38,7 +38,7 @@ class WsServer { vsscommandprocessor* cmdProcessor; SubscriptionHandler* subHandler; Authenticator* tokenValidator; - vssdatabase* database; + VssDatabase* database; AccessChecker* accessCheck; WsServer(std::shared_ptr loggerUtil, int port, std::string configFileName, bool secure); diff --git a/w3c-visserver-api/include/vsscommandprocessor.hpp b/w3c-visserver-api/include/vsscommandprocessor.hpp index 1e5f053..6d4df75 100644 --- a/w3c-visserver-api/include/vsscommandprocessor.hpp +++ b/w3c-visserver-api/include/vsscommandprocessor.hpp @@ -19,7 +19,7 @@ #include -class vssdatabase; +class VssDatabase; class SubscriptionHandler; class Authenticator; class AccessChecker; @@ -29,7 +29,7 @@ class ILogger; class vsscommandprocessor { private: std::shared_ptr logger; - vssdatabase* database = NULL; + VssDatabase* database = NULL; SubscriptionHandler* subHandler = NULL; Authenticator* tokenValidator = NULL; AccessChecker* accessValidator = NULL; @@ -52,7 +52,7 @@ class vsscommandprocessor { public: vsscommandprocessor(std::shared_ptr loggerUtil, - vssdatabase* database, + VssDatabase* database, Authenticator* vdator, SubscriptionHandler* subhandler); ~vsscommandprocessor(); diff --git a/w3c-visserver-api/src/Authenticator.cpp b/w3c-visserver-api/src/Authenticator.cpp index 3c92bee..f8219b5 100644 --- a/w3c-visserver-api/src/Authenticator.cpp +++ b/w3c-visserver-api/src/Authenticator.cpp @@ -20,7 +20,7 @@ #include #include -#include "vssdatabase.hpp" +#include "VssDatabase.hpp" #include "wschannel.hpp" using namespace std; @@ -79,7 +79,7 @@ Authenticator::Authenticator(std::shared_ptr loggerUtil, string secretk // validates the token against expiry date/time. should be extended to check // some other claims. -int Authenticator::validate(wschannel& channel, vssdatabase* db, +int Authenticator::validate(wschannel& channel, VssDatabase* db, string authToken) { int ttl = validateToken(channel, authToken); if (ttl > 0) { @@ -108,7 +108,7 @@ bool Authenticator::isStillValid(wschannel& channel) { // resolves the permission in the JWT token and store the absolute path to the // signals in permissions JSON in wschannel. void Authenticator::resolvePermissions(wschannel& channel, - vssdatabase* database) { + VssDatabase* database) { string authToken = channel.getAuthToken(); auto decoded = jwt::decode(authToken); json claims; diff --git a/w3c-visserver-api/src/SubscriptionHandler.cpp b/w3c-visserver-api/src/SubscriptionHandler.cpp index 997f44f..7c44c24 100644 --- a/w3c-visserver-api/src/SubscriptionHandler.cpp +++ b/w3c-visserver-api/src/SubscriptionHandler.cpp @@ -22,7 +22,7 @@ #include "Authenticator.hpp" #include "exception.hpp" #include "visconf.hpp" -#include "vssdatabase.hpp" +#include "VssDatabase.hpp" #include "WsServer.hpp" #include "ILogger.hpp" @@ -47,7 +47,7 @@ SubscriptionHandler::~SubscriptionHandler() { } uint32_t SubscriptionHandler::subscribe(wschannel& channel, - vssdatabase* db, + VssDatabase* db, uint32_t channelID, string path) { // generate subscribe ID "randomly". uint32_t subId = rand() % 9999999; diff --git a/w3c-visserver-api/src/vssdatabase.cpp b/w3c-visserver-api/src/VssDatabase.cpp similarity index 91% rename from w3c-visserver-api/src/vssdatabase.cpp rename to w3c-visserver-api/src/VssDatabase.cpp index 1293e08..e5b7b7a 100644 --- a/w3c-visserver-api/src/vssdatabase.cpp +++ b/w3c-visserver-api/src/VssDatabase.cpp @@ -11,7 +11,7 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#include "vssdatabase.hpp" +#include "VssDatabase.hpp" #include #include #include @@ -40,7 +40,7 @@ namespace { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } @@ -53,7 +53,7 @@ namespace { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } @@ -66,7 +66,7 @@ namespace { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } @@ -79,7 +79,7 @@ namespace { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } @@ -91,7 +91,7 @@ namespace { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } @@ -104,7 +104,7 @@ namespace { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } @@ -117,7 +117,7 @@ namespace { std::stringstream msg; msg << "The type " << value_type << " with value '" << val.as() << "' is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } @@ -130,7 +130,7 @@ namespace { std::stringstream msg; msg << "The type " << value_type << " with value " << val.as() << " is out of bound"; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); throw outOfBoundException(msg.str()); } @@ -143,7 +143,7 @@ namespace { if (!typeValid) { string msg = "The type " + value_type + " is not supported "; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg); throw genException(msg); } @@ -157,7 +157,7 @@ namespace { string key) { if (!source.has_key("type")) { string msg = "Unknown type for signal found at " + key; - logger->Log(LogLevel::ERROR, "vssdatabase::setJsonValue : " + msg); + logger->Log(LogLevel::ERROR, "VssDatabase::setJsonValue : " + msg); throw genException(msg); } @@ -166,7 +166,7 @@ namespace { } // Constructor -vssdatabase::vssdatabase(std::shared_ptr loggerUtil, +VssDatabase::VssDatabase(std::shared_ptr loggerUtil, SubscriptionHandler* subHandle, AccessChecker* accValidator) { logger = loggerUtil; @@ -174,16 +174,16 @@ vssdatabase::vssdatabase(std::shared_ptr loggerUtil, accessValidator = accValidator; } -vssdatabase::~vssdatabase() {} +VssDatabase::~VssDatabase() {} // Initializer -void vssdatabase::initJsonTree(string fileName) { +void VssDatabase::initJsonTree(string fileName) { try { std::ifstream is(fileName); is >> data_tree; meta_tree = data_tree; - logger->Log(LogLevel::VERBOSE, "vssdatabase::vssdatabase : VSS tree initialized using JSON file = " + logger->Log(LogLevel::VERBOSE, "VssDatabase::VssDatabase : VSS tree initialized using JSON file = " + fileName); is.close(); } catch (exception const& e) { @@ -211,7 +211,7 @@ vector getPathTokens(string path) { // readable path. // Eg: jsonpath = $['Signal']['children']['OBD']['children']['RPM'] // The method returns Signal.OBD.RPM -string vssdatabase::getReadablePath(string jsonpath) { +string VssDatabase::getReadablePath(string jsonpath) { stringstream ss; // regex to remove special characters from JSONPath and make it VSS // compatible. @@ -234,7 +234,7 @@ string vssdatabase::getReadablePath(string jsonpath) { // Eg: path = Signal.OBD.RPM // The method returns $['Signal']['children']['OBD']['children']['RPM'] and // updates isBranch to false. -string vssdatabase::getVSSSpecificPath(string path, bool& isBranch, +string VssDatabase::getVSSSpecificPath(string path, bool& isBranch, jsoncons::json& tree) { vector tokens = getPathTokens(path); int tokLength = tokens.size(); @@ -264,12 +264,12 @@ string vssdatabase::getVSSSpecificPath(string path, bool& isBranch, isBranch = false; } else { isBranch = false; - logger->Log(LogLevel::ERROR, "vssdatabase::getVSSSpecificPath : Path " + logger->Log(LogLevel::ERROR, "VssDatabase::getVSSSpecificPath : Path " + format_path + " is invalid or is an empty tag!"); return ""; } } catch (exception& e) { - logger->Log(LogLevel::ERROR, "vssdatabase::getVSSSpecificPath :Exception \"" + logger->Log(LogLevel::ERROR, "VssDatabase::getVSSSpecificPath :Exception \"" + string(e.what()) + "\" occured while querying JSON. Check Path!"); isBranch = false; return ""; @@ -279,7 +279,7 @@ string vssdatabase::getVSSSpecificPath(string path, bool& isBranch, // Returns the absolute path but does not resolve wild card. Used only for // metadata request. -string vssdatabase::getPathForMetadata(string path, bool& isBranch) { +string VssDatabase::getPathForMetadata(string path, bool& isBranch) { string format_path = getVSSSpecificPath(path, isBranch, meta_tree); jsoncons::json pathRes = json_query(meta_tree, format_path, result_type::path); if (pathRes.size() > 0) { @@ -296,7 +296,7 @@ string vssdatabase::getPathForMetadata(string path, bool& isBranch) { // For eg : path = Signal.*.RPM // The method would return a list containing 1 signal path = // $['Signal']['children']['OBD']['children']['RPM'] -list vssdatabase::getPathForGet(string path, bool& isBranch) { +list VssDatabase::getPathForGet(string path, bool& isBranch) { list paths; string format_path = getVSSSpecificPath(path, isBranch, data_tree); jsoncons::json pathRes = json_query(data_tree, format_path, result_type::path); @@ -343,7 +343,7 @@ vector getVSSTokens(string path) { } // Returns the response JSON for metadata request. -jsoncons::json vssdatabase::getMetaData(string path) { +jsoncons::json VssDatabase::getMetaData(string path) { string format_path = "$"; bool isBranch = false; rwMutex.lock(); @@ -353,7 +353,7 @@ jsoncons::json vssdatabase::getMetaData(string path) { if (jPath == "") { return NULL; } - logger->Log(LogLevel::VERBOSE, "vssdatabase::getMetaData: VSS specific path =" + jPath); + logger->Log(LogLevel::VERBOSE, "VssDatabase::getMetaData: VSS specific path =" + jPath); vector tokens = getVSSTokens(jPath); int tokLength = tokens.size(); @@ -385,7 +385,7 @@ jsoncons::json vssdatabase::getMetaData(string path) { } } else { // handle exception. - logger->Log(LogLevel::ERROR, string("vssdatabase::getMetaData : More than 1 Branch/ value found! ") + logger->Log(LogLevel::ERROR, string("VssDatabase::getMetaData : More than 1 Branch/ value found! ") + string("Path requested needs to be more refined")); return NULL; } @@ -422,7 +422,7 @@ jsoncons::json vssdatabase::getMetaData(string path) { // The function would return = {{"path" : "$.Signal.children.OBD.children.RPM", // "value" : 23}, {"path" : "$.Signal.children.OBD.children.Speed", "value" : // 10}} -jsoncons::json vssdatabase::getPathForSet(string path, jsoncons::json values) { +jsoncons::json VssDatabase::getPathForSet(string path, jsoncons::json values) { jsoncons::json setValues; if (values.is_array()) { std::size_t found = path.find("*"); @@ -483,7 +483,7 @@ jsoncons::json vssdatabase::getPathForSet(string path, jsoncons::json values) { return setValues; } -void vssdatabase::checkSetPermission(wschannel& channel, jsoncons::json valueJson) { +void VssDatabase::checkSetPermission(wschannel& channel, jsoncons::json valueJson) { // check if all the paths have write access. bool haveAccess = accessValidator->checkPathWriteAccess(channel, valueJson); if (!haveAccess) { @@ -494,7 +494,7 @@ void vssdatabase::checkSetPermission(wschannel& channel, jsoncons::json valueJso } // Method for setting values to signals. -void vssdatabase::setSignal(wschannel& channel, string path, +void VssDatabase::setSignal(wschannel& channel, string path, jsoncons::json valueJson) { if (path == "") { string msg = "Path is empty while setting"; @@ -558,7 +558,7 @@ void vssdatabase::setSignal(wschannel& channel, string path, } // Method for setting values to signals. -void vssdatabase::setSignal(string path, +void VssDatabase::setSignal(string path, jsoncons::json valueJson) { if (path == "") { string msg = "Path is empty while setting"; @@ -573,7 +573,7 @@ void vssdatabase::setSignal(string path, jsoncons::json item = setValues[i]; string jPath = item["path"].as(); - logger->Log(LogLevel::VERBOSE, "vssdatabase::setSignal: path found = " + jPath + logger->Log(LogLevel::VERBOSE, "VssDatabase::setSignal: path found = " + jPath + "value to set asstring = " + item["value"].as()); rwMutex.lock(); @@ -592,7 +592,7 @@ void vssdatabase::setSignal(string path, rwMutex.lock(); json_replace(data_tree, jPath, resJson); rwMutex.unlock(); - logger->Log(LogLevel::VERBOSE, "vssdatabase::setSignal: new value set at path " + jPath); + logger->Log(LogLevel::VERBOSE, "VssDatabase::setSignal: new value set at path " + jPath); string uuid = resJson["uuid"].as(); @@ -608,20 +608,20 @@ void vssdatabase::setSignal(string path, } else if (resArray.is_array()) { stringstream msg; msg << "Path " << jPath << " has " << resArray.size() << " signals, the path needs refinement"; - logger->Log(LogLevel::WARNING, "vssdatabase::setSignal: " + msg.str()); + logger->Log(LogLevel::WARNING, "VssDatabase::setSignal: " + msg.str()); throw genException(msg.str()); } } } else { string msg = "Exception occurred while setting data for " + path; - logger->Log(LogLevel::ERROR, "vssdatabase::setSignal: " + msg); + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg); throw genException(msg); } } // Returns response JSON for get request. -jsoncons::json vssdatabase::getSignal(class wschannel& channel, string path) { +jsoncons::json VssDatabase::getSignal(class wschannel& channel, string path) { bool isBranch = false; rwMutex.lock(); @@ -633,11 +633,11 @@ jsoncons::json vssdatabase::getSignal(class wschannel& channel, string path) { return answer; } - logger->Log(LogLevel::VERBOSE, "vssdatabase::getSignal: " + to_string(pathsFound) + logger->Log(LogLevel::VERBOSE, "VssDatabase::getSignal: " + to_string(pathsFound) + " signals found under path = \"" + path + "\""); if (isBranch) { jsoncons::json answer; - logger->Log(LogLevel::VERBOSE, " vssdatabase::getSignal : \"" + path + "\" is a Branch."); + logger->Log(LogLevel::VERBOSE, " VssDatabase::getSignal : \"" + path + "\" is a Branch."); if (pathsFound == 0) { throw noPathFoundonTree(path); diff --git a/w3c-visserver-api/src/WsServer.cpp b/w3c-visserver-api/src/WsServer.cpp index 4a69fb6..b37358a 100644 --- a/w3c-visserver-api/src/WsServer.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -19,7 +19,7 @@ #include "SubscriptionHandler.hpp" #include "visconf.hpp" #include "vsscommandprocessor.hpp" -#include "vssdatabase.hpp" +#include "VssDatabase.hpp" #include @@ -61,7 +61,7 @@ WsServer::WsServer(std::shared_ptr loggerUtil, int port, string configF tokenValidator = new Authenticator(logger, "appstacle", "RS256"); accessCheck = new AccessChecker(tokenValidator); subHandler = new SubscriptionHandler(logger, this, tokenValidator, accessCheck); - database = new vssdatabase(logger, subHandler, accessCheck); + database = new VssDatabase(logger, subHandler, accessCheck); cmdProcessor = new vsscommandprocessor(logger, database, tokenValidator, subHandler); wserver = this; } diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index ae2fce5..3a6f27a 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -21,7 +21,7 @@ #include #include "WsServer.hpp" -#include "vssdatabase.hpp" +#include "VssDatabase.hpp" #include "exception.hpp" #include "BasicLogger.hpp" @@ -37,7 +37,7 @@ using jsoncons::json; // Websocket port #define PORT 8090 -vssdatabase* database = NULL; +VssDatabase* database = NULL; static GDBusNodeInfo *introspection_data = NULL; diff --git a/w3c-visserver-api/src/vsscommandprocessor.cpp b/w3c-visserver-api/src/vsscommandprocessor.cpp index d5ea010..3184b91 100644 --- a/w3c-visserver-api/src/vsscommandprocessor.cpp +++ b/w3c-visserver-api/src/vsscommandprocessor.cpp @@ -23,7 +23,7 @@ #include "exception.hpp" #include "server_ws.hpp" #include "visconf.hpp" -#include "vssdatabase.hpp" +#include "VssDatabase.hpp" #include "AccessChecker.hpp" #include "SubscriptionHandler.hpp" #include "ILogger.hpp" @@ -114,7 +114,7 @@ string valueOutOfBoundsResponse(uint32_t request_id, const string action, vsscommandprocessor::vsscommandprocessor( std::shared_ptr loggerUtil, - vssdatabase *dbase, + VssDatabase *dbase, Authenticator *vdator, SubscriptionHandler *subhandler) { logger = loggerUtil; diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index f0879c7..426b063 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -26,7 +26,7 @@ #include "Authenticator.hpp" #include "signing.hpp" #include "SubscriptionHandler.hpp" -#include "vssdatabase.hpp" +#include "VssDatabase.hpp" #include "vsscommandprocessor.hpp" #include "WsServer.hpp" #include "ILogger.hpp" @@ -66,7 +66,7 @@ WsServer* webSocket; SubscriptionHandler* subhandler; Authenticator* authhandler; AccessChecker* accesshandler; -vssdatabase* database; +VssDatabase* database; signing* json_signer; vsscommandprocessor* commandProc; @@ -78,7 +78,7 @@ w3cunittest::w3cunittest(bool secure) { authhandler = new Authenticator(logger, "",""); accesshandler = new AccessChecker(authhandler); subhandler = new SubscriptionHandler(logger, webSocket, authhandler, accesshandler); - database = new vssdatabase(logger, subhandler, accesshandler); + database = new VssDatabase(logger, subhandler, accesshandler); commandProc = new vsscommandprocessor(logger, database, authhandler , subhandler); json_signer = new signing(logger); database->initJsonTree("vss_rel_2.0.json"); @@ -100,7 +100,7 @@ json w3cunittest::test_wrap_getPathForSet(string path, json value) { } //--------Do not change the order of the tests in this file, because some results are dependent on the previous tests and data in the db------- -//----------------------------------------------------vssdatabase Tests ------------------------------------------------------------------------ +//----------------------------------------------------VssDatabase Tests ------------------------------------------------------------------------ // Get method tests BOOST_AUTO_TEST_CASE(path_for_get_without_wildcard_simple) diff --git a/w3c-visserver-api/unit-test/w3cunittest.hpp b/w3c-visserver-api/unit-test/w3cunittest.hpp index 5f2a985..a9bcdc7 100644 --- a/w3c-visserver-api/unit-test/w3cunittest.hpp +++ b/w3c-visserver-api/unit-test/w3cunittest.hpp @@ -19,7 +19,7 @@ #include -// #include "vssdatabase.hpp" +// #include "VssDatabase.hpp" // #include "WsServer.hpp" // #include "SubscriptionHandler.hpp" // #include "Authenticator.hpp" From 309b7778fb2ae626b3d32f73ef56af0bff4c134b Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 11:31:33 +0200 Subject: [PATCH 07/32] Renamed VssCommandProcessor for later refactor Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 2 +- ...dprocessor.hpp => VssCommandProcessor.hpp} | 7 +-- w3c-visserver-api/include/WsServer.hpp | 6 +-- ...dprocessor.cpp => VssCommandProcessor.cpp} | 45 +++++++++---------- w3c-visserver-api/src/WsServer.cpp | 4 +- w3c-visserver-api/unit-test/w3cunittest.cpp | 8 ++-- 6 files changed, 36 insertions(+), 36 deletions(-) rename w3c-visserver-api/include/{vsscommandprocessor.hpp => VssCommandProcessor.hpp} (94%) rename w3c-visserver-api/src/{vsscommandprocessor.cpp => VssCommandProcessor.cpp} (91%) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index fc1b263..bcf59ae 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -108,7 +108,7 @@ if(UNIT_TEST) set(TEST_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/VssDatabase.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/vsscommandprocessor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/VssCommandProcessor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/AccessChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Authenticator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SubscriptionHandler.cpp diff --git a/w3c-visserver-api/include/vsscommandprocessor.hpp b/w3c-visserver-api/include/VssCommandProcessor.hpp similarity index 94% rename from w3c-visserver-api/include/vsscommandprocessor.hpp rename to w3c-visserver-api/include/VssCommandProcessor.hpp index 6d4df75..8854883 100644 --- a/w3c-visserver-api/include/vsscommandprocessor.hpp +++ b/w3c-visserver-api/include/VssCommandProcessor.hpp @@ -26,7 +26,7 @@ class AccessChecker; class wschannel; class ILogger; -class vsscommandprocessor { +class VssCommandProcessor { private: std::shared_ptr logger; VssDatabase* database = NULL; @@ -51,11 +51,12 @@ class vsscommandprocessor { public: - vsscommandprocessor(std::shared_ptr loggerUtil, + VssCommandProcessor(std::shared_ptr loggerUtil, VssDatabase* database, Authenticator* vdator, SubscriptionHandler* subhandler); - ~vsscommandprocessor(); + ~VssCommandProcessor(); + std::string processQuery(std::string req_json, wschannel& channel); }; diff --git a/w3c-visserver-api/include/WsServer.hpp b/w3c-visserver-api/include/WsServer.hpp index 1ce3677..9054cce 100644 --- a/w3c-visserver-api/include/WsServer.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -18,8 +18,8 @@ #include "server_wss.hpp" -class vsscommandprocessor; -class vsscommandprocessor; +class VssCommandProcessor; +class VssCommandProcessor; class SubscriptionHandler; class Authenticator; class VssDatabase; @@ -35,7 +35,7 @@ class WsServer { std::string configFileName_; public: - vsscommandprocessor* cmdProcessor; + VssCommandProcessor* cmdProcessor; SubscriptionHandler* subHandler; Authenticator* tokenValidator; VssDatabase* database; diff --git a/w3c-visserver-api/src/vsscommandprocessor.cpp b/w3c-visserver-api/src/VssCommandProcessor.cpp similarity index 91% rename from w3c-visserver-api/src/vsscommandprocessor.cpp rename to w3c-visserver-api/src/VssCommandProcessor.cpp index 3184b91..59c20d7 100644 --- a/w3c-visserver-api/src/vsscommandprocessor.cpp +++ b/w3c-visserver-api/src/VssCommandProcessor.cpp @@ -12,7 +12,7 @@ * ***************************************************************************** */ -#include "vsscommandprocessor.hpp" +#include "VssCommandProcessor.hpp" #include #include @@ -112,7 +112,7 @@ string valueOutOfBoundsResponse(uint32_t request_id, const string action, return ss.str(); } -vsscommandprocessor::vsscommandprocessor( +VssCommandProcessor::VssCommandProcessor( std::shared_ptr loggerUtil, VssDatabase *dbase, Authenticator *vdator, @@ -127,11 +127,11 @@ vsscommandprocessor::vsscommandprocessor( #endif } -vsscommandprocessor::~vsscommandprocessor() { +VssCommandProcessor::~VssCommandProcessor() { delete accessValidator; } -string vsscommandprocessor::processGet(wschannel &channel, +string VssCommandProcessor::processGet(wschannel &channel, uint32_t request_id, string path) { logger->Log(LogLevel::VERBOSE, "GET :: path received from client = " + path); jsoncons::json res; @@ -153,10 +153,10 @@ string vsscommandprocessor::processGet(wschannel &channel, } } -string vsscommandprocessor::processSet(wschannel &channel, +string VssCommandProcessor::processSet(wschannel &channel, uint32_t request_id, string path, jsoncons::json value) { - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processSet: path received from client" + path); + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processSet: path received from client" + path); try { database->setSignal(channel, path, value); @@ -199,10 +199,10 @@ string vsscommandprocessor::processSet(wschannel &channel, return ss.str(); } -string vsscommandprocessor::processSubscribe(wschannel &channel, +string VssCommandProcessor::processSubscribe(wschannel &channel, uint32_t request_id, string path, uint32_t connectionID) { - logger->Log(LogLevel::VERBOSE, string("vsscommandprocessor::processSubscribe: path received from client ") + logger->Log(LogLevel::VERBOSE, string("VssCommandProcessor::processSubscribe: path received from client ") + string("for subscription")); uint32_t subId = -1; @@ -251,7 +251,7 @@ string vsscommandprocessor::processSubscribe(wschannel &channel, } } -string vsscommandprocessor::processUnsubscribe(uint32_t request_id, +string VssCommandProcessor::processUnsubscribe(uint32_t request_id, uint32_t subscribeID) { int res = subHandler->unsubscribe(subscribeID); if (res == 0) { @@ -284,7 +284,7 @@ string vsscommandprocessor::processUnsubscribe(uint32_t request_id, } } -string vsscommandprocessor::processGetMetaData(uint32_t request_id, +string VssCommandProcessor::processGetMetaData(uint32_t request_id, string path) { jsoncons::json st = database->getMetaData(path); @@ -301,9 +301,9 @@ string vsscommandprocessor::processGetMetaData(uint32_t request_id, } // Talks to the permission managent daemon and processes the token received. -string vsscommandprocessor::processAuthorizeWithPermManager(wschannel &channel, - uint32_t request_id, - string client, string clientSecret) { +string VssCommandProcessor::processAuthorizeWithPermManager(wschannel &channel, + uint32_t request_id, + string client, string clientSecret) { jsoncons::json response; // Get Token from permission management daemon. @@ -366,8 +366,7 @@ string vsscommandprocessor::processAuthorizeWithPermManager(wschannel &channel, } } - -string vsscommandprocessor::processAuthorize(wschannel &channel, +string VssCommandProcessor::processAuthorize(wschannel &channel, uint32_t request_id, string token) { tokenValidator->updatePubKey(""); @@ -402,7 +401,7 @@ string vsscommandprocessor::processAuthorize(wschannel &channel, } } -string vsscommandprocessor::processQuery(string req_json, +string VssCommandProcessor::processQuery(string req_json, wschannel &channel) { jsoncons::json root; string response; @@ -413,14 +412,14 @@ string vsscommandprocessor::processQuery(string req_json, if (action == "authorize") { string token = root["tokens"].as(); uint32_t request_id = root["requestId"].as(); - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: authorize query with token = " + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: authorize query with token = " + token + " with request id " + to_string(request_id)); response = processAuthorize(channel, request_id, token); } else if (action == "unsubscribe") { uint32_t request_id = root["requestId"].as(); uint32_t subscribeID = root["subscriptionId"].as(); - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: unsubscribe query for sub ID = " + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: unsubscribe query for sub ID = " + to_string(subscribeID) + " with request id " + to_string(request_id)); response = processUnsubscribe(request_id, subscribeID); @@ -438,7 +437,7 @@ string vsscommandprocessor::processQuery(string req_json, uint32_t request_id = root["requestId"].as(); if (action == "get") { - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: get query for " + path + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: get query for " + path + " with request id " + to_string(request_id)); response = processGet(channel, request_id, path); @@ -448,20 +447,20 @@ string vsscommandprocessor::processQuery(string req_json, } else if (action == "set") { jsoncons::json value = root["value"]; - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: set query for " + path + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: set query for " + path + " with request id " + to_string(request_id) + " value " + value.as_string()); response = processSet(channel, request_id, path, value); } else if (action == "subscribe") { - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: subscribe query for " + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: subscribe query for " + path + " with request id " + to_string(request_id)); response = processSubscribe(channel, request_id, path, channel.getConnID()); } else if (action == "getMetadata") { - logger->Log(LogLevel::VERBOSE, "vsscommandprocessor::processQuery: metadata query for " + logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: metadata query for " + path + " with request id " + to_string(request_id)); response = processGetMetaData(request_id, path); } else { - logger->Log(LogLevel::INFO, "vsscommandprocessor::processQuery: Unknown action " + action); + logger->Log(LogLevel::INFO, "VssCommandProcessor::processQuery: Unknown action " + action); } } } catch (jsoncons::json_parse_exception e) { diff --git a/w3c-visserver-api/src/WsServer.cpp b/w3c-visserver-api/src/WsServer.cpp index b37358a..ad3aad3 100644 --- a/w3c-visserver-api/src/WsServer.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -18,7 +18,7 @@ #include "Authenticator.hpp" #include "SubscriptionHandler.hpp" #include "visconf.hpp" -#include "vsscommandprocessor.hpp" +#include "VssCommandProcessor.hpp" #include "VssDatabase.hpp" #include @@ -62,7 +62,7 @@ WsServer::WsServer(std::shared_ptr loggerUtil, int port, string configF accessCheck = new AccessChecker(tokenValidator); subHandler = new SubscriptionHandler(logger, this, tokenValidator, accessCheck); database = new VssDatabase(logger, subHandler, accessCheck); - cmdProcessor = new vsscommandprocessor(logger, database, tokenValidator, subHandler); + cmdProcessor = new VssCommandProcessor(logger, database, tokenValidator, subHandler); wserver = this; } diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index 426b063..59d98db 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -27,7 +27,7 @@ #include "signing.hpp" #include "SubscriptionHandler.hpp" #include "VssDatabase.hpp" -#include "vsscommandprocessor.hpp" +#include "VssCommandProcessor.hpp" #include "WsServer.hpp" #include "ILogger.hpp" #include "BasicLogger.hpp" @@ -68,7 +68,7 @@ Authenticator* authhandler; AccessChecker* accesshandler; VssDatabase* database; signing* json_signer; -vsscommandprocessor* commandProc; +VssCommandProcessor* commandProc; w3cunittest unittestObj(false); @@ -79,7 +79,7 @@ w3cunittest::w3cunittest(bool secure) { accesshandler = new AccessChecker(authhandler); subhandler = new SubscriptionHandler(logger, webSocket, authhandler, accesshandler); database = new VssDatabase(logger, subhandler, accesshandler); - commandProc = new vsscommandprocessor(logger, database, authhandler , subhandler); + commandProc = new VssCommandProcessor(logger, database, authhandler , subhandler); json_signer = new signing(logger); database->initJsonTree("vss_rel_2.0.json"); } @@ -1165,7 +1165,7 @@ BOOST_AUTO_TEST_CASE(test_metadata_branch_with_wildcard) } -//----------------------------------------------------vsscommandprocessor Tests ------------------------------------------------------------------------ +//----------------------------------------------------VssCommandProcessor Tests ------------------------------------------------------------------------ BOOST_AUTO_TEST_CASE(process_query_set_get_simple) { From abe2745532d8051d6b4703b3ababfd37f7c38ac9 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 11:34:49 +0200 Subject: [PATCH 08/32] Renamed SigningHandler for later refactor Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 2 +- .../{signing.hpp => SigningHandler.hpp} | 4 ++-- .../include/VssCommandProcessor.hpp | 2 +- .../src/{signing.cpp => SigningHandler.cpp} | 24 +++++++++---------- w3c-visserver-api/src/VssCommandProcessor.cpp | 4 ++-- w3c-visserver-api/unit-test/w3cunittest.cpp | 10 ++++---- w3c-visserver-api/unit-test/w3cunittest.hpp | 2 +- 7 files changed, 24 insertions(+), 24 deletions(-) rename w3c-visserver-api/include/{signing.hpp => SigningHandler.hpp} (93%) rename w3c-visserver-api/src/{signing.cpp => SigningHandler.cpp} (79%) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index bcf59ae..8a14e5e 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -112,7 +112,7 @@ if(UNIT_TEST) ${CMAKE_CURRENT_SOURCE_DIR}/src/AccessChecker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Authenticator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/SubscriptionHandler.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/signing.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/SigningHandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/WsServer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/BasicLogger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/permmclient.cpp diff --git a/w3c-visserver-api/include/signing.hpp b/w3c-visserver-api/include/SigningHandler.hpp similarity index 93% rename from w3c-visserver-api/include/signing.hpp rename to w3c-visserver-api/include/SigningHandler.hpp index 479f139..243cc3e 100644 --- a/w3c-visserver-api/include/signing.hpp +++ b/w3c-visserver-api/include/SigningHandler.hpp @@ -28,7 +28,7 @@ using namespace jwt; class ILogger; -class signing { +class SigningHandler { private: std::shared_ptr logger; string key = ""; @@ -36,7 +36,7 @@ class signing { string algorithm = "RS256"; public: - signing(std::shared_ptr loggerUtil); + SigningHandler(std::shared_ptr loggerUtil); string getKey(string fileName); string getPublicKey(string fileName); string sign(json data); diff --git a/w3c-visserver-api/include/VssCommandProcessor.hpp b/w3c-visserver-api/include/VssCommandProcessor.hpp index 8854883..90289fb 100644 --- a/w3c-visserver-api/include/VssCommandProcessor.hpp +++ b/w3c-visserver-api/include/VssCommandProcessor.hpp @@ -34,7 +34,7 @@ class VssCommandProcessor { Authenticator* tokenValidator = NULL; AccessChecker* accessValidator = NULL; #ifdef JSON_SIGNING_ON - signing* signer = NULL; + SigningHandler* signer = NULL; #endif std::string processGet(wschannel& channel, uint32_t request_id, std::string path); diff --git a/w3c-visserver-api/src/signing.cpp b/w3c-visserver-api/src/SigningHandler.cpp similarity index 79% rename from w3c-visserver-api/src/signing.cpp rename to w3c-visserver-api/src/SigningHandler.cpp index 9881d65..3ba4ef3 100644 --- a/w3c-visserver-api/src/signing.cpp +++ b/w3c-visserver-api/src/SigningHandler.cpp @@ -12,25 +12,25 @@ * ***************************************************************************** */ -#include "signing.hpp" +#include "SigningHandler.hpp" #include "ILogger.hpp" -#define PRIVATEFILENAME "signing.private.key" -#define PUBLICFILENAME "signing.public.key" +#define PRIVATEFILENAME "SigningHandler.private.key" +#define PUBLICFILENAME "SigningHandler.public.key" /** Constructor */ -signing::signing(std::shared_ptr loggerUtil) : logger(loggerUtil) { +SigningHandler::SigningHandler(std::shared_ptr loggerUtil) : logger(loggerUtil) { getKey(PRIVATEFILENAME); getPublicKey(PUBLICFILENAME); } /** - Get the private key for signing. + Get the private key for SigningHandler. */ -string signing::getKey(string fileName) { +string SigningHandler::getKey(string fileName) { std::ifstream fileStream(fileName); std::string privatekey((std::istreambuf_iterator(fileStream)), (std::istreambuf_iterator())); @@ -39,9 +39,9 @@ string signing::getKey(string fileName) { } /** - Get the public key for signing. + Get the public key for SigningHandler. */ -string signing::getPublicKey(string fileName) { +string SigningHandler::getPublicKey(string fileName) { std::ifstream fileStream(fileName); std::string privatekey((std::istreambuf_iterator(fileStream)), (std::istreambuf_iterator())); @@ -52,7 +52,7 @@ string signing::getPublicKey(string fileName) { /** Signs the JSON and returns a string token */ -string signing::sign(json data) { +string SigningHandler::sign(json data) { auto algo = jwt::algorithm::rs256(pubkey, key, "", ""); auto encode = [](const std::string& data) { auto base = base::encode(data); @@ -76,7 +76,7 @@ string signing::sign(json data) { /** Signs the JSON and returns a string token */ -string signing::sign(string data) { +string SigningHandler::sign(string data) { auto algo = jwt::algorithm::rs256(pubkey, key, "", ""); auto encode = [](const std::string& data) { auto base = base::encode(data); @@ -98,7 +98,7 @@ string signing::sign(string data) { } #ifdef UNIT_TEST -string signing::decode(string signedData) { +string SigningHandler::decode(string signedData) { auto verify = jwt::verify().allow_algorithm(jwt::algorithm::rs256(pubkey, "", "", "")); @@ -106,7 +106,7 @@ string signing::decode(string signedData) { try { verify.verify(decoded_token); } catch (exception& e) { - logger->Log(LogLevel::ERROR, "Error while verifying JSON signing " + string(e.what())); + logger->Log(LogLevel::ERROR, "Error while verifying JSON SigningHandler " + string(e.what())); return ""; } diff --git a/w3c-visserver-api/src/VssCommandProcessor.cpp b/w3c-visserver-api/src/VssCommandProcessor.cpp index 59c20d7..e7f525a 100644 --- a/w3c-visserver-api/src/VssCommandProcessor.cpp +++ b/w3c-visserver-api/src/VssCommandProcessor.cpp @@ -29,7 +29,7 @@ #include "ILogger.hpp" #ifdef JSON_SIGNING_ON -#include "signing.hpp" +#include "SigningHandler.hpp" #endif using namespace std; @@ -123,7 +123,7 @@ VssCommandProcessor::VssCommandProcessor( subHandler = subhandler; accessValidator = new AccessChecker(tokenValidator); #ifdef JSON_SIGNING_ON - signer = new signing(); + signer = new SigningHandler(); #endif } diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index 59d98db..316414b 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -24,7 +24,7 @@ #include "AccessChecker.hpp" #include "Authenticator.hpp" -#include "signing.hpp" +#include "SigningHandler.hpp" #include "SubscriptionHandler.hpp" #include "VssDatabase.hpp" #include "VssCommandProcessor.hpp" @@ -67,7 +67,7 @@ SubscriptionHandler* subhandler; Authenticator* authhandler; AccessChecker* accesshandler; VssDatabase* database; -signing* json_signer; +SigningHandler* json_signer; VssCommandProcessor* commandProc; w3cunittest unittestObj(false); @@ -80,7 +80,7 @@ w3cunittest::w3cunittest(bool secure) { subhandler = new SubscriptionHandler(logger, webSocket, authhandler, accesshandler); database = new VssDatabase(logger, subhandler, accesshandler); commandProc = new VssCommandProcessor(logger, database, authhandler , subhandler); - json_signer = new signing(logger); + json_signer = new SigningHandler(logger); database->initJsonTree("vss_rel_2.0.json"); } @@ -1530,9 +1530,9 @@ BOOST_AUTO_TEST_CASE(process_query_set_one_valid_one_invalid_value, *utf::expect BOOST_TEST(response_response_getvalid_json == expected_getvalid); } -//----------------------------------------------------json signing Tests ------------------------------------------------------------------------ +//----------------------------------------------------json SigningHandler Tests ------------------------------------------------------------------------ -BOOST_AUTO_TEST_CASE(json_signing) +BOOST_AUTO_TEST_CASE(json_SigningHandler) { wschannel channel; diff --git a/w3c-visserver-api/unit-test/w3cunittest.hpp b/w3c-visserver-api/unit-test/w3cunittest.hpp index a9bcdc7..b048576 100644 --- a/w3c-visserver-api/unit-test/w3cunittest.hpp +++ b/w3c-visserver-api/unit-test/w3cunittest.hpp @@ -24,7 +24,7 @@ // #include "SubscriptionHandler.hpp" // #include "Authenticator.hpp" // #include "AccessChecker.hpp" -// #include "signing.hpp" +// #include "SigningHandler.hpp" // using namespace std; // using namespace jsoncons; From fb9dbd0945a51de59bd8a3af02fb2d46a83c058b Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 11:36:19 +0200 Subject: [PATCH 09/32] Renamed WsChannel for later refactor Signed-off-by: Miladinovic Bojan --- .../Simple-WebSocket-Server/server_ws.hpp | 4 +- w3c-visserver-api/include/AccessChecker.hpp | 8 +-- w3c-visserver-api/include/Authenticator.hpp | 12 ++-- .../include/SubscriptionHandler.hpp | 4 +- .../include/VssCommandProcessor.hpp | 14 ++-- w3c-visserver-api/include/VssDatabase.hpp | 9 +-- .../include/{wschannel.hpp => WsChannel.hpp} | 2 +- w3c-visserver-api/src/AccessChecker.cpp | 10 +-- w3c-visserver-api/src/Authenticator.cpp | 13 ++-- w3c-visserver-api/src/SubscriptionHandler.cpp | 2 +- w3c-visserver-api/src/VssCommandProcessor.cpp | 14 ++-- w3c-visserver-api/src/VssDatabase.cpp | 6 +- w3c-visserver-api/unit-test/w3cunittest.cpp | 70 +++++++++---------- 13 files changed, 84 insertions(+), 84 deletions(-) rename w3c-visserver-api/include/{wschannel.hpp => WsChannel.hpp} (98%) diff --git a/3rd-party-libs/Simple-WebSocket-Server/server_ws.hpp b/3rd-party-libs/Simple-WebSocket-Server/server_ws.hpp index 2a74f9a..edc9f2f 100644 --- a/3rd-party-libs/Simple-WebSocket-Server/server_ws.hpp +++ b/3rd-party-libs/Simple-WebSocket-Server/server_ws.hpp @@ -3,7 +3,7 @@ #include "crypto.hpp" #include "utility.hpp" -#include "wschannel.hpp" +#include "WsChannel.hpp" #include #include @@ -109,7 +109,7 @@ namespace SimpleWeb { CaseInsensitiveMultimap header; - wschannel channel; + WsChannel channel; regex::smatch path_match; diff --git a/w3c-visserver-api/include/AccessChecker.hpp b/w3c-visserver-api/include/AccessChecker.hpp index bbe8abe..980acba 100644 --- a/w3c-visserver-api/include/AccessChecker.hpp +++ b/w3c-visserver-api/include/AccessChecker.hpp @@ -17,7 +17,7 @@ #include #include #include "Authenticator.hpp" -#include "wschannel.hpp" +#include "WsChannel.hpp" using namespace std; using namespace jsoncons; @@ -29,9 +29,9 @@ class AccessChecker { public: AccessChecker(Authenticator *vdator); - bool checkReadAccess(wschannel &channel, string path); - bool checkWriteAccess(wschannel &channel, string path); - bool checkPathWriteAccess(wschannel &channel, json paths); + bool checkReadAccess(WsChannel &channel, string path); + bool checkWriteAccess(WsChannel &channel, string path); + bool checkPathWriteAccess(WsChannel &channel, json paths); }; #endif diff --git a/w3c-visserver-api/include/Authenticator.hpp b/w3c-visserver-api/include/Authenticator.hpp index 80cc4af..0214e69 100644 --- a/w3c-visserver-api/include/Authenticator.hpp +++ b/w3c-visserver-api/include/Authenticator.hpp @@ -19,7 +19,7 @@ using namespace std; -class wschannel; +class WsChannel; class VssDatabase; class ILogger; @@ -29,15 +29,15 @@ class Authenticator { string algorithm = "RS256"; std::shared_ptr logger; - int validateToken(wschannel& channel, string authToken); + int validateToken(WsChannel& channel, string authToken); public: Authenticator(std::shared_ptr loggerUtil, string secretkey, string algorithm); - int validate(wschannel &channel, VssDatabase *database, + int validate(WsChannel &channel, VssDatabase *database, string authToken); - + void updatePubKey(string key); - bool isStillValid(wschannel &channel); - void resolvePermissions(wschannel &channel, VssDatabase *database); + bool isStillValid(WsChannel &channel); + void resolvePermissions(WsChannel &channel, VssDatabase *database); }; #endif diff --git a/w3c-visserver-api/include/SubscriptionHandler.hpp b/w3c-visserver-api/include/SubscriptionHandler.hpp index 396c8aa..cb3631e 100644 --- a/w3c-visserver-api/include/SubscriptionHandler.hpp +++ b/w3c-visserver-api/include/SubscriptionHandler.hpp @@ -27,7 +27,7 @@ class AccessChecker; class Authenticator; class VssDatabase; -class wschannel; +class WsChannel; class WsServer; class ILogger; @@ -56,7 +56,7 @@ class SubscriptionHandler { AccessChecker* checkAccess); ~SubscriptionHandler(); - uint32_t subscribe(wschannel& channel, VssDatabase* db, + uint32_t subscribe(WsChannel& channel, VssDatabase* db, uint32_t channelID, std::string path); int unsubscribe(uint32_t subscribeID); int unsubscribeAll(uint32_t connectionID); diff --git a/w3c-visserver-api/include/VssCommandProcessor.hpp b/w3c-visserver-api/include/VssCommandProcessor.hpp index 90289fb..13c5b4b 100644 --- a/w3c-visserver-api/include/VssCommandProcessor.hpp +++ b/w3c-visserver-api/include/VssCommandProcessor.hpp @@ -23,7 +23,7 @@ class VssDatabase; class SubscriptionHandler; class Authenticator; class AccessChecker; -class wschannel; +class WsChannel; class ILogger; class VssCommandProcessor { @@ -37,16 +37,16 @@ class VssCommandProcessor { SigningHandler* signer = NULL; #endif - std::string processGet(wschannel& channel, uint32_t request_id, std::string path); - std::string processSet(wschannel& channel, uint32_t request_id, std::string path, + std::string processGet(WsChannel& channel, uint32_t request_id, std::string path); + std::string processSet(WsChannel& channel, uint32_t request_id, std::string path, jsoncons::json value); - std::string processSubscribe(wschannel& channel, uint32_t request_id, + std::string processSubscribe(WsChannel& channel, uint32_t request_id, std::string path, uint32_t connectionID); std::string processUnsubscribe(uint32_t request_id, uint32_t subscribeID); std::string processGetMetaData(uint32_t request_id, std::string path); - std::string processAuthorize(wschannel& channel, uint32_t request_id, + std::string processAuthorize(WsChannel& channel, uint32_t request_id, std::string token); - std::string processAuthorizeWithPermManager(wschannel &channel, uint32_t request_id, + std::string processAuthorizeWithPermManager(WsChannel &channel, uint32_t request_id, std::string client, std::string clientSecret); @@ -57,7 +57,7 @@ class VssCommandProcessor { SubscriptionHandler* subhandler); ~VssCommandProcessor(); - std::string processQuery(std::string req_json, wschannel& channel); + std::string processQuery(std::string req_json, WsChannel& channel); }; #endif diff --git a/w3c-visserver-api/include/VssDatabase.hpp b/w3c-visserver-api/include/VssDatabase.hpp index 30d0cdd..6bfee77 100644 --- a/w3c-visserver-api/include/VssDatabase.hpp +++ b/w3c-visserver-api/include/VssDatabase.hpp @@ -23,7 +23,7 @@ class SubscriptionHandler; class AccessChecker; -class wschannel; +class WsChannel; class ILogger; class VssDatabase { @@ -45,7 +45,7 @@ class VssDatabase { std::list getPathForGet(std::string path, bool& isBranch); jsoncons::json getPathForSet(std::string path, jsoncons::json value); std::string getReadablePath(std::string jsonpath); - void checkSetPermission(wschannel& channel, jsoncons::json valueJson); + void checkSetPermission(WsChannel& channel, jsoncons::json valueJson); public: VssDatabase(std::shared_ptr loggerUtil, @@ -54,8 +54,9 @@ class VssDatabase { ~VssDatabase(); void initJsonTree(std::string fileName); jsoncons::json getMetaData(std::string path); - void setSignal(wschannel& channel, std::string path, jsoncons::json value); + void setSignal(WsChannel& channel, std::string path, jsoncons::json value); void setSignal(std::string path, jsoncons::json value); - jsoncons::json getSignal(wschannel& channel, std::string path); + jsoncons::json getSignal(WsChannel& channel, std::string path); + }; #endif diff --git a/w3c-visserver-api/include/wschannel.hpp b/w3c-visserver-api/include/WsChannel.hpp similarity index 98% rename from w3c-visserver-api/include/wschannel.hpp rename to w3c-visserver-api/include/WsChannel.hpp index 2b3d405..186f75b 100644 --- a/w3c-visserver-api/include/wschannel.hpp +++ b/w3c-visserver-api/include/WsChannel.hpp @@ -22,7 +22,7 @@ using namespace std; using namespace jsoncons; using jsoncons::json; -class wschannel { +class WsChannel { private: uint32_t connectionID; bool authorized = false; diff --git a/w3c-visserver-api/src/AccessChecker.cpp b/w3c-visserver-api/src/AccessChecker.cpp index e0b1fc7..54f97be 100644 --- a/w3c-visserver-api/src/AccessChecker.cpp +++ b/w3c-visserver-api/src/AccessChecker.cpp @@ -20,8 +20,8 @@ AccessChecker::AccessChecker(Authenticator *vdator) { tokenValidator = vdator; } -// check the permissions json in wschannel if path has read access -bool AccessChecker::checkReadAccess(wschannel &channel, string path) { +// check the permissions json in WsChannel if path has read access +bool AccessChecker::checkReadAccess(WsChannel &channel, string path) { json permissions = channel.getPermissions(); string perm = permissions.get_with_default(path, ""); @@ -31,8 +31,8 @@ bool AccessChecker::checkReadAccess(wschannel &channel, string path) { return false; } -// check the permissions json in wschannel if path has write access -bool AccessChecker::checkWriteAccess(wschannel &channel, string path) { +// check the permissions json in WsChannel if path has write access +bool AccessChecker::checkWriteAccess(WsChannel &channel, string path) { json permissions = channel.getPermissions(); string perm = permissions.get_with_default(path, ""); @@ -44,7 +44,7 @@ bool AccessChecker::checkWriteAccess(wschannel &channel, string path) { // Checks if all the paths have write access.If even 1 path in the list does not // have write access, this method returns false. -bool AccessChecker::checkPathWriteAccess(wschannel &channel, json paths) { +bool AccessChecker::checkPathWriteAccess(WsChannel &channel, json paths) { for (size_t i = 0; i < paths.size(); i++) { json item = paths[i]; string jPath = item["path"].as(); diff --git a/w3c-visserver-api/src/Authenticator.cpp b/w3c-visserver-api/src/Authenticator.cpp index f8219b5..3a251e0 100644 --- a/w3c-visserver-api/src/Authenticator.cpp +++ b/w3c-visserver-api/src/Authenticator.cpp @@ -21,7 +21,7 @@ #include #include #include "VssDatabase.hpp" -#include "wschannel.hpp" +#include "WsChannel.hpp" using namespace std; @@ -38,7 +38,6 @@ namespace { } } - void Authenticator::updatePubKey(string key) { pubkey = key; if(pubkey == "") @@ -46,7 +45,7 @@ void Authenticator::updatePubKey(string key) { } // utility method to validate token. -int Authenticator::validateToken(wschannel& channel, string authToken) { +int Authenticator::validateToken(WsChannel& channel, string authToken) { auto decoded = jwt::decode(authToken); json claims; (void) channel; @@ -79,7 +78,7 @@ Authenticator::Authenticator(std::shared_ptr loggerUtil, string secretk // validates the token against expiry date/time. should be extended to check // some other claims. -int Authenticator::validate(wschannel& channel, VssDatabase* db, +int Authenticator::validate(WsChannel& channel, VssDatabase* db, string authToken) { int ttl = validateToken(channel, authToken); if (ttl > 0) { @@ -92,7 +91,7 @@ int Authenticator::validate(wschannel& channel, VssDatabase* db, // Checks if the token is still valid for the requests from the channel(client). // Internally check this before publishing messages for previously subscribed // signals. -bool Authenticator::isStillValid(wschannel& channel) { +bool Authenticator::isStillValid(WsChannel& channel) { string token = channel.getAuthToken(); int ret = validateToken(channel, token); @@ -106,8 +105,8 @@ bool Authenticator::isStillValid(wschannel& channel) { // **Do this only once for authenticate request** // resolves the permission in the JWT token and store the absolute path to the -// signals in permissions JSON in wschannel. -void Authenticator::resolvePermissions(wschannel& channel, +// signals in permissions JSON in WsChannel. +void Authenticator::resolvePermissions(WsChannel& channel, VssDatabase* database) { string authToken = channel.getAuthToken(); auto decoded = jwt::decode(authToken); diff --git a/w3c-visserver-api/src/SubscriptionHandler.cpp b/w3c-visserver-api/src/SubscriptionHandler.cpp index 7c44c24..735ff0c 100644 --- a/w3c-visserver-api/src/SubscriptionHandler.cpp +++ b/w3c-visserver-api/src/SubscriptionHandler.cpp @@ -46,7 +46,7 @@ SubscriptionHandler::~SubscriptionHandler() { stopThread(); } -uint32_t SubscriptionHandler::subscribe(wschannel& channel, +uint32_t SubscriptionHandler::subscribe(WsChannel& channel, VssDatabase* db, uint32_t channelID, string path) { // generate subscribe ID "randomly". diff --git a/w3c-visserver-api/src/VssCommandProcessor.cpp b/w3c-visserver-api/src/VssCommandProcessor.cpp index e7f525a..68e6035 100644 --- a/w3c-visserver-api/src/VssCommandProcessor.cpp +++ b/w3c-visserver-api/src/VssCommandProcessor.cpp @@ -131,7 +131,7 @@ VssCommandProcessor::~VssCommandProcessor() { delete accessValidator; } -string VssCommandProcessor::processGet(wschannel &channel, +string VssCommandProcessor::processGet(WsChannel &channel, uint32_t request_id, string path) { logger->Log(LogLevel::VERBOSE, "GET :: path received from client = " + path); jsoncons::json res; @@ -153,7 +153,7 @@ string VssCommandProcessor::processGet(wschannel &channel, } } -string VssCommandProcessor::processSet(wschannel &channel, +string VssCommandProcessor::processSet(WsChannel &channel, uint32_t request_id, string path, jsoncons::json value) { logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processSet: path received from client" + path); @@ -199,7 +199,7 @@ string VssCommandProcessor::processSet(wschannel &channel, return ss.str(); } -string VssCommandProcessor::processSubscribe(wschannel &channel, +string VssCommandProcessor::processSubscribe(WsChannel &channel, uint32_t request_id, string path, uint32_t connectionID) { logger->Log(LogLevel::VERBOSE, string("VssCommandProcessor::processSubscribe: path received from client ") @@ -300,8 +300,8 @@ string VssCommandProcessor::processGetMetaData(uint32_t request_id, return ss.str(); } -// Talks to the permission managent daemon and processes the token received. -string VssCommandProcessor::processAuthorizeWithPermManager(wschannel &channel, +// Talks to the permission management daemon and processes the token received. +string VssCommandProcessor::processAuthorizeWithPermManager(WsChannel &channel, uint32_t request_id, string client, string clientSecret) { @@ -366,7 +366,7 @@ string VssCommandProcessor::processAuthorizeWithPermManager(wschannel &channel, } } -string VssCommandProcessor::processAuthorize(wschannel &channel, +string VssCommandProcessor::processAuthorize(WsChannel &channel, uint32_t request_id, string token) { tokenValidator->updatePubKey(""); @@ -402,7 +402,7 @@ string VssCommandProcessor::processAuthorize(wschannel &channel, } string VssCommandProcessor::processQuery(string req_json, - wschannel &channel) { + WsChannel &channel) { jsoncons::json root; string response; try { diff --git a/w3c-visserver-api/src/VssDatabase.cpp b/w3c-visserver-api/src/VssDatabase.cpp index e5b7b7a..6668351 100644 --- a/w3c-visserver-api/src/VssDatabase.cpp +++ b/w3c-visserver-api/src/VssDatabase.cpp @@ -483,7 +483,7 @@ jsoncons::json VssDatabase::getPathForSet(string path, jsoncons::json values) { return setValues; } -void VssDatabase::checkSetPermission(wschannel& channel, jsoncons::json valueJson) { +void VssDatabase::checkSetPermission(WsChannel& channel, jsoncons::json valueJson) { // check if all the paths have write access. bool haveAccess = accessValidator->checkPathWriteAccess(channel, valueJson); if (!haveAccess) { @@ -494,7 +494,7 @@ void VssDatabase::checkSetPermission(wschannel& channel, jsoncons::json valueJso } // Method for setting values to signals. -void VssDatabase::setSignal(wschannel& channel, string path, +void VssDatabase::setSignal(WsChannel& channel, string path, jsoncons::json valueJson) { if (path == "") { string msg = "Path is empty while setting"; @@ -621,7 +621,7 @@ void VssDatabase::setSignal(string path, } // Returns response JSON for get request. -jsoncons::json VssDatabase::getSignal(class wschannel& channel, string path) { +jsoncons::json VssDatabase::getSignal(class WsChannel& channel, string path) { bool isBranch = false; rwMutex.lock(); diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index 316414b..c92fead 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(set_get_test_all_datatypes_boundary_conditions) string test_path_string = "Vehicle.Cabin.Infotainment.Media.Played.URI"; json result; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1169,7 +1169,7 @@ BOOST_AUTO_TEST_CASE(test_metadata_branch_with_wildcard) BOOST_AUTO_TEST_CASE(process_query_set_get_simple) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1234,7 +1234,7 @@ BOOST_AUTO_TEST_CASE(process_query_set_get_simple) BOOST_AUTO_TEST_CASE(process_query_get_withwildcard) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1276,7 +1276,7 @@ BOOST_AUTO_TEST_CASE(process_query_get_withwildcard) BOOST_AUTO_TEST_CASE(process_query_set_get_withwildcard) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1355,7 +1355,7 @@ BOOST_AUTO_TEST_CASE(process_query_set_get_withwildcard) BOOST_AUTO_TEST_CASE(process_query_get_withwildcard_invalid) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1393,7 +1393,7 @@ BOOST_AUTO_TEST_CASE(process_query_get_withwildcard_invalid) BOOST_AUTO_TEST_CASE(process_query_set_withwildcard_invalid) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1429,7 +1429,7 @@ BOOST_AUTO_TEST_CASE(process_query_set_withwildcard_invalid) BOOST_AUTO_TEST_CASE(process_query_set_invalid_value, *utf::expected_failures(1)) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1465,7 +1465,7 @@ BOOST_AUTO_TEST_CASE(process_query_set_invalid_value, *utf::expected_failures(1) BOOST_AUTO_TEST_CASE(process_query_set_one_valid_one_invalid_value, *utf::expected_failures(1)) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1535,7 +1535,7 @@ BOOST_AUTO_TEST_CASE(process_query_set_one_valid_one_invalid_value, *utf::expect BOOST_AUTO_TEST_CASE(json_SigningHandler) { - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1607,7 +1607,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJyIn19.jbhdMq5hEWXXNfZn9xE4rECIWEVsw-q3g-jxp5lLS0VAZ2WYOGoSd5JX2P9YG0Lals7Ue0wdXtgLSFtvIGU4Ol2MuPaF-Rbb-Q5O4njxg60AZ00kr6RywpyGZHK0eT9MuFCnVMN8Krf3lo2pPB1ms-WAHX6rfArbXxx0FsMau9Ewn_3m3Sc-6sz5alQw1Y7Rk0GD9Y7WP_mbICU__gd40Ypu_ki1i59M8ba5GNfd8RytEIJXAg7RTcKREWDZfMdFH5X7F6gAPA7h_tVH3-bsbT-nOsKCbM-3PM0EKAOl104SwmKcqoWnfXbUow5zt25O8LYwmrukuRBtWiLI5FxeW6ovmS-1acvS3w1LXlQZVGWtM_ZC7shtHh-iz7nyL1WCTpZswHgoqVrvnJ0PVZQkBMBFKvsbu9WkWPUqHe0sx2cDUOdolelspfClO6iP7CYTUQQqyDov9zByDiBfQ7rILQ_LcwPG6UAAbEgM0pC_lntsPzbdcq0V-rE_OMO6y7HtmGN7GPhYHGU0K4qQBuYI_Pdn2gqyCEciI6_awB1LG4RwfoC8ietGUuGmxdcl2PWm0DI-Kj1f1bNlwc-54LKg8v5K54zsBdmK4SrrJ6Nt6OgCqq3On7zHfTDFN01dqWP6EoQHhEn6Akx5HiioTW3CHSVq6pd09Po5cgAAIoQE2U0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1661,7 +1661,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_path) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJyIn19.jbhdMq5hEWXXNfZn9xE4rECIWEVsw-q3g-jxp5lLS0VAZ2WYOGoSd5JX2P9YG0Lals7Ue0wdXtgLSFtvIGU4Ol2MuPaF-Rbb-Q5O4njxg60AZ00kr6RywpyGZHK0eT9MuFCnVMN8Krf3lo2pPB1ms-WAHX6rfArbXxx0FsMau9Ewn_3m3Sc-6sz5alQw1Y7Rk0GD9Y7WP_mbICU__gd40Ypu_ki1i59M8ba5GNfd8RytEIJXAg7RTcKREWDZfMdFH5X7F6gAPA7h_tVH3-bsbT-nOsKCbM-3PM0EKAOl104SwmKcqoWnfXbUow5zt25O8LYwmrukuRBtWiLI5FxeW6ovmS-1acvS3w1LXlQZVGWtM_ZC7shtHh-iz7nyL1WCTpZswHgoqVrvnJ0PVZQkBMBFKvsbu9WkWPUqHe0sx2cDUOdolelspfClO6iP7CYTUQQqyDov9zByDiBfQ7rILQ_LcwPG6UAAbEgM0pC_lntsPzbdcq0V-rE_OMO6y7HtmGN7GPhYHGU0K4qQBuYI_Pdn2gqyCEciI6_awB1LG4RwfoC8ietGUuGmxdcl2PWm0DI-Kj1f1bNlwc-54LKg8v5K54zsBdmK4SrrJ6Nt6OgCqq3On7zHfTDFN01dqWP6EoQHhEn6Akx5HiioTW3CHSVq6pd09Po5cgAAIoQE2U0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1713,7 +1713,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_branch_path) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJyIn19.jbhdMq5hEWXXNfZn9xE4rECIWEVsw-q3g-jxp5lLS0VAZ2WYOGoSd5JX2P9YG0Lals7Ue0wdXtgLSFtvIGU4Ol2MuPaF-Rbb-Q5O4njxg60AZ00kr6RywpyGZHK0eT9MuFCnVMN8Krf3lo2pPB1ms-WAHX6rfArbXxx0FsMau9Ewn_3m3Sc-6sz5alQw1Y7Rk0GD9Y7WP_mbICU__gd40Ypu_ki1i59M8ba5GNfd8RytEIJXAg7RTcKREWDZfMdFH5X7F6gAPA7h_tVH3-bsbT-nOsKCbM-3PM0EKAOl104SwmKcqoWnfXbUow5zt25O8LYwmrukuRBtWiLI5FxeW6ovmS-1acvS3w1LXlQZVGWtM_ZC7shtHh-iz7nyL1WCTpZswHgoqVrvnJ0PVZQkBMBFKvsbu9WkWPUqHe0sx2cDUOdolelspfClO6iP7CYTUQQqyDov9zByDiBfQ7rILQ_LcwPG6UAAbEgM0pC_lntsPzbdcq0V-rE_OMO6y7HtmGN7GPhYHGU0K4qQBuYI_Pdn2gqyCEciI6_awB1LG4RwfoC8ietGUuGmxdcl2PWm0DI-Kj1f1bNlwc-54LKg8v5K54zsBdmK4SrrJ6Nt6OgCqq3On7zHfTDFN01dqWP6EoQHhEn6Akx5HiioTW3CHSVq6pd09Po5cgAAIoQE2U0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1765,7 +1765,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_non_permitted_path, *utf::expect string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJyIn19.jbhdMq5hEWXXNfZn9xE4rECIWEVsw-q3g-jxp5lLS0VAZ2WYOGoSd5JX2P9YG0Lals7Ue0wdXtgLSFtvIGU4Ol2MuPaF-Rbb-Q5O4njxg60AZ00kr6RywpyGZHK0eT9MuFCnVMN8Krf3lo2pPB1ms-WAHX6rfArbXxx0FsMau9Ewn_3m3Sc-6sz5alQw1Y7Rk0GD9Y7WP_mbICU__gd40Ypu_ki1i59M8ba5GNfd8RytEIJXAg7RTcKREWDZfMdFH5X7F6gAPA7h_tVH3-bsbT-nOsKCbM-3PM0EKAOl104SwmKcqoWnfXbUow5zt25O8LYwmrukuRBtWiLI5FxeW6ovmS-1acvS3w1LXlQZVGWtM_ZC7shtHh-iz7nyL1WCTpZswHgoqVrvnJ0PVZQkBMBFKvsbu9WkWPUqHe0sx2cDUOdolelspfClO6iP7CYTUQQqyDov9zByDiBfQ7rILQ_LcwPG6UAAbEgM0pC_lntsPzbdcq0V-rE_OMO6y7HtmGN7GPhYHGU0K4qQBuYI_Pdn2gqyCEciI6_awB1LG4RwfoC8ietGUuGmxdcl2PWm0DI-Kj1f1bNlwc-54LKg8v5K54zsBdmK4SrrJ6Nt6OgCqq3On7zHfTDFN01dqWP6EoQHhEn6Akx5HiioTW3CHSVq6pd09Po5cgAAIoQE2U0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1816,7 +1816,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_invalid_permission_valid_path) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQxIjoiciJ9fQ.hwJVPN9jBuu5vePuLh3wrn2wGXmkzbnKBKJI5FRcP_40AgQcaYlxKZf5OE3cb-CLXJLGWqnQAGKwfWawFaFIMumibIoruZghbeW3mMPtD2PRLXh5HYZuAxVHInvLqVvR4BGEFCvAx36b5aXWlmtYG69Qr1u77qEi62vVR7bh9K05ZDxaW_iQgpURlzLNQYnfzQ0POFhWeHr9RoL8xypnE8QcAb-AtS4flXov1glt2XSgVxaBf0tYAnsrsBDSPpK6_ldRAY-9LbBRK4YLyTzCRXK3KYr9hdh1pWodeKmVGAObsCUwInPdj2C17vFxBLigdPccbfViY399rPCsH6lLRU3sQo-5UWpP9pvQgieu8XpewrIXJxDTu31PpTgc60UuaoDfnPnOvgQzucBNJRGTd6sc6defijjEpLZjX2HfIs50j8uAsMjYKqJqs0LGsNBZFkKHA4ZxpgxUwWoznlponYWN_G-iSy8ASAPjsLMnYKaC0whT3lTWjpPg-yGAIAlyvH7c-QwPf1t-jUjhffXRWp3_t824-Cey93P8ZLwegL2ww6QHyzRQKnAmEnn8KgAdl99zpFfeEB6Or8o--ILIraVN4xe2CfHMKXFD3RxJ2CCbOL5gREuChoXmFY0dYZ34SoOzsnDQ-Jbvu9JTewI9DCnSCbjGw5tJZzAetuckT7w"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1867,7 +1867,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_branch_permission_valid_path) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQiOiJyIn19.aFM4kmqmupJmxkCXXPHUnmC5JC4ioIYwczk_YcGLy8ULfT4As6fzZNnAFTZVk-axBbIHnU3aUTigI5wZroF0ZkdWzJ1FA1-ZEXRLwUug9pigOC4hvTn4layT4tSEylHm3Mhh8M3Z0O4QKr-TuKZgZcbIICjrI3GQvm0kdkAO1hUTDPF2yQv16qCPuWvmJEDmy70MjzZfKIy94LCXL6Gf1OdIo4hXIbhLFPOR4ea8iaEz35VEjEZ828KbP8DCXRNWlad-CjAx7f4whS_YHctVZelFoa5G-MzNXRcr54VmkGbYM4WGDn6Twamsfb7YmwROzNmsI8DNahWWXciWxVZElsAexKx5yFXr3CslxG8dbgZgHWQ1tZe0Nq1b-4XUkomz6e6hd0iUF913D-TjvBACz4fpl5_28Wr0TMy84w1DvkpfNmgQ_1fiZNbho2uBxDoisfDWq_sI_ph0xO5mu-HKrs2T_tkuFveCPDHvIm1uZIzT8hX2cFDoBNClUlf4BOo6EHw3Ax9ISr28WzRxpZPOs9bYh4AIlnkqh28P91emK7pdb4eXhZKm3ZVWGEA17pLUUraxEhTYWXAFMsafwqTAaUhYt-JUBiExvHRvZpa49UDkXd4lJvl9gYO9YPiyrG6dioIsK9QSw-Mhob2WacRUzbG0O8V9uVApjw73tK4FYhA"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1920,7 +1920,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_branch_permission_valid_path_2) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQiOiJyIn19.aFM4kmqmupJmxkCXXPHUnmC5JC4ioIYwczk_YcGLy8ULfT4As6fzZNnAFTZVk-axBbIHnU3aUTigI5wZroF0ZkdWzJ1FA1-ZEXRLwUug9pigOC4hvTn4layT4tSEylHm3Mhh8M3Z0O4QKr-TuKZgZcbIICjrI3GQvm0kdkAO1hUTDPF2yQv16qCPuWvmJEDmy70MjzZfKIy94LCXL6Gf1OdIo4hXIbhLFPOR4ea8iaEz35VEjEZ828KbP8DCXRNWlad-CjAx7f4whS_YHctVZelFoa5G-MzNXRcr54VmkGbYM4WGDn6Twamsfb7YmwROzNmsI8DNahWWXciWxVZElsAexKx5yFXr3CslxG8dbgZgHWQ1tZe0Nq1b-4XUkomz6e6hd0iUF913D-TjvBACz4fpl5_28Wr0TMy84w1DvkpfNmgQ_1fiZNbho2uBxDoisfDWq_sI_ph0xO5mu-HKrs2T_tkuFveCPDHvIm1uZIzT8hX2cFDoBNClUlf4BOo6EHw3Ax9ISr28WzRxpZPOs9bYh4AIlnkqh28P91emK7pdb4eXhZKm3ZVWGEA17pLUUraxEhTYWXAFMsafwqTAaUhYt-JUBiExvHRvZpa49UDkXd4lJvl9gYO9YPiyrG6dioIsK9QSw-Mhob2WacRUzbG0O8V9uVApjw73tK4FYhA"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -1973,7 +1973,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_permission) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS4qLkVuZ2luZVNwZWVkIjoiciJ9fQ.fUyHFOh3rD2IwOVMIYzNKdR4Y6IbKmQhN3Y2pGfcOy8SjXcE5MS6owIYRUVCxnlnH9-ywpNrwvePgKPHnjnWq8wSHr6I22zh3dNty0dFn-gJ82LQ-aNRKcweFqZXXP7-b-__Lc_ivEZpl-w0T9IzPWsUhZyt82XIPkzOZrfULv-DhXpoVIFTr-nz7KSpypcp0j_zXvbkf35bLLwcca7nMY5a9ftMKzMcv4xWekjPQQYvGchtLi1lOG1k8iHlf_cGsVEE4CK55x3bkrEjOYagT7WlRkMgR4F4HOzG0LNHsiXEVpNf17cs1Fsy6K5ObY-_J4BCx8wWGc7Bnkg4yap_3jG1IW_o7gcINcx4WiTNHz42LU6d0GQ9spc3vSP5vPm7J1aglBqazYf-tWRHp7QF8WDtAgenLpb4Ld4n_Aq5gHBWfOlt4tCyMgOgLlnzUJT1yc65vNesB7zUAFCdJ49kSV4Lwf0hv4W-hXl3wUPvb06yaff4U2_zrDQOc7GhoVLMzHmAYccNlDEMfM6HjQAnGLLIdvMxfs5g4a2CLKfxbOusRAQYNd4XwU4CpNAWabiu0FHIC43vy578zY3dpCHBOtpEC5csNEnHqyTSWdJwMy9BLmPneNM04NIHni-4ir4ExzK1TUmIDisk5_KBWmcjKyW-HX8k_u2gxylCf9I82Y0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2026,7 +2026,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_write_permission) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS4qLkVuZ2luZVNwZWVkIjoidyJ9fQ.Z6sph6d_z6CQ66nawtQsx300u1uxREGreJ70zrKiB9_leskohqwzxGJLG3uIHRAP2SajmMWXAfxrECQsiop_ayblUf7tnhjt4wljJBreZ7P16UJxv7jQVVLf2oke9b2EF9SJCCxEYEirasChwq8kA-oQkZGdoC5lIGDepT8-tM3NZ17nNdQzQJzypLCRfUz1eA7yNnG2C7mfAdSC1aWoLNGwsy62DbgXl-S7gvRYFHZV9Kk9KCjscvGuj3550IqQfMwqnpPn92E_f82b0ip4jtm2pE4tJTw-oPjbojxN9eMFTYSM6krXDQXcCxY-bZ7zARza3qfPA8sePX33WtJPDhKt8pQ-H6mI-9wEVvwDAP9deQD4ljeiOrROKlQ6yh9CNq5ixrOnH9sYnuTe5TaGxwLDTqc4NKd76dicfAMd_dDiAs7C-wl836Xj8Aq-n4CnLW5Bh_DRpnOhyK5FgbQR5DxefVBDfDP43t6STozfk0hrU44GFpWjjedjWoWZoHqjMEcoM3Y890_mxwEoJYJgu3ZH031pY6zCuGpPtm9gysMSUy-KKkPe5jYVZcfXRR9aJRJDIki1MVHBFkbdgWy9ZTfvjQRDSY1LrAysFR6bPDs8_HZFJA7aZZRO2JNxZuizky34Pp2ZqmBQRHo_cehsXDfw5F6yh4gjxrvLprGHhBc"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2077,7 +2077,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_permission_wildcard_req string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS4qLkVuZ2luZVNwZWVkIjoiciJ9fQ.fUyHFOh3rD2IwOVMIYzNKdR4Y6IbKmQhN3Y2pGfcOy8SjXcE5MS6owIYRUVCxnlnH9-ywpNrwvePgKPHnjnWq8wSHr6I22zh3dNty0dFn-gJ82LQ-aNRKcweFqZXXP7-b-__Lc_ivEZpl-w0T9IzPWsUhZyt82XIPkzOZrfULv-DhXpoVIFTr-nz7KSpypcp0j_zXvbkf35bLLwcca7nMY5a9ftMKzMcv4xWekjPQQYvGchtLi1lOG1k8iHlf_cGsVEE4CK55x3bkrEjOYagT7WlRkMgR4F4HOzG0LNHsiXEVpNf17cs1Fsy6K5ObY-_J4BCx8wWGc7Bnkg4yap_3jG1IW_o7gcINcx4WiTNHz42LU6d0GQ9spc3vSP5vPm7J1aglBqazYf-tWRHp7QF8WDtAgenLpb4Ld4n_Aq5gHBWfOlt4tCyMgOgLlnzUJT1yc65vNesB7zUAFCdJ49kSV4Lwf0hv4W-hXl3wUPvb06yaff4U2_zrDQOc7GhoVLMzHmAYccNlDEMfM6HjQAnGLLIdvMxfs5g4a2CLKfxbOusRAQYNd4XwU4CpNAWabiu0FHIC43vy578zY3dpCHBOtpEC5csNEnHqyTSWdJwMy9BLmPneNM04NIHni-4ir4ExzK1TUmIDisk5_KBWmcjKyW-HX8k_u2gxylCf9I82Y0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2129,7 +2129,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_wildcard_permission_branch_path_ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS4qLkVuZ2luZVNwZWVkIjoiciJ9fQ.fUyHFOh3rD2IwOVMIYzNKdR4Y6IbKmQhN3Y2pGfcOy8SjXcE5MS6owIYRUVCxnlnH9-ywpNrwvePgKPHnjnWq8wSHr6I22zh3dNty0dFn-gJ82LQ-aNRKcweFqZXXP7-b-__Lc_ivEZpl-w0T9IzPWsUhZyt82XIPkzOZrfULv-DhXpoVIFTr-nz7KSpypcp0j_zXvbkf35bLLwcca7nMY5a9ftMKzMcv4xWekjPQQYvGchtLi1lOG1k8iHlf_cGsVEE4CK55x3bkrEjOYagT7WlRkMgR4F4HOzG0LNHsiXEVpNf17cs1Fsy6K5ObY-_J4BCx8wWGc7Bnkg4yap_3jG1IW_o7gcINcx4WiTNHz42LU6d0GQ9spc3vSP5vPm7J1aglBqazYf-tWRHp7QF8WDtAgenLpb4Ld4n_Aq5gHBWfOlt4tCyMgOgLlnzUJT1yc65vNesB7zUAFCdJ49kSV4Lwf0hv4W-hXl3wUPvb06yaff4U2_zrDQOc7GhoVLMzHmAYccNlDEMfM6HjQAnGLLIdvMxfs5g4a2CLKfxbOusRAQYNd4XwU4CpNAWabiu0FHIC43vy578zY3dpCHBOtpEC5csNEnHqyTSWdJwMy9BLmPneNM04NIHni-4ir4ExzK1TUmIDisk5_KBWmcjKyW-HX8k_u2gxylCf9I82Y0"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2181,7 +2181,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_read_with_full_read_permission) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZSI6InIifX0.AKCWRDrtl4UkFzAGEzsyUZIKH4Q7jHg_Y5p8xVMLWtGTXIeFdQlz7FW-6rAy1Os9ad-H8ghEepcy2gs6DK3kr3FNDxqUyIaz8p_CDkwrgqp0Wczi1DsgwbyTsY2wsDa94Mja3ng2VszQgs4FZmsdbnzFzJeAsGucOZeIQj8w68up6YI6KXiCjO1094I2eixkclNnb1psPiucTkyListTLHxK3029fZT1EGGcrt7ziFYGiT5Z0Zk7x0PSN7dvmaT1rMOBWjbpluLCepkWZYt73ipO8YoAWhluMiW0sJI7ezrIU3UAKDkna_6kjAapm-9vAVTA63Tv4ovnRaALb_V2oxbIGCPTNoAY7ui00uFcxuN2B5l4M05OFgMIcxwS9-UEVQIWbFUxvPTQLNkOp12jd73ic786lUOs7fvuwseMZ2KX9cpV03TSAN1BIwG_TJ0iOQJ_5wuyDFRqwBK8zubg-zohUPMwsLpZgc7fTVI7AhXzGLZ57fE977NsluzfjFS0smtuzN-8JTvAMnCrgTQwu5GyTiL64a3NlYcQ2qt5K6D8xOIs7xSe8i0_PlKSgG8ECMOBE3JwSCwU1q83mTPCTSGHxTaNYnPhputc6gEiPv2VYmppGrMCeG2d5oLNzGhecIaLf_PGTTSmrUUZNK6HkuybgZStgMdN5uMTXrBkEvw"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2234,7 +2234,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuU3BlZWQiOiJ3In19.dQGq3-OFFZSdhDYYd0IpoNvDh-p9Ak6twajzQVqY_MAWqdwc5BG6_AE6QJzvALAlCp2d-I8kE46YFM9kMGkyR7G5LUhT3h3_iEbsceyFBoPFgAwjgff-c5mZ7l7zFUEJAwtPPFysIku_uNOqmQnvUMLCJ6mBApfAaUc_08S7IUe6e6n3eu7mUdG7ZXBsg1ufcFzPZJHEjzwS4c5Nc2ZqJ4zJBMJJqq0K-TgcjsvNDYBz9is2_ClaeoWqwIZmKLszXfVViFww1Ou81ftuAfUcozyCvK3U1LIloIDxyhQ4kuz5J-Hzy2Y2SAWIZs_zwy34fijKTAj9AJSkZz_3X1dd0P0pFPkeuJUL0BVkC0PZRgtVk3IAWgSoCXOg5ra-vEPKemyoZVxGyUEbDM5D3hpSw01bl_YkZZEKvGniYGmrHelofp2NLLq9_ZkO_7_mYXedXoG82CxO315imv2x31tLg-9_oJSYP40PCRd2n4zALnywJEegn143GvSurOAwPRERtJhiI5WKbhPPwuhnhmvZVYkicPO6ZTO-8RwAB0CVaV9HjrY8Cutqb9gXppW3ajZEVRMxN4BHObqfAHeD_snZIp7GeGPRKEvshCKy7A3lynXpNYUAtkSSXUaqesjp9YoOvLM3ka1zQWNFCuLvpOFjCnR-myE4Vw4Kp-iC__Y1OxU"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2286,7 +2286,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_not_permitted) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuU3BlZWQiOiJ3In19.dQGq3-OFFZSdhDYYd0IpoNvDh-p9Ak6twajzQVqY_MAWqdwc5BG6_AE6QJzvALAlCp2d-I8kE46YFM9kMGkyR7G5LUhT3h3_iEbsceyFBoPFgAwjgff-c5mZ7l7zFUEJAwtPPFysIku_uNOqmQnvUMLCJ6mBApfAaUc_08S7IUe6e6n3eu7mUdG7ZXBsg1ufcFzPZJHEjzwS4c5Nc2ZqJ4zJBMJJqq0K-TgcjsvNDYBz9is2_ClaeoWqwIZmKLszXfVViFww1Ou81ftuAfUcozyCvK3U1LIloIDxyhQ4kuz5J-Hzy2Y2SAWIZs_zwy34fijKTAj9AJSkZz_3X1dd0P0pFPkeuJUL0BVkC0PZRgtVk3IAWgSoCXOg5ra-vEPKemyoZVxGyUEbDM5D3hpSw01bl_YkZZEKvGniYGmrHelofp2NLLq9_ZkO_7_mYXedXoG82CxO315imv2x31tLg-9_oJSYP40PCRd2n4zALnywJEegn143GvSurOAwPRERtJhiI5WKbhPPwuhnhmvZVYkicPO6ZTO-8RwAB0CVaV9HjrY8Cutqb9gXppW3ajZEVRMxN4BHObqfAHeD_snZIp7GeGPRKEvshCKy7A3lynXpNYUAtkSSXUaqesjp9YoOvLM3ka1zQWNFCuLvpOFjCnR-myE4Vw4Kp-iC__Y1OxU"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2339,7 +2339,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_wildcard_permission) string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuKiI6InJ3In19.LLdJFIuoViNF4uVv1IL30fXPySoM1oCHxLrWm6xM_4eynvXqPvrCI9fW__GMI0d8PxBLpM8FhgG3ynVVlOuLV_Sl6lDImZlkNQiR02lJwFqf67RWVdI4f4uhdHdKjEpe0a0F-6e7McS__3qQYyjNuQAZIIXkZUIDyXye9upwNARj1wGPtZyNzSY1uyxmuc7MMPaILAIzL8ZnY_D9qgbpbiInGavZtDE_X1iy9GhxbUguP8oiVYn14-H6RBDIF0s5dXwXnJ0cm9Q2DTFpb0YRq4sMgTC4PT1Smdda_6Pj2ELmBjGbH7PYlqfVk1jVdSPGcUpU48e__qVitSRkEK_unM5CihrDVIy7nw3_KclIZJa8_af3kQ4x-leg-ErRCt78j5l0DDxIo5EtCxAeLbO7baZD6D1tPrb3vYkI_9zl1vzydp_nmNMS9QzCRq5yEJfP07gpvG0Z1O0tSLedSCG9cZJ-lJ3Oj3bqxNxk3ih6vKfjnvjAgli7wEP_etHofZmqs3NI-qtP6ldz93fBfAK_iApsSnG4PV7oS_-3UQIow6fUHAA8szn4Ad1ZYiaDsXRcHbdIoLEemGDllkRTYNBe_5vFDT3s1gY82L3fvgwAzTGZ2k46eh66Zx3SmuPgHlCQK6gR-6eAVn0jh_Tjk5vubtin6UdRjHF0Y4BvCwvE0bk"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2417,7 +2417,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_branch_permission) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQiOiJ3In19.AzvUr20ZqIMt2mKhkYhM0b3RgBSnVii6oFpU6tgkP7vLOAURSqN8Lazx07eqz8CXTONqIQrmSFY9l6SFxAXNmYRCTM3t_WW3LohAmXvOajS2FQ-JrMMdSmrpWtEFlKfbvUA83qxEc9ywuPECxCxbaeWRQsK2llhsx3JdiLxkuNGdWvDpbWUTecxtedFXFhX2kQAobdhWVbxcb8qgsDvKy-WbcVjP8bJ_6-5PEAa3LggHkG3LWet9ZItBmYYX6D0ClvN12r3K0r8W1tu7NPEkB6dRZiJJ0iGg-mvTbMr3Mvprq4vTPt1xIXxWQ62d7XNm6HAV6TemUxaABcNSHtV9g753iGBOU11SwEcfZ6nAmYpKasScdRaJJJoVGgUQ1xsrTqa6Szb1jts9H1UGBTdGk1gzuCxsI_V_DQqsOjvPm-QiNr5Yl_WSZjGRoF8cpmDHW7IlEmsZl4C7X3XFRzCMzlyw8MUGq3JRTgGLdv9pJFP_pqPw2CwHai5HJJsjxJ6KgM760fIy0IiXzSdpmBHurVf25vKtcni-wAgE33wo5nVjZtFTRsSAxnXdmUdJ1YlSXUR2vm8rTVMbBP55sshFWvM_qhI-KMVe4KqDKN0XY6tzUKli1sFBLiQMRCZFXVIGyIkSRW6XIJJJqOIhk2hD7eMyCv2ZDwhMv6q7-UX-mRo"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2492,7 +2492,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_branch_permission) */ string GET_AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuU3BlZWQiOiJyIn19.d9SFeuz9_GPIlFCJNdeHMZMQzDmRMv9AtAl7H4AjTfKC4P_ivqWoH3WSfYvyrSwobjQH5EZwR8QEn0MHjOppVm0IR4nGL0Zy9KWeCTdrt97hn_Dx_dsK0FMgBgBN7KhMQmf1FKD58DDFQpkikryJCiUZ1NUHoJabWbCdOeqg0cPai2wuqd5gXwmegGb8-e4x4QB7Qvx3e2PV7bzzVI3HoDdgiNcpi1aovgqVUxY9glIP66823JzVA0dRNuooMS-pNBp26dPzxna35dWQkJ4Ps5faY1QIVTx_6LYdaN8odyWoEeeVG63RvLZPpIaMJdUUhCkJwaXXz1cZduxi6XjqdLDIK5EJjAWXyimGECLc4DoRzdlIa2Zl3Jr1u4MtZyTdl2QknWmxul9memCSxXIBq9UK0GHbhE-c8xcGY7QwvEY9l5Z5Lfd9nJVnOHGrdf24wpHKXEWCuJV5m3Bb2WzHlTttvAWrPsWMFGPnd5YmA2jio6NTPIvpbRsNcHMVWfoX4jmvdVH6XLsoGhfnFojLNSdzCQ942qO5Gg0o78hBdGCxRwlnIoqP6dMtgxS31lqs9Fct6wBS8ObngbiV7YghAKioVX7ldchJfQhWPD_xESj4YC_fs5wunCoWy-xqGFJltOC-RHS93F_5HSmBnwXaEkQwSeImyw8sVrjqHu1eOos"; - wschannel get_channel; + WsChannel get_channel; get_channel.setConnID(1234); string get_authReq(R"({ "action": "authorize", @@ -2546,7 +2546,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_wildcard_in_permitted_path) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2624,7 +2624,7 @@ BOOST_AUTO_TEST_CASE(permission_basic_write_with_wildcard_in_unpermitted_path, * */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciJ9fQ.a7qIqBsDEcBfGRGaXvmk6f8Bg3T7DkBJEnJf9iLLiney6F6XDKk-Q_5pk1vhGEKfkNpVj908JRu2Hqx7e9-Dd6XpkUtr85YztXA7bJYMelsn4ANCipcZp36xLVLYRXqSbafSbQ08BKHa9Omd1j2k7hbldd0-QhU7trdxmnbhHE7QVdE6q1CB8ogt5QRenrax4q0AjaZ49LalDU9tsxWyu2Da2_OO7M-k-LysDAL7JKabApX10sxVGhXiToxL7kYCwUAXfNSXR_wHreWhnm47h-346URjBn-kX5FeC8p2t08IkGKv-ecKEHNhmKMRQ52PLpvuVgQhASCWX8q11VomdEMZHKWJxknd535h9AS4g7aAlbquGMGi1NRWkJc1eBR9DBicA2B1kjME35Tr3Xc954W5zatN3FJCbSQJtSpcHipJUsPzmV231PMfIZJOGSeAreJTtS3F2NqMhJiafem0JkxVgMFkCOv3vHPV2-hJJ-nKwfE7TFFvjfbl3X2GVj3s-tbEelYZZyfdCHaAHmPa4W4sK5WYKUqlSnJN9_l7sLYqzziCxSR8tTeNHBddjPXx6UoZm7D8U_K1HdJcWeDkZfCIkMGYaiBMj8WPVkTym2iQxkc9pgpXs-N1SRL9Zp84FNzXZ1qGfCicceZhkTWlzOm4_SD4te3LVD14RpXaF18"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2704,7 +2704,7 @@ BOOST_AUTO_TEST_CASE(subscription_test) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2792,7 +2792,7 @@ BOOST_AUTO_TEST_CASE(subscription_test_wildcard_permission) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuKiI6InJ3In19.LLdJFIuoViNF4uVv1IL30fXPySoM1oCHxLrWm6xM_4eynvXqPvrCI9fW__GMI0d8PxBLpM8FhgG3ynVVlOuLV_Sl6lDImZlkNQiR02lJwFqf67RWVdI4f4uhdHdKjEpe0a0F-6e7McS__3qQYyjNuQAZIIXkZUIDyXye9upwNARj1wGPtZyNzSY1uyxmuc7MMPaILAIzL8ZnY_D9qgbpbiInGavZtDE_X1iy9GhxbUguP8oiVYn14-H6RBDIF0s5dXwXnJ0cm9Q2DTFpb0YRq4sMgTC4PT1Smdda_6Pj2ELmBjGbH7PYlqfVk1jVdSPGcUpU48e__qVitSRkEK_unM5CihrDVIy7nw3_KclIZJa8_af3kQ4x-leg-ErRCt78j5l0DDxIo5EtCxAeLbO7baZD6D1tPrb3vYkI_9zl1vzydp_nmNMS9QzCRq5yEJfP07gpvG0Z1O0tSLedSCG9cZJ-lJ3Oj3bqxNxk3ih6vKfjnvjAgli7wEP_etHofZmqs3NI-qtP6ldz93fBfAK_iApsSnG4PV7oS_-3UQIow6fUHAA8szn4Ad1ZYiaDsXRcHbdIoLEemGDllkRTYNBe_5vFDT3s1gY82L3fvgwAzTGZ2k46eh66Zx3SmuPgHlCQK6gR-6eAVn0jh_Tjk5vubtin6UdRjHF0Y4BvCwvE0bk"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2881,7 +2881,7 @@ BOOST_AUTO_TEST_CASE(subscription_test_no_permission, *utf::expected_failures(1) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2934,7 +2934,7 @@ BOOST_AUTO_TEST_CASE(subscription_test_invalidpath, *utf::expected_failures(1)) */ string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", @@ -2972,7 +2972,7 @@ BOOST_AUTO_TEST_CASE(process_sub_with_wildcard) { string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); channel.setAuthorized(true); channel.setAuthToken(AUTH_TOKEN); @@ -3008,7 +3008,7 @@ BOOST_AUTO_TEST_CASE(process_sub_without_wildcard) { string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); channel.setAuthorized(true); channel.setAuthToken(AUTH_TOKEN); @@ -3063,7 +3063,7 @@ BOOST_AUTO_TEST_CASE(subscription_test_invalid_wildcard, *utf::expected_failures string AUTH_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJFeGFtcGxlIEpXVCIsImlzcyI6IkVjbGlwc2Uga3Vrc2EiLCJhZG1pbiI6dHJ1ZSwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE2MDkzNzI4MDAsInczYy12c3MiOnsiVmVoaWNsZS5PQkQuRW5naW5lU3BlZWQiOiJ3ciIsIlZlaGljbGUuT0JELlNwZWVkIjoidyJ9fQ.R4Ulq0T84oiTJFb8scj-t4C-GnFQ0QvYVCd4glsXxiOlaNUIovZUehQwJAO5WK3b3Phz86yILuFCxNO7fsdHMmyUzNLhjiXMrL7Y2PU3gvr20EIoWYKyh52BFTH_YT6sB1EWfyhPb63_tWP0P2aa1JcXhBjAlXtmnIghjcj7KloH8MQGzKArjXa4R2NaKLH0FrO5aK8hBH3tevWp38Wae-fIypr4MgG-tXoKMt8juaE7RVDVTRiYyHJkCHjbZ0EZB9gAmy-_FyMiPxHNo8f49UtCGdBq82ZlQ_SKF6cMfH3iPw19BYG9ayIgzfEIm3HFhW8RdnxuxHzHYRtqaQKFYr37qNNk3lg4NRS3g9Mn4XA3ubi07JxBUcFl8_2ReJkcVqhua3ZiTcISkBmje6CUg1DmbH8-7SMaZhC-LJsZc8K9DBZN1cYCId7smhln5LcfjkZRh8N3d-hamrVRvfbdbee7_Ua-2SiJpWlPiIEgx65uYTV7flMgdnng0KVxv5-t_8QjySfKFruXE-HkYKN7TH8EqQA1RXuiDhj8bdFGtrB36HAlVah-cHnCCgL-p-29GceNIEoWJQT9hKWk8kQieXfJfiFUZPOxInDxHyUQEjblY049qMbU2kVSNvQ7nrmwP9OTjcXfnp7bndbstTHCGsVj1ixq8QF3tOdEGlC3Brg"; - wschannel channel; + WsChannel channel; channel.setConnID(1234); string authReq(R"({ "action": "authorize", From bebb9aa1dc52c0bb2fd1cb5b1b0a1304f4e356f2 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 12:45:07 +0200 Subject: [PATCH 10/32] Added initial interface classes Created interfaces based on existing class APIs Interface APIs (and interfaces) shall change more in future based on further work (decomposing, dividing who can get/set to component, ...) Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/include/IAccessChecker.hpp | 31 +++++++++++++ w3c-visserver-api/include/IAuthenticator.hpp | 37 ++++++++++++++++ w3c-visserver-api/include/ISigningHandler.hpp | 34 +++++++++++++++ .../include/ISubscriptionHandler.hpp | 43 +++++++++++++++++++ .../include/IVssCommandProcessor.hpp | 31 +++++++++++++ w3c-visserver-api/include/IVssDatabase.hpp | 35 +++++++++++++++ w3c-visserver-api/include/IWsServer.hpp | 29 +++++++++++++ 7 files changed, 240 insertions(+) create mode 100644 w3c-visserver-api/include/IAccessChecker.hpp create mode 100644 w3c-visserver-api/include/IAuthenticator.hpp create mode 100644 w3c-visserver-api/include/ISigningHandler.hpp create mode 100644 w3c-visserver-api/include/ISubscriptionHandler.hpp create mode 100644 w3c-visserver-api/include/IVssCommandProcessor.hpp create mode 100644 w3c-visserver-api/include/IVssDatabase.hpp create mode 100644 w3c-visserver-api/include/IWsServer.hpp diff --git a/w3c-visserver-api/include/IAccessChecker.hpp b/w3c-visserver-api/include/IAccessChecker.hpp new file mode 100644 index 0000000..e5ec827 --- /dev/null +++ b/w3c-visserver-api/include/IAccessChecker.hpp @@ -0,0 +1,31 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IACCESSCHECKER_H__ +#define __IACCESSCHECKER_H__ + +#include +#include + +class WsChannel; + +class IAccessChecker { +public: + virtual ~IAccessChecker() {} + + virtual bool checkReadAccess(WsChannel &channel, const std::string &path) = 0; + virtual bool checkWriteAccess(WsChannel &channel, const std::string &path) = 0; + virtual bool checkPathWriteAccess(WsChannel &channel, const jsoncons::json &paths) = 0; +}; + +#endif diff --git a/w3c-visserver-api/include/IAuthenticator.hpp b/w3c-visserver-api/include/IAuthenticator.hpp new file mode 100644 index 0000000..5d4ad72 --- /dev/null +++ b/w3c-visserver-api/include/IAuthenticator.hpp @@ -0,0 +1,37 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IAUTHENTICATOR_H__ +#define __IAUTHENTICATOR_H__ + +#include +#include + +using namespace std; + +class WsChannel; +class VssDatabase; +class ILogger; + +class IAuthenticator { +public: + virtual ~IAuthenticator() {} + + virtual int validate(WsChannel &channel, + VssDatabase *database, + string authToken) = 0; + virtual bool isStillValid(WsChannel &channel) = 0; + virtual void resolvePermissions(WsChannel &channel, VssDatabase *database) = 0; +}; + +#endif diff --git a/w3c-visserver-api/include/ISigningHandler.hpp b/w3c-visserver-api/include/ISigningHandler.hpp new file mode 100644 index 0000000..d9d77e0 --- /dev/null +++ b/w3c-visserver-api/include/ISigningHandler.hpp @@ -0,0 +1,34 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __ISIGNING_H__ +#define __ISIGNING_H__ + +#include +#include + +class ISigningHandler { + public: + virtual ~ISigningHandler() {} + + virtual std::string getKey(const std::string &fileName) = 0; + virtual std::string getPublicKey(const std::string &fileName) = 0; + virtual std::string sign(const jsoncons::json &data) = 0; + virtual std::string sign(const std::string &data) = 0; + +#ifdef UNIT_TEST + virtual std::string decode(std::string signedData) = 0; +#endif +}; + +#endif diff --git a/w3c-visserver-api/include/ISubscriptionHandler.hpp b/w3c-visserver-api/include/ISubscriptionHandler.hpp new file mode 100644 index 0000000..74725c0 --- /dev/null +++ b/w3c-visserver-api/include/ISubscriptionHandler.hpp @@ -0,0 +1,43 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __ISUBSCRIPTIONHANDLER_H__ +#define __ISUBSCRIPTIONHANDLER_H__ + +#include +#include + +class VssDatabase; +class WsChannel; +class WsServer; +class ILogger; + +class ISubscriptionHandler { + public: + virtual ~ISubscriptionHandler() {} + + virtual uint32_t subscribe(WsChannel& channel, + VssDatabase* db, + uint32_t channelID, + const std::string &path) = 0; + virtual int unsubscribe(uint32_t subscribeID) = 0; + virtual int unsubscribeAll(uint32_t connectionID) = 0; + virtual int updateByUUID(const std::string &signalUUID, const jsoncons::json &value) = 0; + virtual int updateByPath(const std::string &path, const jsoncons::json &value) = 0; + virtual WsServer* getServer() = 0; + virtual int startThread() = 0; + virtual int stopThread() = 0; + virtual bool isThreadRunning() const = 0; + virtual void* subThreadRunner() = 0; +}; +#endif diff --git a/w3c-visserver-api/include/IVssCommandProcessor.hpp b/w3c-visserver-api/include/IVssCommandProcessor.hpp new file mode 100644 index 0000000..eefcea9 --- /dev/null +++ b/w3c-visserver-api/include/IVssCommandProcessor.hpp @@ -0,0 +1,31 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IVSSCOMMANDPROCESSOR_H__ +#define __IVSSCOMMANDPROCESSOR_H__ + +#include + +#include + +class WsChannel; + +class IVssCommandProcessor { + public: + virtual ~IVssCommandProcessor() {} + + virtual std::string processQuery(const std::string &req_json, + WsChannel& channel) = 0; +}; + +#endif diff --git a/w3c-visserver-api/include/IVssDatabase.hpp b/w3c-visserver-api/include/IVssDatabase.hpp new file mode 100644 index 0000000..0304ba4 --- /dev/null +++ b/w3c-visserver-api/include/IVssDatabase.hpp @@ -0,0 +1,35 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IVSSDATABASE_HPP__ +#define __IVSSDATABASE_HPP__ + +#include + +#include + +class WsChannel; + +class IVssDatabase { + public: + virtual ~IVssDatabase() {} + + virtual void initJsonTree(const std::string &fileName) = 0; + virtual jsoncons::json getMetaData(const std::string &path) = 0; + virtual void setSignal(WsChannel& channel, + const std::string &path, + jsoncons::json value) = 0; + virtual jsoncons::json getSignal(WsChannel& channel, const std::string &path) = 0; +}; + +#endif diff --git a/w3c-visserver-api/include/IWsServer.hpp b/w3c-visserver-api/include/IWsServer.hpp new file mode 100644 index 0000000..ad9e986 --- /dev/null +++ b/w3c-visserver-api/include/IWsServer.hpp @@ -0,0 +1,29 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ + +#ifndef __IWSSERVER_H__ +#define __IWSSERVER_H__ + +#include + + +class IWsServer { + public: + virtual ~IWsServer() {} + + virtual void startServer(const std::string &endpointName) = 0; + virtual void sendToConnection(uint32_t connID, const std::string &message) = 0; + virtual void start() = 0; +}; +#endif From e8aed160d84376cab423530aae01110d3908d09a Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 12:48:37 +0200 Subject: [PATCH 11/32] Updated classes to inherit their interfaces Small refactor to align with interface API definitions (mostly change of const on input by default). Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/include/AccessChecker.hpp | 15 +++++---------- w3c-visserver-api/include/Authenticator.hpp | 1 + w3c-visserver-api/include/SigningHandler.hpp | 12 +++++++----- w3c-visserver-api/include/SubscriptionHandler.hpp | 14 ++++++++------ w3c-visserver-api/include/VssCommandProcessor.hpp | 6 ++++-- w3c-visserver-api/include/VssDatabase.hpp | 15 +++++++++------ w3c-visserver-api/include/WsServer.hpp | 7 ++++--- w3c-visserver-api/src/AccessChecker.cpp | 12 +++++++++--- w3c-visserver-api/src/SigningHandler.cpp | 8 ++++---- w3c-visserver-api/src/SubscriptionHandler.cpp | 13 +++++++------ w3c-visserver-api/src/VssCommandProcessor.cpp | 2 +- w3c-visserver-api/src/VssDatabase.cpp | 12 +++++++----- w3c-visserver-api/src/WsServer.cpp | 4 ++-- 13 files changed, 68 insertions(+), 53 deletions(-) diff --git a/w3c-visserver-api/include/AccessChecker.hpp b/w3c-visserver-api/include/AccessChecker.hpp index 980acba..2b777e3 100644 --- a/w3c-visserver-api/include/AccessChecker.hpp +++ b/w3c-visserver-api/include/AccessChecker.hpp @@ -14,24 +14,19 @@ #ifndef __ACCESSCHECKER_H__ #define __ACCESSCHECKER_H__ -#include -#include +#include "IAccessChecker.hpp" #include "Authenticator.hpp" -#include "WsChannel.hpp" -using namespace std; -using namespace jsoncons; -using jsoncons::json; -class AccessChecker { +class AccessChecker : public IAccessChecker { private: Authenticator *tokenValidator; public: AccessChecker(Authenticator *vdator); - bool checkReadAccess(WsChannel &channel, string path); - bool checkWriteAccess(WsChannel &channel, string path); - bool checkPathWriteAccess(WsChannel &channel, json paths); + bool checkReadAccess(WsChannel &channel, const std::string &path); + bool checkWriteAccess(WsChannel &channel, const std::string &path); + bool checkPathWriteAccess(WsChannel &channel, const jsoncons::json &paths); }; #endif diff --git a/w3c-visserver-api/include/Authenticator.hpp b/w3c-visserver-api/include/Authenticator.hpp index 0214e69..a1612eb 100644 --- a/w3c-visserver-api/include/Authenticator.hpp +++ b/w3c-visserver-api/include/Authenticator.hpp @@ -33,6 +33,7 @@ class Authenticator { public: Authenticator(std::shared_ptr loggerUtil, string secretkey, string algorithm); + int validate(WsChannel &channel, VssDatabase *database, string authToken); diff --git a/w3c-visserver-api/include/SigningHandler.hpp b/w3c-visserver-api/include/SigningHandler.hpp index 243cc3e..4361185 100644 --- a/w3c-visserver-api/include/SigningHandler.hpp +++ b/w3c-visserver-api/include/SigningHandler.hpp @@ -22,13 +22,15 @@ #include +#include "ISigningHandler.hpp" + using namespace std; using namespace jsoncons; using namespace jwt; class ILogger; -class SigningHandler { +class SigningHandler : public ISigningHandler { private: std::shared_ptr logger; string key = ""; @@ -37,10 +39,10 @@ class SigningHandler { public: SigningHandler(std::shared_ptr loggerUtil); - string getKey(string fileName); - string getPublicKey(string fileName); - string sign(json data); - string sign(string data); + string getKey(const string &fileName); + string getPublicKey(const string &fileName); + string sign(const json &data); + string sign(const string &data); #ifdef UNIT_TEST string decode(string signedData); #endif diff --git a/w3c-visserver-api/include/SubscriptionHandler.hpp b/w3c-visserver-api/include/SubscriptionHandler.hpp index cb3631e..cbce2c2 100644 --- a/w3c-visserver-api/include/SubscriptionHandler.hpp +++ b/w3c-visserver-api/include/SubscriptionHandler.hpp @@ -17,13 +17,15 @@ #include #include #include -#include "visconf.hpp" #include #include #include #include +#include "visconf.hpp" +#include "ISubscriptionHandler.hpp" + class AccessChecker; class Authenticator; class VssDatabase; @@ -37,7 +39,7 @@ typedef std::unordered_map subscriptions_t; // Subscription UUID typedef std::string uuid_t; -class SubscriptionHandler { +class SubscriptionHandler : public ISubscriptionHandler { private: std::shared_ptr logger; std::unordered_map subscribeHandle; @@ -57,15 +59,15 @@ class SubscriptionHandler { ~SubscriptionHandler(); uint32_t subscribe(WsChannel& channel, VssDatabase* db, - uint32_t channelID, std::string path); + uint32_t channelID, const std::string &path); int unsubscribe(uint32_t subscribeID); int unsubscribeAll(uint32_t connectionID); - int updateByUUID(std::string signalUUID, jsoncons::json value); - int updateByPath(std::string path, jsoncons::json value); + int updateByUUID(const std::string &signalUUID, const jsoncons::json &value); + int updateByPath(const std::string &path, const jsoncons::json &value); WsServer* getServer(); int startThread(); int stopThread(); - bool isThreadRunning(); + bool isThreadRunning() const; void* subThreadRunner(); }; #endif diff --git a/w3c-visserver-api/include/VssCommandProcessor.hpp b/w3c-visserver-api/include/VssCommandProcessor.hpp index 13c5b4b..06116fa 100644 --- a/w3c-visserver-api/include/VssCommandProcessor.hpp +++ b/w3c-visserver-api/include/VssCommandProcessor.hpp @@ -19,6 +19,8 @@ #include +#include "IVssCommandProcessor.hpp" + class VssDatabase; class SubscriptionHandler; class Authenticator; @@ -26,7 +28,7 @@ class AccessChecker; class WsChannel; class ILogger; -class VssCommandProcessor { +class VssCommandProcessor : public IVssCommandProcessor { private: std::shared_ptr logger; VssDatabase* database = NULL; @@ -57,7 +59,7 @@ class VssCommandProcessor { SubscriptionHandler* subhandler); ~VssCommandProcessor(); - std::string processQuery(std::string req_json, WsChannel& channel); + std::string processQuery(const std::string &req_json, WsChannel& channel); }; #endif diff --git a/w3c-visserver-api/include/VssDatabase.hpp b/w3c-visserver-api/include/VssDatabase.hpp index 6bfee77..bb15edb 100644 --- a/w3c-visserver-api/include/VssDatabase.hpp +++ b/w3c-visserver-api/include/VssDatabase.hpp @@ -21,12 +21,14 @@ #include +#include "IVssDatabase.hpp" + class SubscriptionHandler; class AccessChecker; class WsChannel; class ILogger; -class VssDatabase { +class VssDatabase : public IVssDatabase { friend class SubscriptionHandler; friend class Authenticator; #ifdef UNIT_TEST @@ -52,11 +54,12 @@ class VssDatabase { SubscriptionHandler* subHandle, AccessChecker* accValidator); ~VssDatabase(); - void initJsonTree(std::string fileName); - jsoncons::json getMetaData(std::string path); - void setSignal(WsChannel& channel, std::string path, jsoncons::json value); - void setSignal(std::string path, jsoncons::json value); - jsoncons::json getSignal(WsChannel& channel, std::string path); + + void initJsonTree(const std::string &fileName); + jsoncons::json getMetaData(const std::string &path); + void setSignal(WsChannel& channel, const std::string &path, jsoncons::json value); + void setSignal(const std::string &path, jsoncons::json value); + jsoncons::json getSignal(WsChannel& channel, const std::string &path); }; #endif diff --git a/w3c-visserver-api/include/WsServer.hpp b/w3c-visserver-api/include/WsServer.hpp index 9054cce..1475d14 100644 --- a/w3c-visserver-api/include/WsServer.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -16,6 +16,7 @@ #define __WSSERVER_H__ #include "server_wss.hpp" +#include "IWsServer.hpp" class VssCommandProcessor; @@ -26,7 +27,7 @@ class VssDatabase; class AccessChecker; class ILogger; -class WsServer { +class WsServer : public IWsServer { private: SimpleWeb::SocketServer *secureServer_; SimpleWeb::SocketServer *insecureServer_; @@ -43,8 +44,8 @@ class WsServer { WsServer(std::shared_ptr loggerUtil, int port, std::string configFileName, bool secure); ~WsServer(); - void startServer(std::string endpointName); - void sendToConnection(uint32_t connID, std::string message); + void startServer(const std::string &endpointName); + void sendToConnection(uint32_t connID, const std::string &message); void start(); }; #endif diff --git a/w3c-visserver-api/src/AccessChecker.cpp b/w3c-visserver-api/src/AccessChecker.cpp index 54f97be..e908306 100644 --- a/w3c-visserver-api/src/AccessChecker.cpp +++ b/w3c-visserver-api/src/AccessChecker.cpp @@ -14,14 +14,20 @@ #include "AccessChecker.hpp" +#include +#include +#include "WsChannel.hpp" + using namespace std; +using namespace jsoncons; +//using jsoncons::json; AccessChecker::AccessChecker(Authenticator *vdator) { tokenValidator = vdator; } // check the permissions json in WsChannel if path has read access -bool AccessChecker::checkReadAccess(WsChannel &channel, string path) { +bool AccessChecker::checkReadAccess(WsChannel &channel, const string &path) { json permissions = channel.getPermissions(); string perm = permissions.get_with_default(path, ""); @@ -32,7 +38,7 @@ bool AccessChecker::checkReadAccess(WsChannel &channel, string path) { } // check the permissions json in WsChannel if path has write access -bool AccessChecker::checkWriteAccess(WsChannel &channel, string path) { +bool AccessChecker::checkWriteAccess(WsChannel &channel, const string &path) { json permissions = channel.getPermissions(); string perm = permissions.get_with_default(path, ""); @@ -44,7 +50,7 @@ bool AccessChecker::checkWriteAccess(WsChannel &channel, string path) { // Checks if all the paths have write access.If even 1 path in the list does not // have write access, this method returns false. -bool AccessChecker::checkPathWriteAccess(WsChannel &channel, json paths) { +bool AccessChecker::checkPathWriteAccess(WsChannel &channel, const json &paths) { for (size_t i = 0; i < paths.size(); i++) { json item = paths[i]; string jPath = item["path"].as(); diff --git a/w3c-visserver-api/src/SigningHandler.cpp b/w3c-visserver-api/src/SigningHandler.cpp index 3ba4ef3..e04ff00 100644 --- a/w3c-visserver-api/src/SigningHandler.cpp +++ b/w3c-visserver-api/src/SigningHandler.cpp @@ -30,7 +30,7 @@ SigningHandler::SigningHandler(std::shared_ptr loggerUtil) : logger(log /** Get the private key for SigningHandler. */ -string SigningHandler::getKey(string fileName) { +string SigningHandler::getKey(const string &fileName) { std::ifstream fileStream(fileName); std::string privatekey((std::istreambuf_iterator(fileStream)), (std::istreambuf_iterator())); @@ -41,7 +41,7 @@ string SigningHandler::getKey(string fileName) { /** Get the public key for SigningHandler. */ -string SigningHandler::getPublicKey(string fileName) { +string SigningHandler::getPublicKey(const string &fileName) { std::ifstream fileStream(fileName); std::string privatekey((std::istreambuf_iterator(fileStream)), (std::istreambuf_iterator())); @@ -52,7 +52,7 @@ string SigningHandler::getPublicKey(string fileName) { /** Signs the JSON and returns a string token */ -string SigningHandler::sign(json data) { +string SigningHandler::sign(const json &data) { auto algo = jwt::algorithm::rs256(pubkey, key, "", ""); auto encode = [](const std::string& data) { auto base = base::encode(data); @@ -76,7 +76,7 @@ string SigningHandler::sign(json data) { /** Signs the JSON and returns a string token */ -string SigningHandler::sign(string data) { +string SigningHandler::sign(const string &data) { auto algo = jwt::algorithm::rs256(pubkey, key, "", ""); auto encode = [](const std::string& data) { auto base = base::encode(data); diff --git a/w3c-visserver-api/src/SubscriptionHandler.cpp b/w3c-visserver-api/src/SubscriptionHandler.cpp index 735ff0c..4734cda 100644 --- a/w3c-visserver-api/src/SubscriptionHandler.cpp +++ b/w3c-visserver-api/src/SubscriptionHandler.cpp @@ -29,7 +29,7 @@ using namespace std; // using namespace jsoncons; using namespace jsoncons::jsonpath; -// using jsoncons::jsoncons::jsoncons::json; +using jsoncons::json; SubscriptionHandler::SubscriptionHandler(std::shared_ptr loggerUtil, WsServer* wserver, @@ -48,7 +48,7 @@ SubscriptionHandler::~SubscriptionHandler() { uint32_t SubscriptionHandler::subscribe(WsChannel& channel, VssDatabase* db, - uint32_t channelID, string path) { + uint32_t channelID, const string &path) { // generate subscribe ID "randomly". uint32_t subId = rand() % 9999999; // embed connection ID into subID. @@ -121,8 +121,9 @@ int SubscriptionHandler::unsubscribeAll(uint32_t connectionID) { return 0; } -int SubscriptionHandler::updateByUUID(string UUID, jsoncons::json value) { - auto handle = subscribeHandle.find(UUID); +int SubscriptionHandler::updateByUUID(const string &signalUUID, + const jsoncons::json &value) { + auto handle = subscribeHandle.find(signalUUID); if (handle == subscribeHandle.end()) { // UUID not found return 0; @@ -143,7 +144,7 @@ WsServer* SubscriptionHandler::getServer() { return server; } -int SubscriptionHandler::updateByPath(string path, json value) { +int SubscriptionHandler::updateByPath(const string &path, const json &value) { /* TODO: Implement */ (void) path; (void) value; @@ -208,4 +209,4 @@ int SubscriptionHandler::stopThread() { return 0; } -bool SubscriptionHandler::isThreadRunning() { return threadRun; } +bool SubscriptionHandler::isThreadRunning() const { return threadRun; } diff --git a/w3c-visserver-api/src/VssCommandProcessor.cpp b/w3c-visserver-api/src/VssCommandProcessor.cpp index 68e6035..f6b9bb3 100644 --- a/w3c-visserver-api/src/VssCommandProcessor.cpp +++ b/w3c-visserver-api/src/VssCommandProcessor.cpp @@ -401,7 +401,7 @@ string VssCommandProcessor::processAuthorize(WsChannel &channel, } } -string VssCommandProcessor::processQuery(string req_json, +string VssCommandProcessor::processQuery(const string &req_json, WsChannel &channel) { jsoncons::json root; string response; diff --git a/w3c-visserver-api/src/VssDatabase.cpp b/w3c-visserver-api/src/VssDatabase.cpp index 6668351..11acba3 100644 --- a/w3c-visserver-api/src/VssDatabase.cpp +++ b/w3c-visserver-api/src/VssDatabase.cpp @@ -26,6 +26,7 @@ using namespace std; using namespace jsoncons::jsonpath; +using jsoncons::json; namespace { // Check the value type and if the value is within the range @@ -177,7 +178,7 @@ VssDatabase::VssDatabase(std::shared_ptr loggerUtil, VssDatabase::~VssDatabase() {} // Initializer -void VssDatabase::initJsonTree(string fileName) { +void VssDatabase::initJsonTree(const string &fileName) { try { std::ifstream is(fileName); is >> data_tree; @@ -343,7 +344,7 @@ vector getVSSTokens(string path) { } // Returns the response JSON for metadata request. -jsoncons::json VssDatabase::getMetaData(string path) { +jsoncons::json VssDatabase::getMetaData(const std::string &path) { string format_path = "$"; bool isBranch = false; rwMutex.lock(); @@ -494,7 +495,8 @@ void VssDatabase::checkSetPermission(WsChannel& channel, jsoncons::json valueJso } // Method for setting values to signals. -void VssDatabase::setSignal(WsChannel& channel, string path, +void VssDatabase::setSignal(WsChannel& channel, + const string &path, jsoncons::json valueJson) { if (path == "") { string msg = "Path is empty while setting"; @@ -558,7 +560,7 @@ void VssDatabase::setSignal(WsChannel& channel, string path, } // Method for setting values to signals. -void VssDatabase::setSignal(string path, +void VssDatabase::setSignal(const string &path, jsoncons::json valueJson) { if (path == "") { string msg = "Path is empty while setting"; @@ -621,7 +623,7 @@ void VssDatabase::setSignal(string path, } // Returns response JSON for get request. -jsoncons::json VssDatabase::getSignal(class WsChannel& channel, string path) { +jsoncons::json VssDatabase::getSignal(class WsChannel& channel, const string &path) { bool isBranch = false; rwMutex.lock(); diff --git a/w3c-visserver-api/src/WsServer.cpp b/w3c-visserver-api/src/WsServer.cpp index ad3aad3..ebd8fd3 100644 --- a/w3c-visserver-api/src/WsServer.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -114,7 +114,7 @@ static void onMessage(std::weak_ptr wLogger, connection->send(send_stream); } -void WsServer::startServer(string endpointName) { +void WsServer::startServer(const string &endpointName) { (void) endpointName; auto wLogger = std::weak_ptr(logger); @@ -233,7 +233,7 @@ void WsServer::startServer(string endpointName) { } } -void WsServer::sendToConnection(uint32_t connectionID, string message) { +void WsServer::sendToConnection(uint32_t connectionID, const string &message) { if (isSecure_) { auto send_stream = make_shared(); *send_stream << message; From 33a4c8a35cc77e085a5203498124f5af96dbc26b Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 3 Oct 2019 15:02:09 +0200 Subject: [PATCH 12/32] Replaced most raw pointers with std::shared_ptr - Now classes accept interfaces only of their existing dependencies. - Some interfaces and implementations will be refactored to remove access to more internal values (e.g. friend classes use-cases). - Removed un-used dependencies from WsServer - Left raw pointers will be refactored later - Updated test init - Interface commenting will be done later when they are more stable Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/include/AccessChecker.hpp | 7 +-- w3c-visserver-api/include/Authenticator.hpp | 11 +++-- w3c-visserver-api/include/IAuthenticator.hpp | 7 +-- .../include/{IWsServer.hpp => IServer.hpp} | 4 +- .../include/ISubscriptionHandler.hpp | 8 ++-- w3c-visserver-api/include/IVssDatabase.hpp | 7 +++ .../include/SubscriptionHandler.hpp | 23 +++++---- .../include/VssCommandProcessor.hpp | 27 ++++++----- w3c-visserver-api/include/VssDatabase.hpp | 24 +++++----- w3c-visserver-api/include/WsServer.hpp | 28 +++++------ w3c-visserver-api/src/AccessChecker.cpp | 4 +- w3c-visserver-api/src/Authenticator.cpp | 6 +-- w3c-visserver-api/src/SubscriptionHandler.cpp | 13 ++--- w3c-visserver-api/src/VssCommandProcessor.cpp | 20 +++++--- w3c-visserver-api/src/VssDatabase.cpp | 40 ++++++++-------- w3c-visserver-api/src/WsServer.cpp | 47 ++++++++++--------- w3c-visserver-api/src/main.cpp | 36 ++++++++++++-- w3c-visserver-api/unit-test/w3cunittest.cpp | 37 ++++++++------- 18 files changed, 204 insertions(+), 145 deletions(-) rename w3c-visserver-api/include/{IWsServer.hpp => IServer.hpp} (94%) diff --git a/w3c-visserver-api/include/AccessChecker.hpp b/w3c-visserver-api/include/AccessChecker.hpp index 2b777e3..4478e87 100644 --- a/w3c-visserver-api/include/AccessChecker.hpp +++ b/w3c-visserver-api/include/AccessChecker.hpp @@ -15,15 +15,16 @@ #define __ACCESSCHECKER_H__ #include "IAccessChecker.hpp" -#include "Authenticator.hpp" + +class IAuthenticator; class AccessChecker : public IAccessChecker { private: - Authenticator *tokenValidator; + std::shared_ptr tokenValidator; public: - AccessChecker(Authenticator *vdator); + AccessChecker(std::shared_ptr vdator); bool checkReadAccess(WsChannel &channel, const std::string &path); bool checkWriteAccess(WsChannel &channel, const std::string &path); bool checkPathWriteAccess(WsChannel &channel, const jsoncons::json &paths); diff --git a/w3c-visserver-api/include/Authenticator.hpp b/w3c-visserver-api/include/Authenticator.hpp index a1612eb..89792d3 100644 --- a/w3c-visserver-api/include/Authenticator.hpp +++ b/w3c-visserver-api/include/Authenticator.hpp @@ -17,13 +17,15 @@ #include #include +#include "IAuthenticator.hpp" + using namespace std; class WsChannel; -class VssDatabase; +class IVssDatabase; class ILogger; -class Authenticator { +class Authenticator : public IAuthenticator { private: string pubkey = "secret"; string algorithm = "RS256"; @@ -34,11 +36,12 @@ class Authenticator { public: Authenticator(std::shared_ptr loggerUtil, string secretkey, string algorithm); - int validate(WsChannel &channel, VssDatabase *database, + int validate(WsChannel &channel, + std::shared_ptr dn, string authToken); void updatePubKey(string key); bool isStillValid(WsChannel &channel); - void resolvePermissions(WsChannel &channel, VssDatabase *database); + void resolvePermissions(WsChannel &channel, std::shared_ptr database); }; #endif diff --git a/w3c-visserver-api/include/IAuthenticator.hpp b/w3c-visserver-api/include/IAuthenticator.hpp index 5d4ad72..73896df 100644 --- a/w3c-visserver-api/include/IAuthenticator.hpp +++ b/w3c-visserver-api/include/IAuthenticator.hpp @@ -20,7 +20,7 @@ using namespace std; class WsChannel; -class VssDatabase; +class IVssDatabase; class ILogger; class IAuthenticator { @@ -28,10 +28,11 @@ class IAuthenticator { virtual ~IAuthenticator() {} virtual int validate(WsChannel &channel, - VssDatabase *database, + std::shared_ptr database, string authToken) = 0; + virtual void updatePubKey(string key) = 0; virtual bool isStillValid(WsChannel &channel) = 0; - virtual void resolvePermissions(WsChannel &channel, VssDatabase *database) = 0; + virtual void resolvePermissions(WsChannel &channel, std::shared_ptr database) = 0; }; #endif diff --git a/w3c-visserver-api/include/IWsServer.hpp b/w3c-visserver-api/include/IServer.hpp similarity index 94% rename from w3c-visserver-api/include/IWsServer.hpp rename to w3c-visserver-api/include/IServer.hpp index ad9e986..47d4529 100644 --- a/w3c-visserver-api/include/IWsServer.hpp +++ b/w3c-visserver-api/include/IServer.hpp @@ -18,9 +18,9 @@ #include -class IWsServer { +class IServer { public: - virtual ~IWsServer() {} + virtual ~IServer() {} virtual void startServer(const std::string &endpointName) = 0; virtual void sendToConnection(uint32_t connID, const std::string &message) = 0; diff --git a/w3c-visserver-api/include/ISubscriptionHandler.hpp b/w3c-visserver-api/include/ISubscriptionHandler.hpp index 74725c0..98ae6d6 100644 --- a/w3c-visserver-api/include/ISubscriptionHandler.hpp +++ b/w3c-visserver-api/include/ISubscriptionHandler.hpp @@ -15,26 +15,28 @@ #define __ISUBSCRIPTIONHANDLER_H__ #include +#include #include class VssDatabase; class WsChannel; class WsServer; -class ILogger; +class IVssDatabase; +class IServer; class ISubscriptionHandler { public: virtual ~ISubscriptionHandler() {} virtual uint32_t subscribe(WsChannel& channel, - VssDatabase* db, + std::shared_ptr db, uint32_t channelID, const std::string &path) = 0; virtual int unsubscribe(uint32_t subscribeID) = 0; virtual int unsubscribeAll(uint32_t connectionID) = 0; virtual int updateByUUID(const std::string &signalUUID, const jsoncons::json &value) = 0; virtual int updateByPath(const std::string &path, const jsoncons::json &value) = 0; - virtual WsServer* getServer() = 0; + virtual std::shared_ptr getServer() = 0; virtual int startThread() = 0; virtual int stopThread() = 0; virtual bool isThreadRunning() const = 0; diff --git a/w3c-visserver-api/include/IVssDatabase.hpp b/w3c-visserver-api/include/IVssDatabase.hpp index 0304ba4..1adb0d6 100644 --- a/w3c-visserver-api/include/IVssDatabase.hpp +++ b/w3c-visserver-api/include/IVssDatabase.hpp @@ -30,6 +30,13 @@ class IVssDatabase { const std::string &path, jsoncons::json value) = 0; virtual jsoncons::json getSignal(WsChannel& channel, const std::string &path) = 0; + + // TODO: temporary added while components are refactored + jsoncons::json data_tree; + jsoncons::json meta_tree; + virtual std::list getPathForGet(const std::string &path, bool& isBranch) = 0; + virtual std::string getVSSSpecificPath(const std::string &path, bool& isBranch, jsoncons::json& tree) = 0; + virtual jsoncons::json getPathForSet(const std::string &path, jsoncons::json value) = 0; }; #endif diff --git a/w3c-visserver-api/include/SubscriptionHandler.hpp b/w3c-visserver-api/include/SubscriptionHandler.hpp index cbce2c2..987cd83 100644 --- a/w3c-visserver-api/include/SubscriptionHandler.hpp +++ b/w3c-visserver-api/include/SubscriptionHandler.hpp @@ -25,6 +25,9 @@ #include "visconf.hpp" #include "ISubscriptionHandler.hpp" +#include "IAuthenticator.hpp" +#include "IAccessChecker.hpp" +#include "IServer.hpp" class AccessChecker; class Authenticator; @@ -43,9 +46,9 @@ class SubscriptionHandler : public ISubscriptionHandler { private: std::shared_ptr logger; std::unordered_map subscribeHandle; - WsServer* server; - Authenticator* validator; - AccessChecker* checkAccess; + std::shared_ptr server; + std::shared_ptr validator; + std::shared_ptr checkAccess; std::mutex subMutex; std::thread subThread; bool threadRun; @@ -53,18 +56,20 @@ class SubscriptionHandler : public ISubscriptionHandler { public: SubscriptionHandler(std::shared_ptr loggerUtil, - WsServer* wserver, - Authenticator* authenticate, - AccessChecker* checkAccess); + std::shared_ptr wserver, + std::shared_ptr authenticate, + std::shared_ptr checkAccess); ~SubscriptionHandler(); - uint32_t subscribe(WsChannel& channel, VssDatabase* db, - uint32_t channelID, const std::string &path); + uint32_t subscribe(WsChannel& channel, + std::shared_ptr db, + uint32_t channelID, + const std::string &path); int unsubscribe(uint32_t subscribeID); int unsubscribeAll(uint32_t connectionID); int updateByUUID(const std::string &signalUUID, const jsoncons::json &value); int updateByPath(const std::string &path, const jsoncons::json &value); - WsServer* getServer(); + std::shared_ptr getServer(); int startThread(); int stopThread(); bool isThreadRunning() const; diff --git a/w3c-visserver-api/include/VssCommandProcessor.hpp b/w3c-visserver-api/include/VssCommandProcessor.hpp index 06116fa..9e78d49 100644 --- a/w3c-visserver-api/include/VssCommandProcessor.hpp +++ b/w3c-visserver-api/include/VssCommandProcessor.hpp @@ -21,22 +21,23 @@ #include "IVssCommandProcessor.hpp" -class VssDatabase; -class SubscriptionHandler; -class Authenticator; -class AccessChecker; -class WsChannel; +class IVssDatabase; +class ISubscriptionHandler; +class IAuthenticator; +class IAccessChecker; class ILogger; +class WsChannel; + class VssCommandProcessor : public IVssCommandProcessor { private: std::shared_ptr logger; - VssDatabase* database = NULL; - SubscriptionHandler* subHandler = NULL; - Authenticator* tokenValidator = NULL; - AccessChecker* accessValidator = NULL; + std::shared_ptr database; + std::shared_ptr subHandler; + std::shared_ptr tokenValidator; + std::shared_ptr accessValidator; #ifdef JSON_SIGNING_ON - SigningHandler* signer = NULL; + std::shared_ptr signer; #endif std::string processGet(WsChannel& channel, uint32_t request_id, std::string path); @@ -54,9 +55,9 @@ class VssCommandProcessor : public IVssCommandProcessor { public: VssCommandProcessor(std::shared_ptr loggerUtil, - VssDatabase* database, - Authenticator* vdator, - SubscriptionHandler* subhandler); + std::shared_ptr database, + std::shared_ptr vdator, + std::shared_ptr subhandler); ~VssCommandProcessor(); std::string processQuery(const std::string &req_json, WsChannel& channel); diff --git a/w3c-visserver-api/include/VssDatabase.hpp b/w3c-visserver-api/include/VssDatabase.hpp index bb15edb..edeac92 100644 --- a/w3c-visserver-api/include/VssDatabase.hpp +++ b/w3c-visserver-api/include/VssDatabase.hpp @@ -23,14 +23,12 @@ #include "IVssDatabase.hpp" -class SubscriptionHandler; -class AccessChecker; class WsChannel; +class IAccessChecker; +class ISubscriptionHandler; class ILogger; class VssDatabase : public IVssDatabase { - friend class SubscriptionHandler; - friend class Authenticator; #ifdef UNIT_TEST friend class w3cunittest; #endif @@ -38,21 +36,17 @@ class VssDatabase : public IVssDatabase { private: std::shared_ptr logger; std::mutex rwMutex; - jsoncons::json data_tree; - jsoncons::json meta_tree; - SubscriptionHandler* subHandler; - AccessChecker* accessValidator; - std::string getVSSSpecificPath(std::string path, bool& isBranch, jsoncons::json& tree); + + std::shared_ptr subHandler; + std::shared_ptr accessValidator; std::string getPathForMetadata(std::string path, bool& isBranch); - std::list getPathForGet(std::string path, bool& isBranch); - jsoncons::json getPathForSet(std::string path, jsoncons::json value); std::string getReadablePath(std::string jsonpath); void checkSetPermission(WsChannel& channel, jsoncons::json valueJson); public: VssDatabase(std::shared_ptr loggerUtil, - SubscriptionHandler* subHandle, - AccessChecker* accValidator); + std::shared_ptr subHandle, + std::shared_ptr accValidator); ~VssDatabase(); void initJsonTree(const std::string &fileName); @@ -61,5 +55,9 @@ class VssDatabase : public IVssDatabase { void setSignal(const std::string &path, jsoncons::json value); jsoncons::json getSignal(WsChannel& channel, const std::string &path); + std::list getPathForGet(const std::string &path, bool& isBranch); + std::string getVSSSpecificPath(const std::string &path, bool& isBranch, + jsoncons::json& tree); + jsoncons::json getPathForSet(const std::string &path, jsoncons::json value); }; #endif diff --git a/w3c-visserver-api/include/WsServer.hpp b/w3c-visserver-api/include/WsServer.hpp index 1475d14..be283db 100644 --- a/w3c-visserver-api/include/WsServer.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -15,34 +15,32 @@ #ifndef __WSSERVER_H__ #define __WSSERVER_H__ +#include + #include "server_wss.hpp" -#include "IWsServer.hpp" + +#include "IServer.hpp" -class VssCommandProcessor; -class VssCommandProcessor; -class SubscriptionHandler; -class Authenticator; -class VssDatabase; -class AccessChecker; +class IVssCommandProcessor; class ILogger; -class WsServer : public IWsServer { +class WsServer : public IServer { private: SimpleWeb::SocketServer *secureServer_; SimpleWeb::SocketServer *insecureServer_; std::shared_ptr logger; bool isSecure_; - std::string configFileName_; + bool isInitialized_; public: - VssCommandProcessor* cmdProcessor; - SubscriptionHandler* subHandler; - Authenticator* tokenValidator; - VssDatabase* database; - AccessChecker* accessCheck; + std::shared_ptr cmdProcessor; - WsServer(std::shared_ptr loggerUtil, int port, std::string configFileName, bool secure); + WsServer(); + bool Initialize(std::shared_ptr loggerUtil, + std::shared_ptr processor, + bool secure, + int port); ~WsServer(); void startServer(const std::string &endpointName); void sendToConnection(uint32_t connID, const std::string &message); diff --git a/w3c-visserver-api/src/AccessChecker.cpp b/w3c-visserver-api/src/AccessChecker.cpp index e908306..fed60b0 100644 --- a/w3c-visserver-api/src/AccessChecker.cpp +++ b/w3c-visserver-api/src/AccessChecker.cpp @@ -16,13 +16,15 @@ #include #include + +#include "IAuthenticator.hpp" #include "WsChannel.hpp" using namespace std; using namespace jsoncons; //using jsoncons::json; -AccessChecker::AccessChecker(Authenticator *vdator) { +AccessChecker::AccessChecker(std::shared_ptr vdator) { tokenValidator = vdator; } diff --git a/w3c-visserver-api/src/Authenticator.cpp b/w3c-visserver-api/src/Authenticator.cpp index 3a251e0..705e811 100644 --- a/w3c-visserver-api/src/Authenticator.cpp +++ b/w3c-visserver-api/src/Authenticator.cpp @@ -50,7 +50,7 @@ int Authenticator::validateToken(WsChannel& channel, string authToken) { json claims; (void) channel; for (auto& e : decoded.get_payload_claims()) { - logger->Log(LogLevel::INFO, e.first + " = " + e.second.as_string()); + logger->Log(LogLevel::INFO, e.first + " = " + e.second.to_json().to_str()); claims[e.first] = e.second.to_json().to_str(); } @@ -78,7 +78,7 @@ Authenticator::Authenticator(std::shared_ptr loggerUtil, string secretk // validates the token against expiry date/time. should be extended to check // some other claims. -int Authenticator::validate(WsChannel& channel, VssDatabase* db, +int Authenticator::validate(WsChannel& channel, std::shared_ptr db, string authToken) { int ttl = validateToken(channel, authToken); if (ttl > 0) { @@ -107,7 +107,7 @@ bool Authenticator::isStillValid(WsChannel& channel) { // resolves the permission in the JWT token and store the absolute path to the // signals in permissions JSON in WsChannel. void Authenticator::resolvePermissions(WsChannel& channel, - VssDatabase* database) { + std::shared_ptr database) { string authToken = channel.getAuthToken(); auto decoded = jwt::decode(authToken); json claims; diff --git a/w3c-visserver-api/src/SubscriptionHandler.cpp b/w3c-visserver-api/src/SubscriptionHandler.cpp index 4734cda..11d0b77 100644 --- a/w3c-visserver-api/src/SubscriptionHandler.cpp +++ b/w3c-visserver-api/src/SubscriptionHandler.cpp @@ -32,9 +32,9 @@ using namespace jsoncons::jsonpath; using jsoncons::json; SubscriptionHandler::SubscriptionHandler(std::shared_ptr loggerUtil, - WsServer* wserver, - Authenticator* authenticate, - AccessChecker* checkAcc) { + std::shared_ptr wserver, + std::shared_ptr authenticate, + std::shared_ptr checkAcc) { logger = loggerUtil; server = wserver; validator = authenticate; @@ -47,8 +47,9 @@ SubscriptionHandler::~SubscriptionHandler() { } uint32_t SubscriptionHandler::subscribe(WsChannel& channel, - VssDatabase* db, - uint32_t channelID, const string &path) { + std::shared_ptr db, + uint32_t channelID, + const string &path) { // generate subscribe ID "randomly". uint32_t subId = rand() % 9999999; // embed connection ID into subID. @@ -140,7 +141,7 @@ int SubscriptionHandler::updateByUUID(const string &signalUUID, return 0; } -WsServer* SubscriptionHandler::getServer() { +std::shared_ptr SubscriptionHandler::getServer() { return server; } diff --git a/w3c-visserver-api/src/VssCommandProcessor.cpp b/w3c-visserver-api/src/VssCommandProcessor.cpp index f6b9bb3..b3ad0c9 100644 --- a/w3c-visserver-api/src/VssCommandProcessor.cpp +++ b/w3c-visserver-api/src/VssCommandProcessor.cpp @@ -27,6 +27,9 @@ #include "AccessChecker.hpp" #include "SubscriptionHandler.hpp" #include "ILogger.hpp" +#include "IVssDatabase.hpp" +#include "IAuthenticator.hpp" +#include "ISubscriptionHandler.hpp" #ifdef JSON_SIGNING_ON #include "SigningHandler.hpp" @@ -114,21 +117,26 @@ string valueOutOfBoundsResponse(uint32_t request_id, const string action, VssCommandProcessor::VssCommandProcessor( std::shared_ptr loggerUtil, - VssDatabase *dbase, - Authenticator *vdator, - SubscriptionHandler *subhandler) { + std::shared_ptr dbase, + std::shared_ptr vdator, + std::shared_ptr subhandler) { logger = loggerUtil; database = dbase; tokenValidator = vdator; subHandler = subhandler; - accessValidator = new AccessChecker(tokenValidator); + // TODO: add accessValidator as dependency + accessValidator = std::make_shared(tokenValidator); #ifdef JSON_SIGNING_ON - signer = new SigningHandler(); + // TODO: add signer as dependency + signer = std::make_shared(); #endif } VssCommandProcessor::~VssCommandProcessor() { - delete accessValidator; + accessValidator.reset(); +#ifdef JSON_SIGNING_ON + signer.reset(); +#endif } string VssCommandProcessor::processGet(WsChannel &channel, diff --git a/w3c-visserver-api/src/VssDatabase.cpp b/w3c-visserver-api/src/VssDatabase.cpp index 11acba3..11c25c8 100644 --- a/w3c-visserver-api/src/VssDatabase.cpp +++ b/w3c-visserver-api/src/VssDatabase.cpp @@ -11,18 +11,18 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#include "VssDatabase.hpp" + +#include #include #include #include + #include "exception.hpp" #include "visconf.hpp" #include "ILogger.hpp" - -#include - -#include "AccessChecker.hpp" -#include "SubscriptionHandler.hpp" +#include "IAccessChecker.hpp" +#include "ISubscriptionHandler.hpp" +#include "VssDatabase.hpp" using namespace std; using namespace jsoncons::jsonpath; @@ -168,8 +168,8 @@ namespace { // Constructor VssDatabase::VssDatabase(std::shared_ptr loggerUtil, - SubscriptionHandler* subHandle, - AccessChecker* accValidator) { + std::shared_ptr subHandle, + std::shared_ptr accValidator) { logger = loggerUtil; subHandler = subHandle; accessValidator = accValidator; @@ -235,7 +235,7 @@ string VssDatabase::getReadablePath(string jsonpath) { // Eg: path = Signal.OBD.RPM // The method returns $['Signal']['children']['OBD']['children']['RPM'] and // updates isBranch to false. -string VssDatabase::getVSSSpecificPath(string path, bool& isBranch, +string VssDatabase::getVSSSpecificPath(const string &path, bool& isBranch, jsoncons::json& tree) { vector tokens = getPathTokens(path); int tokLength = tokens.size(); @@ -297,7 +297,7 @@ string VssDatabase::getPathForMetadata(string path, bool& isBranch) { // For eg : path = Signal.*.RPM // The method would return a list containing 1 signal path = // $['Signal']['children']['OBD']['children']['RPM'] -list VssDatabase::getPathForGet(string path, bool& isBranch) { +list VssDatabase::getPathForGet(const string &path, bool& isBranch) { list paths; string format_path = getVSSSpecificPath(path, isBranch, data_tree); jsoncons::json pathRes = json_query(data_tree, format_path, result_type::path); @@ -423,18 +423,20 @@ jsoncons::json VssDatabase::getMetaData(const std::string &path) { // The function would return = {{"path" : "$.Signal.children.OBD.children.RPM", // "value" : 23}, {"path" : "$.Signal.children.OBD.children.Speed", "value" : // 10}} -jsoncons::json VssDatabase::getPathForSet(string path, jsoncons::json values) { +jsoncons::json VssDatabase::getPathForSet(const string &path, jsoncons::json values) { jsoncons::json setValues; + string updatedPath = path; + if (values.is_array()) { - std::size_t found = path.find("*"); + std::size_t found = updatedPath.find("*"); if (found == std::string::npos) { - path = path + "."; - found = path.length(); + updatedPath = updatedPath + "."; + found = updatedPath.length(); } setValues = jsoncons::json::make_array(values.size()); for (size_t i = 0; i < values.size(); i++) { jsoncons::json value = values[i]; - string readablePath = path; + string readablePath = updatedPath; string replaceString; auto iter = value.object_range(); @@ -446,7 +448,7 @@ jsoncons::json VssDatabase::getPathForSet(string path, jsoncons::json values) { } } else { stringstream msg; - msg << "More than 1 signal found while setting for path = " << path + msg << "More than 1 signal found while setting for path = " << updatedPath << " with values " << pretty_print(values); throw genException(msg.str()); } @@ -457,7 +459,7 @@ jsoncons::json VssDatabase::getPathForSet(string path, jsoncons::json values) { getVSSSpecificPath(readablePath, isBranch, data_tree); if (isBranch) { stringstream msg; - msg << "Path = " << path << " with values " << pretty_print(values) + msg << "Path = " << updatedPath << " with values " << pretty_print(values) << " points to a branch. Needs to point to a signal"; throw genException(msg.str()); } @@ -470,10 +472,10 @@ jsoncons::json VssDatabase::getPathForSet(string path, jsoncons::json values) { setValues = jsoncons::json::make_array(1); jsoncons::json pathValue; bool isBranch = false; - string absolutePath = getVSSSpecificPath(path, isBranch, data_tree); + string absolutePath = getVSSSpecificPath(updatedPath, isBranch, data_tree); if (isBranch) { stringstream msg; - msg << "Path = " << path << " with values " << pretty_print(values) + msg << "Path = " << updatedPath << " with values " << pretty_print(values) << " points to a branch. Needs to point to a signal"; throw genException(msg.str()); } diff --git a/w3c-visserver-api/src/WsServer.cpp b/w3c-visserver-api/src/WsServer.cpp index ebd8fd3..5993af2 100644 --- a/w3c-visserver-api/src/WsServer.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -14,14 +14,11 @@ #include "WsServer.hpp" #include "ILogger.hpp" -#include "AccessChecker.hpp" -#include "Authenticator.hpp" -#include "SubscriptionHandler.hpp" +#include "IVssCommandProcessor.hpp" #include "visconf.hpp" -#include "VssCommandProcessor.hpp" -#include "VssDatabase.hpp" #include +#include using namespace std; @@ -29,7 +26,7 @@ using SecuredServer = SimpleWeb::SocketServer; using SimpleServer = SimpleWeb::SocketServer; uint16_t connections[MAX_CLIENTS + 1] = {0}; -WsServer *wserver; +WsServer *wserver = NULL; uint32_t generateConnID() { uint32_t retValueValue = 0; @@ -43,12 +40,26 @@ uint32_t generateConnID() { return retValueValue; } -WsServer::WsServer(std::shared_ptr loggerUtil, int port, string configFileName, bool secure) { - logger = loggerUtil; - isSecure_ = secure; +WsServer::WsServer() +{ + isSecure_ = false; secureServer_ = nullptr; insecureServer_ = nullptr; - configFileName_ = configFileName; + isInitialized_ = false; + + wserver = this; +} + +bool WsServer::Initialize(std::shared_ptr loggerUtil, + std::shared_ptr processor, + bool secure, + int port) +{ + logger = loggerUtil; + cmdProcessor = processor; + isSecure_ = secure; + + // TODO: pass server instance as dependency if (isSecure_) { secureServer_ = new SecuredServer("Server.pem", "Server.key"); @@ -57,21 +68,14 @@ WsServer::WsServer(std::shared_ptr loggerUtil, int port, string configF insecureServer_ = new SimpleServer(); insecureServer_->config.port = port; } + isInitialized_ = true; - tokenValidator = new Authenticator(logger, "appstacle", "RS256"); - accessCheck = new AccessChecker(tokenValidator); - subHandler = new SubscriptionHandler(logger, this, tokenValidator, accessCheck); - database = new VssDatabase(logger, subHandler, accessCheck); - cmdProcessor = new VssCommandProcessor(logger, database, tokenValidator, subHandler); - wserver = this; + return isInitialized_; } WsServer::~WsServer() { - delete cmdProcessor; - delete database; - delete subHandler; - delete accessCheck; - delete tokenValidator; + cmdProcessor.reset(); + if (secureServer_) { delete secureServer_; } @@ -265,7 +269,6 @@ void *startWSServer(void *arg) { } void WsServer::start() { - this->database->initJsonTree(configFileName_); pthread_t startWSServer_thread; /* create the web socket server thread. */ diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index 3a6f27a..2f39209 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -25,6 +25,13 @@ #include "exception.hpp" #include "BasicLogger.hpp" +#include "Authenticator.hpp" +#include "AccessChecker.hpp" +#include "SubscriptionHandler.hpp" +#include "VssCommandProcessor.hpp" +#include "VssDatabase.hpp" + +#include "WsServer.hpp" using namespace std; @@ -281,11 +288,30 @@ int main(int argc, const char *argv[]) { #else logLevelsActive = static_cast(LogLevel::INFO & LogLevel::WARNING & LogLevel::ERROR); #endif - std::shared_ptr logger = std::make_shared(logLevelsActive); - - WsServer server(logger, port, vss_filename, secure); - server.start(); - + auto server = std::make_shared(); + auto logger = std::make_shared(logLevelsActive); + auto tokenValidator = std::make_shared(logger, "appstacle", "RS256"); + auto accessCheck = std::make_shared(tokenValidator); + auto subHandler = std::make_shared(logger, server, tokenValidator, accessCheck); + auto database = std::make_shared(logger, subHandler, accessCheck); + auto cmdProcessor = std::make_shared(logger, database, tokenValidator, subHandler); + + if (server->Initialize(logger, + cmdProcessor, + secure, + port)) + { + database->initJsonTree(vss_filename); + server->start(); + + while (1) { + usleep(1000000); + }; + } + else + { + cerr << "Failed to initialize server" << std::endl; + } } catch (const program_options::error &ex) { print_usage(argv[0], desc); cerr << ex.what() << std::endl; diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index c92fead..164297a 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -62,33 +62,34 @@ namespace bt = boost::unit_test; #define PORT 8090 std::shared_ptr logger; -WsServer* webSocket; -SubscriptionHandler* subhandler; -Authenticator* authhandler; -AccessChecker* accesshandler; -VssDatabase* database; -SigningHandler* json_signer; -VssCommandProcessor* commandProc; +std::shared_ptr webSocket; +std::shared_ptr subhandler; +std::shared_ptr authhandler; +std::shared_ptr accesshandler; +std::shared_ptr database; +std::shared_ptr json_signer; +std::shared_ptr commandProc; w3cunittest unittestObj(false); w3cunittest::w3cunittest(bool secure) { + webSocket = std::make_shared(); logger = std::make_shared(static_cast(LogLevel::ALL)); - webSocket = new WsServer(logger, PORT, "vss_rel_2.0.json", secure); - authhandler = new Authenticator(logger, "",""); - accesshandler = new AccessChecker(authhandler); - subhandler = new SubscriptionHandler(logger, webSocket, authhandler, accesshandler); - database = new VssDatabase(logger, subhandler, accesshandler); - commandProc = new VssCommandProcessor(logger, database, authhandler , subhandler); - json_signer = new SigningHandler(logger); + authhandler = std::make_shared(logger, "",""); + accesshandler = std::make_shared(authhandler); + subhandler = std::make_shared(logger, webSocket, authhandler, accesshandler); + database = std::make_shared(logger, subhandler, accesshandler); + commandProc = std::make_shared(logger, database, authhandler , subhandler); + json_signer = std::make_shared(logger); database->initJsonTree("vss_rel_2.0.json"); + + webSocket->Initialize(logger, + commandProc, + secure, + PORT); } w3cunittest::~w3cunittest() { - // delete json_signer; - // delete database; - // delete webSocket; - // delete commandProc; } list w3cunittest::test_wrap_getPathForGet(string path , bool &isBranch) { From d5867faba116babe8938e0c6ca9c4347f7cfdf41 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Fri, 11 Oct 2019 12:46:30 +0200 Subject: [PATCH 13/32] Added implementation of Web-Socket and HTTP combined server Implementation based on Boost.Beast examples - Web-Socket fully works and can replace exiting WVV server - HTTP part works, and will be updated later to support REST API handling - Added SSL helper classes from Boost.Beast examples Signed-off-by: Miladinovic Bojan --- .../include/WebSockHttpFlexServer.hpp | 81 + w3c-visserver-api/include/detect_ssl.hpp | 463 +++++ w3c-visserver-api/include/ssl_stream.hpp | 325 ++++ .../src/WebSockHttpFlexServer.cpp | 1711 +++++++++++++++++ 4 files changed, 2580 insertions(+) create mode 100644 w3c-visserver-api/include/WebSockHttpFlexServer.hpp create mode 100644 w3c-visserver-api/include/detect_ssl.hpp create mode 100644 w3c-visserver-api/include/ssl_stream.hpp create mode 100644 w3c-visserver-api/src/WebSockHttpFlexServer.cpp diff --git a/w3c-visserver-api/include/WebSockHttpFlexServer.hpp b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp new file mode 100644 index 0000000..f29e75f --- /dev/null +++ b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp @@ -0,0 +1,81 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __WEBSOCKHTTPFLEXSERVER_H__ +#define __WEBSOCKHTTPFLEXSERVER_H__ + +#include "IServer.hpp" + +#include +#include +#include +#include + +class WsChannel; +class ILogger; + +/** + * \class WebSockHttpFlexServer + * \brief Combined Web-socket and HTTP server for both plain and SSL connections + * Implementation of \ref IServer interface, based on Boost.Beast library + * and its flex example code + */ +class WebSockHttpFlexServer : public IServer { + private: + std::vector>> listeners_; + std::mutex mutex_; + std::shared_ptr logger_; + + const uint8_t NumOfThreads = 1; + bool isInitialized = false; + + /** + * @brief Load server SSL certificates + * @param ctx ssl context to which certificates will be added + * @note 'Server.pem' and 'Server.key' needs to be located with executable + */ + void LoadCertData(boost::asio::ssl::context& ctx); + /** + * @brief Handle incoming data requests + * @param req_json Request message from connection + * @param channel Connection identifier + * @return Response JSON message for client + */ + std::string HandleRequest(const std::string &req_json, WsChannel &channel); + public: + explicit WebSockHttpFlexServer(std::shared_ptr loggerUtil); + ~WebSockHttpFlexServer(); + + /** + * @brief Initialize Boost.Beast server + * @param host Hostname for server connection + * @param port Port where to wait for server connections + * @param allowInsecure If true, plain connections are allowed, otherwise SSL is mandatory + */ + void Initialize(std::string host, int port, bool allowInsecure = false); + /** + * @brief Start server + * Server needs to be initialized before is started + */ + void Start(); + + // IServer + + void AddListener(ObserverType type, std::shared_ptr listener); + void RemoveListener(ObserverType type, std::shared_ptr listener); + void SendToConnection(uint64_t connID, const std::string &message); +}; + + + +#endif /* __WEBSOCKHTTPFLEXSERVER_H__ */ diff --git a/w3c-visserver-api/include/detect_ssl.hpp b/w3c-visserver-api/include/detect_ssl.hpp new file mode 100644 index 0000000..6fd1d65 --- /dev/null +++ b/w3c-visserver-api/include/detect_ssl.hpp @@ -0,0 +1,463 @@ +// +// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_EXAMPLE_COMMON_DETECT_SSL_HPP +#define BOOST_BEAST_EXAMPLE_COMMON_DETECT_SSL_HPP + +#include +#include + +//------------------------------------------------------------------------------ +// +// Example: Detect TLS/SSL +// +//------------------------------------------------------------------------------ + +//[example_core_detect_ssl_1 + +#include +#include +#include + +/** Return `true` if a buffer contains a TLS/SSL client handshake. + + This function returns `true` if the beginning of the buffer + indicates that a TLS handshake is being negotiated, and that + there are at least four octets in the buffer. + + If the content of the buffer cannot possibly be a TLS handshake + request, the function returns `false`. Otherwise, if additional + octets are required, `boost::indeterminate` is returned. + + @param buffer The input buffer to inspect. This type must meet + the requirements of @b ConstBufferSequence. + + @return `boost::tribool` indicating whether the buffer contains + a TLS client handshake, does not contain a handshake, or needs + additional octets. + + @see + + http://www.ietf.org/rfc/rfc2246.txt + 7.4. Handshake protocol +*/ +template +boost::tribool +is_ssl_handshake(ConstBufferSequence const& buffers); + +//] + +//[example_core_detect_ssl_2 + +template< + class ConstBufferSequence> +boost::tribool +is_ssl_handshake( + ConstBufferSequence const& buffers) +{ + // Make sure buffers meets the requirements + static_assert( + boost::asio::is_const_buffer_sequence::value, + "ConstBufferSequence requirements not met"); + + // We need at least one byte to really do anything + if(boost::asio::buffer_size(buffers) < 1) + return boost::indeterminate; + + // Extract the first byte, which holds the + // "message" type for the Handshake protocol. + unsigned char v; + boost::asio::buffer_copy(boost::asio::buffer(&v, 1), buffers); + + // Check that the message type is "SSL Handshake" (rfc2246) + if(v != 0x16) + { + // This is definitely not a handshake + return false; + } + + // At least four bytes are needed for the handshake + // so make sure that we get them before returning `true` + if(boost::asio::buffer_size(buffers) < 4) + return boost::indeterminate; + + // This can only be a TLS/SSL handshake + return true; +} + +//] + +//[example_core_detect_ssl_3 + +/** Detect a TLS/SSL handshake on a stream. + + This function reads from a stream to determine if a TLS/SSL + handshake is being received. The function call will block + until one of the following conditions is true: + + @li The disposition of the handshake is determined + + @li An error occurs + + Octets read from the stream will be stored in the passed dynamic + buffer, which may be used to perform the TLS handshake if the + detector returns true, or otherwise consumed by the caller based + on the expected protocol. + + @param stream The stream to read from. This type must meet the + requirements of @b SyncReadStream. + + @param buffer The dynamic buffer to use. This type must meet the + requirements of @b DynamicBuffer. + + @param ec Set to the error if any occurred. + + @return `boost::tribool` indicating whether the buffer contains + a TLS client handshake, does not contain a handshake, or needs + additional octets. If an error occurs, the return value is + undefined. +*/ +template< + class SyncReadStream, + class DynamicBuffer> +boost::tribool +detect_ssl( + SyncReadStream& stream, + DynamicBuffer& buffer, + boost::beast::error_code& ec) +{ + namespace beast = boost::beast; + + // Make sure arguments meet the requirements + static_assert(beast::is_sync_read_stream::value, + "SyncReadStream requirements not met"); + static_assert( + boost::asio::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + + // Loop until an error occurs or we get a definitive answer + for(;;) + { + // There could already be data in the buffer + // so we do this first, before reading from the stream. + auto const result = is_ssl_handshake(buffer.data()); + + // If we got an answer, return it + if(! boost::indeterminate(result)) + { + // This is a fast way to indicate success + // without retrieving the default category. + ec.assign(0, ec.category()); + return result; + } + + // The algorithm should never need more than 4 bytes + BOOST_ASSERT(buffer.size() < 4); + + // Prepare the buffer's output area. + auto const mutable_buffer = buffer.prepare(beast::read_size(buffer, 1536)); + + // Try to fill our buffer by reading from the stream + std::size_t const bytes_transferred = stream.read_some(mutable_buffer, ec); + + // Check for an error + if(ec) + break; + + // Commit what we read into the buffer's input area. + buffer.commit(bytes_transferred); + } + + // error + return false; +} + +//] + +//[example_core_detect_ssl_4 + +/** Detect a TLS/SSL handshake asynchronously on a stream. + + This function is used to asynchronously determine if a TLS/SSL + handshake is being received. + The function call always returns immediately. The asynchronous + operation will continue until one of the following conditions + is true: + + @li The disposition of the handshake is determined + + @li An error occurs + + This operation is implemented in terms of zero or more calls to + the next layer's `async_read_some` function, and is known as a + composed operation. The program must ensure that the + stream performs no other operations until this operation completes. + + Octets read from the stream will be stored in the passed dynamic + buffer, which may be used to perform the TLS handshake if the + detector returns true, or otherwise consumed by the caller based + on the expected protocol. + + @param stream The stream to read from. This type must meet the + requirements of @b AsyncReadStream. + + @param buffer The dynamic buffer to use. This type must meet the + requirements of @b DynamicBuffer. + + @param handler Invoked when the operation completes. + The handler may be moved or copied as needed. + The equivalent function signature of the handler must be: + @code + void handler( + error_code const& error, // Set to the error, if any + boost::tribool result // The result of the detector + ); + @endcode + Regardless of whether the asynchronous operation completes + immediately or not, the handler will not be invoked from within + this function. Invocation of the handler will be performed in a + manner equivalent to using `boost::asio::io_context::post`. +*/ +template< + class AsyncReadStream, + class DynamicBuffer, + class CompletionToken> +BOOST_ASIO_INITFN_RESULT_TYPE( /*< `BOOST_ASIO_INITFN_RESULT_TYPE` customizes the return value based on the completion token >*/ + CompletionToken, + void(boost::beast::error_code, boost::tribool)) /*< This is the signature for the completion handler >*/ +async_detect_ssl( + AsyncReadStream& stream, + DynamicBuffer& buffer, + CompletionToken&& token); + +//] + +//[example_core_detect_ssl_5 + +// This is the composed operation. +template< + class AsyncReadStream, + class DynamicBuffer, + class Handler> +class detect_ssl_op; + +// Here is the implementation of the asynchronous initation function +template< + class AsyncReadStream, + class DynamicBuffer, + class CompletionToken> +BOOST_ASIO_INITFN_RESULT_TYPE( + CompletionToken, + void(boost::beast::error_code, boost::tribool)) +async_detect_ssl( + AsyncReadStream& stream, + DynamicBuffer& buffer, + CompletionToken&& token) +{ + namespace beast = boost::beast; + + // Make sure arguments meet the requirements + static_assert(beast::is_async_read_stream::value, + "SyncReadStream requirements not met"); + static_assert( + boost::asio::is_dynamic_buffer::value, + "DynamicBuffer requirements not met"); + + // This helper manages some of the handler's lifetime and + // uses the result and handler specializations associated with + // the completion token to help customize the return value. + // + boost::asio::async_completion< + CompletionToken, void(beast::error_code, boost::tribool)> init{token}; + + // Create the composed operation and launch it. This is a constructor + // call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE + // to convert the completion token into the correct handler type, + // allowing user defined specializations of the async result template + // to take effect. + // + detect_ssl_op< + AsyncReadStream, + DynamicBuffer, + BOOST_ASIO_HANDLER_TYPE( + CompletionToken, void(beast::error_code, boost::tribool))>{ + stream, buffer, init.completion_handler}(beast::error_code{}, 0); + + // This hook lets the caller see a return value when appropriate. + // For example this might return std::future if + // CompletionToken is boost::asio::use_future. + // + // If a coroutine is used for the token, the return value from + // this function will be the `boost::tribool` representing the result. + // + return init.result.get(); +} + +//] + +//[example_core_detect_ssl_6 + +// Read from a stream to invoke is_tls_handshake asynchronously. +// This will be implemented using Asio's "stackless coroutines" +// which are based on macros forming a switch statement. The +// operation is derived from `coroutine` for this reason. +// +template< + class AsyncReadStream, + class DynamicBuffer, + class Handler> +class detect_ssl_op : public boost::asio::coroutine +{ + // This composed operation has trivial state, + // so it is just kept inside the class and can + // be cheaply copied as needed by the implementation. + + AsyncReadStream& stream_; + DynamicBuffer& buffer_; + Handler handler_; + boost::tribool result_ = false; + +public: + // Boost.Asio requires that handlers are CopyConstructible. + // The state for this operation is cheap to copy. + detect_ssl_op(detect_ssl_op const&) = default; + + // The constructor just keeps references the callers varaibles. + // + template + detect_ssl_op( + AsyncReadStream& stream, + DynamicBuffer& buffer, + DeducedHandler&& handler) + : stream_(stream) + , buffer_(buffer) + , handler_(std::forward(handler)) + { + } + + // Associated allocator support. This is Asio's system for + // allowing the final completion handler to customize the + // memory allocation strategy used for composed operation + // states. A composed operation needs to use the same allocator + // as the final handler. These declarations achieve that. + + using allocator_type = + boost::asio::associated_allocator_t; + + allocator_type + get_allocator() const noexcept + { + return (boost::asio::get_associated_allocator)(handler_); + } + + // Executor hook. This is Asio's system for customizing the + // manner in which asynchronous completion handlers are invoked. + // A composed operation needs to use the same executor to invoke + // intermediate completion handlers as that used to invoke the + // final handler. + + using executor_type = boost::asio::associated_executor_t< + Handler, decltype(std::declval().get_executor())>; + + executor_type get_executor() const noexcept + { + return (boost::asio::get_associated_executor)(handler_, stream_.get_executor()); + } + + // Our main entry point. This will get called as our + // intermediate operations complete. Definition below. + // + void operator()(boost::beast::error_code ec, std::size_t bytes_transferred); +}; + +//] + +//[example_core_detect_ssl_7 + +// detect_ssl_op is callable with the signature +// void(error_code, bytes_transferred), +// allowing `*this` to be used as a ReadHandler +// +template< + class AsyncStream, + class DynamicBuffer, + class Handler> +void +detect_ssl_op:: +operator()(boost::beast::error_code ec, std::size_t bytes_transferred) +{ + namespace beast = boost::beast; + + // This introduces the scope of the stackless coroutine + BOOST_ASIO_CORO_REENTER(*this) + { + // There could already be data in the buffer + // so we do this first, before reading from the stream. + result_ = is_ssl_handshake(buffer_.data()); + + // If we got an answer, return it + if(! boost::indeterminate(result_)) + { + // We need to invoke the handler, but the guarantee + // is that the handler will not be called before the + // call to async_detect_ssl returns, so we must post + // the operation to the executor. The helper function + // `bind_handler` lets us bind arguments in a safe way + // that preserves the type customization hooks of the + // original handler. + BOOST_ASIO_CORO_YIELD + boost::asio::post( + stream_.get_executor(), + beast::bind_handler(std::move(*this), ec, 0)); + } + else + { + // Loop until an error occurs or we get a definitive answer + for(;;) + { + // The algorithm should never need more than 4 bytes + BOOST_ASSERT(buffer_.size() < 4); + + BOOST_ASIO_CORO_YIELD + { + // Prepare the buffer's output area. + auto const mutable_buffer = buffer_.prepare(beast::read_size(buffer_, 1536)); + + // Try to fill our buffer by reading from the stream + stream_.async_read_some(mutable_buffer, std::move(*this)); + } + + // Check for an error + if(ec) + break; + + // Commit what we read into the buffer's input area. + buffer_.commit(bytes_transferred); + + // See if we can detect the handshake + result_ = is_ssl_handshake(buffer_.data()); + + // If it is detected, call the handler + if(! boost::indeterminate(result_)) + { + // We don't need bind_handler here because we were invoked + // as a result of an intermediate asynchronous operation. + break; + } + } + } + + // Invoke the final handler. + handler_(ec, result_); + } +} + +//] + +#endif diff --git a/w3c-visserver-api/include/ssl_stream.hpp b/w3c-visserver-api/include/ssl_stream.hpp new file mode 100644 index 0000000..85bea9d --- /dev/null +++ b/w3c-visserver-api/include/ssl_stream.hpp @@ -0,0 +1,325 @@ +// +// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/beast +// + +#ifndef BOOST_BEAST_EXAMPLE_COMMON_SSL_STREAM_HPP +#define BOOST_BEAST_EXAMPLE_COMMON_SSL_STREAM_HPP + +// This include is necessary to work with `ssl::stream` and `boost::beast::websocket::stream` +#include + +#include +#include +#include +#include +#include +#include + +/** C++11 enabled SSL socket wrapper + + This wrapper provides an interface identical to `boost::asio::ssl::stream`, + with the following additional properties: + + @li Satisfies @b MoveConstructible + + @li Satisfies @b MoveAssignable + + @li Constructible from a moved socket. +*/ +template +class ssl_stream + : public boost::asio::ssl::stream_base +{ + using stream_type = boost::asio::ssl::stream; + + std::unique_ptr p_; + boost::asio::ssl::context* ctx_; + +public: + /// The native handle type of the SSL stream. + using native_handle_type = typename stream_type::native_handle_type; + + /// Structure for use with deprecated impl_type. + using impl_struct = typename stream_type::impl_struct; + + /// The type of the next layer. + using next_layer_type = typename stream_type::next_layer_type; + + /// The type of the lowest layer. + using lowest_layer_type = typename stream_type::lowest_layer_type; + + /// The type of the executor associated with the object. + using executor_type = typename stream_type::executor_type; + + template + ssl_stream( + Arg&& arg, + boost::asio::ssl::context& ctx) + : p_(new stream_type{ + std::forward(arg), ctx}) + , ctx_(&ctx) + { + } + + ssl_stream(ssl_stream&& other) + : p_(std::move(other.p_)) + , ctx_(other.ctx_) + { + } + + ssl_stream& operator=(ssl_stream&& other) + { + p_ = std::move(other.p_); + ctx_ = other.ctx_; + return *this; + } + + executor_type + get_executor() noexcept + { + return p_->get_executor(); + } + + native_handle_type + native_handle() + { + return p_->native_handle(); + } + + next_layer_type const& + next_layer() const + { + return p_->next_layer(); + } + + next_layer_type& + next_layer() + { + return p_->next_layer(); + } + + lowest_layer_type& + lowest_layer() + { + return p_->lowest_layer(); + } + + lowest_layer_type const& + lowest_layer() const + { + return p_->lowest_layer(); + } + + void + set_verify_mode(boost::asio::ssl::verify_mode v) + { + p_->set_verify_mode(v); + } + + boost::system::error_code + set_verify_mode(boost::asio::ssl::verify_mode v, + boost::system::error_code& ec) + { + return p_->set_verify_mode(v, ec); + } + + void + set_verify_depth(int depth) + { + p_->set_verify_depth(depth); + } + + boost::system::error_code + set_verify_depth( + int depth, boost::system::error_code& ec) + { + return p_->set_verify_depth(depth, ec); + } + + template + void + set_verify_callback(VerifyCallback callback) + { + p_->set_verify_callback(callback); + } + + template + boost::system::error_code + set_verify_callback(VerifyCallback callback, + boost::system::error_code& ec) + { + return p_->set_verify_callback(callback, ec); + } + + void + handshake(handshake_type type) + { + p_->handshake(type); + } + + boost::system::error_code + handshake(handshake_type type, + boost::system::error_code& ec) + { + return p_->handshake(type, ec); + } + + template + void + handshake( + handshake_type type, ConstBufferSequence const& buffers) + { + p_->handshake(type, buffers); + } + + template + boost::system::error_code + handshake(handshake_type type, + ConstBufferSequence const& buffers, + boost::system::error_code& ec) + { + return p_->handshake(type, buffers, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler, + void(boost::system::error_code)) + async_handshake(handshake_type type, + BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler) + { + return p_->async_handshake(type, + BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler)); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler, + void (boost::system::error_code, std::size_t)) + async_handshake(handshake_type type, ConstBufferSequence const& buffers, + BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler) + { + return p_->async_handshake(type, buffers, + BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler)); + } + + void + shutdown() + { + p_->shutdown(); + } + + boost::system::error_code + shutdown(boost::system::error_code& ec) + { + return p_->shutdown(ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ShutdownHandler, + void (boost::system::error_code)) + async_shutdown(BOOST_ASIO_MOVE_ARG(ShutdownHandler) handler) + { + return p_->async_shutdown( + BOOST_ASIO_MOVE_CAST(ShutdownHandler)(handler)); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers) + { + return p_->write_some(buffers); + } + + template + std::size_t + write_some(ConstBufferSequence const& buffers, + boost::system::error_code& ec) + { + return p_->write_some(buffers, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void (boost::system::error_code, std::size_t)) + async_write_some(ConstBufferSequence const& buffers, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + return p_->async_write_some(buffers, + BOOST_ASIO_MOVE_CAST(WriteHandler)(handler)); + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers) + { + return p_->read_some(buffers); + } + + template + std::size_t + read_some(MutableBufferSequence const& buffers, + boost::system::error_code& ec) + { + return p_->read_some(buffers, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_read_some(MutableBufferSequence const& buffers, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + return p_->async_read_some(buffers, + BOOST_ASIO_MOVE_CAST(ReadHandler)(handler)); + } + + template + friend + void + teardown(boost::beast::websocket::role_type, + ssl_stream& stream, + boost::system::error_code& ec); + + template + friend + void + async_teardown(boost::beast::websocket::role_type, + ssl_stream& stream, TeardownHandler&& handler); +}; + +// These hooks are used to inform boost::beast::websocket::stream on +// how to tear down the connection as part of the WebSocket +// protocol specifications + +template +inline +void +teardown( + boost::beast::websocket::role_type role, + ssl_stream& stream, + boost::system::error_code& ec) +{ + // Just forward it to the wrapped ssl::stream + using boost::beast::websocket::teardown; + teardown(role, *stream.p_, ec); +} + +template +inline +void +async_teardown( + boost::beast::websocket::role_type role, + ssl_stream& stream, + TeardownHandler&& handler) +{ + // Just forward it to the wrapped ssl::stream + using boost::beast::websocket::async_teardown; + async_teardown(role, + *stream.p_, std::forward(handler)); +} + +#endif diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp new file mode 100644 index 0000000..3f5ce3d --- /dev/null +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -0,0 +1,1711 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "detect_ssl.hpp" +#include "ssl_stream.hpp" + +#include "WebSockHttpFlexServer.hpp" +#include "IVssCommandProcessor.hpp" +#include "WsChannel.hpp" +#include "ILogger.hpp" + +using RequestHandler = std::function; +using Listeners = std::vector>>; +using tcp = boost::asio::ip::tcp; // from +namespace ssl = boost::asio::ssl; // from +namespace http = boost::beast::http; // from +namespace websocket = boost::beast::websocket; // from + +namespace { + // forward declaration for classes that are defined below + class PlainWebsocketSession; + class SslWebsocketSession; + class PlainHttpSession; + class SslHttpSession; + class BeastListener; + +/** + * @class ConnectionHandler + * @brief Helper class to handle connection between \ref WsChannel and actual sessions + */ + class ConnectionHandler { + // Allow access to connection information from this file implementation details + friend class ::WebSockHttpFlexServer; + + private: + std::mutex mPlainWebSock_; + std::mutex mSslWebSock_; + std::mutex mPlainHttp_; + std::mutex mSslHttp_; + + std::unordered_map> connPlainWebSock_; + std::unordered_map> connSslWebSock_; + std::unordered_map> connPlainHttp_; + std::unordered_map> connSslHttp_; + + + public: + ConnectionHandler() = default; + + /** + * @brief Add new client for plain Web-Socket session + * @param session New session to add + * @return \ref WsChannel with new connection information + */ + WsChannel& AddClient(const PlainWebsocketSession * session) { + auto newChannel = std::make_shared(); + std::lock_guard lock(mPlainWebSock_); + + newChannel->setConnID(reinterpret_cast(session)); + newChannel->setType(WsChannel::Type::WEBSOCKET_PLAIN); + connPlainWebSock_[session] = newChannel; + + return *newChannel; + } + + /** + * @brief Add new client for SSL Web-Socket session + * @param session New session to add + * @return \ref WsChannel with new connection information + */ + WsChannel& AddClient(const SslWebsocketSession * session) { + auto newChannel = std::make_shared(); + std::lock_guard lock(mSslWebSock_); + + newChannel->setConnID(reinterpret_cast(session)); + newChannel->setType(WsChannel::Type::WEBSOCKET_SSL); + connSslWebSock_[session] = newChannel; + + return *newChannel; + } + + /** + * @brief Add new client for plain HTTP session + * @param session New session to add + * @return \ref WsChannel with new connection information + */ + WsChannel& AddClient(const PlainHttpSession * session) { + auto newChannel = std::make_shared(); + std::lock_guard lock(mPlainHttp_); + + newChannel->setConnID(reinterpret_cast(session)); + newChannel->setType(WsChannel::Type::HTTP_PLAIN); + connPlainHttp_[session] = newChannel; + + return *newChannel; + } + + /** + * @brief Add new client for SSL HTTP session + * @param session New session to add + * @return \ref WsChannel with new connection information + */ + WsChannel& AddClient(const SslHttpSession * session) { + auto newChannel = std::make_shared(); + std::lock_guard lock(mSslHttp_); + + newChannel->setConnID(reinterpret_cast(session)); + newChannel->setType(WsChannel::Type::HTTP_SSL); + connSslHttp_[session] = newChannel; + + return *newChannel; + } + + /** + * @brief Remove client for plain Web-Socket session + * @param session Existing session to remove + */ + void RemoveClient(const PlainWebsocketSession * session) { + std::lock_guard lock(mPlainWebSock_); + connPlainWebSock_.erase(session); + } + + /** + * @brief Remove client for SSL Web-Socket session + * @param session Existing session to remove + */ + void RemoveClient(const SslWebsocketSession * session) { + std::lock_guard lock(mSslWebSock_); + connSslWebSock_.erase(session); + } + + /** + * @brief Remove client for plain HTTP session + * @param session Existing session to remove + */ + void RemoveClient(const PlainHttpSession * session) { + std::lock_guard lock(mPlainHttp_); + connPlainHttp_.erase(session); + } + + /** + * @brief Remove client for SSL HTTP session + * @param session Existing session to remove + */ + void RemoveClient(const SslHttpSession * session) { + std::lock_guard lock(mSslHttp_); + connSslHttp_.erase(session); + } + }; + + /**** Local variables ****/ + + + // Boost.Beast helper state variables + ConnectionHandler connHandler; + std::shared_ptr connListener; + std::shared_ptr ioc; + ssl::context ctx{ssl::context::sslv23}; + std::vector iocRunners; + + /// Are allowed plain Web-socket/HTTP connections + bool allowInsecureConns = false; + + std::shared_ptr logger; + + + /**** Boost.Beast implementation below ****/ + + /// Report a failure + void + fail(boost::system::error_code ec, char const* what) + { + logger->Log(LogLevel::ERROR, std::string(what) + ": " + ec.message()); + } + + /// Report a failure and remove client from active connections that are tracked + template + void + fail(const T* fromType, boost::system::error_code ec, char const* what) + { + fail(ec, what); + logger->Log(LogLevel::ERROR, "Connection error detected, remove client from active connections"); + connHandler.RemoveClient(fromType); + } + + // Return a reasonable mime type based on the extension of a file. + boost::beast::string_view + mimeType(boost::beast::string_view path) + { + using boost::beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == boost::beast::string_view::npos) + return boost::beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; + } + + // Append an HTTP rel-path to a local filesystem path. + // The returned path is normalized for the platform. + std::string + fixPath( + boost::beast::string_view base, + boost::beast::string_view path) + { + if(base.empty()) + return path.to_string(); + std::string result = base.to_string(); + #if BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; + #else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + #endif + return result; + } + + // This function produces an HTTP response for the given + // request. The type of the response object depends on the + // contents of the request, so the interface requires the + // caller to pass a generic lambda for receiving the response. + // TODO: in future this shall be replaced with REST handler as IVssCommandProcessor listener + template< + class Body, class Allocator, + class Send> + void + defaultHttpRequestHandler( + boost::beast::string_view doc_root, + http::request>&& req, + Send&& send) + { + // Returns a bad request response + auto const bad_request = + [&req](boost::beast::string_view why) + { + http::response res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = why.to_string(); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](boost::beast::string_view target) + { + http::response res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + target.to_string() + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](boost::beast::string_view what) + { + http::response res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + what.to_string() + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != boost::beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = fixPath(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + boost::beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), boost::beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == boost::system::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mimeType(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mimeType(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + //------------------------------------------------------------------------------ + + //------------------------------------------------------------------------------ + + // Echoes back all received WebSocket messages. + // This uses the Curiously Recurring Template Pattern so that + // the same code works with both SSL streams and regular sockets. + template + class WebSocketSession + { + // Access the derived class, this is part of + // the Curiously Recurring Template Pattern idiom. + Derived& + derived() + { + return static_cast(*this); + } + + boost::beast::multi_buffer bufferRead_; + boost::beast::multi_buffer bufferWrite_; + char ping_state_ = 0; + + protected: + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + boost::asio::steady_timer timer_; + RequestHandler requestHandler_; + WsChannel channel; + public: + // Construct the session + explicit + WebSocketSession(boost::asio::io_context& ioc, + RequestHandler requestHandler) + : strand_(ioc.get_executor()) + , timer_(ioc, + (std::chrono::steady_clock::time_point::max)()) + , requestHandler_(requestHandler) + { + } + + // Start the asynchronous operation + template + void + doAccept(http::request> req) + { + // Set the control callback. This will be called + // on every incoming ping, pong, and close frame. + derived().ws().control_callback( + std::bind( + &WebSocketSession::on_control_callback, + this, + std::placeholders::_1, + std::placeholders::_2)); + + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + + // Accept the websocket handshake + derived().ws().async_accept( + req, + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onAccept, + derived().shared_from_this(), + std::placeholders::_1))); + } + + void + onAccept(boost::system::error_code ec) + { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(&derived(), ec, "accept"); + return; + } + + // Read a message + doRead(); + } + + // Called when the timer expires. + void + onTimer(boost::system::error_code ec) + { + if(ec && ec != boost::asio::error::operation_aborted) { + fail<>(&derived(), ec, "timer"); + return; + } + + // See if the timer really expired since the deadline may have moved. + if(timer_.expiry() <= std::chrono::steady_clock::now()) + { + // If this is the first time the timer expired, + // send a ping to see if the other end is there. + if(derived().ws().is_open() && ping_state_ == 0) + { + // Note that we are sending a ping + ping_state_ = 1; + + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + + // Now send the ping + derived().ws().async_ping({}, + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onPing, + derived().shared_from_this(), + std::placeholders::_1))); + } + else + { + // The timer expired while trying to handshake, + // or we sent a ping and it never completed or + // we never got back a control frame, so close. + + derived().doTimeout(); + return; + } + } + + // Wait on the timer + timer_.async_wait( + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onTimer, + derived().shared_from_this(), + std::placeholders::_1))); + } + + // Called to indicate activity from the remote peer + void + activity() + { + // Note that the connection is alive + ping_state_ = 0; + + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + } + + // Called after a ping is sent. + void + onPing(boost::system::error_code ec) + { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(&derived(), ec, "ping"); + return; + } + + // Note that the ping was sent. + if(ping_state_ == 1) + { + ping_state_ = 2; + } + else + { + // ping_state_ could have been set to 0 + // if an incoming control frame was received + // at exactly the same time we sent a ping. + BOOST_ASSERT(ping_state_ == 0); + } + } + + void + on_control_callback( + websocket::frame_type kind, + boost::beast::string_view payload) + { + boost::ignore_unused(kind, payload); + + // Note that there is activity + activity(); + } + + void + doRead() + { + // Read a message into our buffer + derived().ws().async_read( + bufferRead_, + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onRead, + derived().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + + void + onRead( + boost::system::error_code ec, + std::size_t bytesTransferred) + { + boost::ignore_unused(bytesTransferred); + + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + // This indicates that the websocket_session was closed + if(ec == websocket::error::closed) + return; + + if(ec) + fail(ec, "read"); + + // Note that there is activity + activity(); + + derived().ws().text(derived().ws().got_text()); + + std::string response = requestHandler_(boost::beast::buffers_to_string(bufferRead_.data()), channel); + bufferRead_.consume(bytesTransferred); // clear existing buffer data + + // send response + write(response); + + // do another read + doRead(); + } + + void + write(const std::string &message) { + // TODO: add queuing as async_write should be called one at a time + boost::asio::buffer_copy(bufferWrite_.prepare(message.size()), boost::asio::buffer(message)); + bufferWrite_.commit(message.size()); // commit copied data for write + + // send message + derived().ws().async_write( + bufferWrite_.data(), + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onWrite, + derived().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + + void + onWrite( + boost::system::error_code ec, + std::size_t bytesTransferred) + { + boost::ignore_unused(bytesTransferred); + + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(&derived(), ec, "write"); + return; + } + + // Clear the buffer + bufferWrite_.consume(bytesTransferred); + + // Do another read + //doRead(); + } + }; + + // Handles a plain WebSocket connection + class PlainWebsocketSession + : public WebSocketSession + , public std::enable_shared_from_this + { + websocket::stream ws_; + bool close_ = false; + + public: + // Create the session + explicit + PlainWebsocketSession(tcp::socket socket, RequestHandler requestHandler) + : WebSocketSession( + socket.get_executor().context(), requestHandler) + , ws_(std::move(socket)) + { + } + + // Called by the base class + websocket::stream& + ws() + { + return ws_; + } + + // Start the asynchronous operation + template + void + run(http::request> req) + { + channel = connHandler.AddClient(this); + + // Run the timer. The timer is operated + // continuously, this simplifies the code. + onTimer({}); + + // Accept the WebSocket upgrade request + doAccept(std::move(req)); + } + + void + doTimeout() + { + // This is so the close can have a timeout + if(close_) + return; + close_ = true; + + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + + // Close the WebSocket Connection + ws_.async_close( + websocket::close_code::normal, + boost::asio::bind_executor( + strand_, + std::bind( + &PlainWebsocketSession::on_close, + shared_from_this(), + std::placeholders::_1))); + } + + void + on_close(boost::system::error_code ec) + { + // Happens when close times out + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(this, ec, "close"); + return; + } + + // At this point the connection is gracefully closed + } + }; + + // Handles an SSL WebSocket connection + class SslWebsocketSession + : public WebSocketSession + , public std::enable_shared_from_this + { + websocket::stream> ws_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + bool eof_ = false; + + public: + // Create the http_session + explicit + SslWebsocketSession(ssl_stream stream, RequestHandler requestHandler) + : WebSocketSession( + stream.get_executor().context(), requestHandler) + , ws_(std::move(stream)) + , strand_(ws_.get_executor()) + { + } + + // Called by the base class + websocket::stream>& + ws() + { + return ws_; + } + + // Start the asynchronous operation + template + void + run(http::request> req) + { + channel = connHandler.AddClient(this); + + // Run the timer. The timer is operated + // continuously, this simplifies the code. + onTimer({}); + + // Accept the WebSocket upgrade request + doAccept(std::move(req)); + } + + void + doEof() + { + eof_ = true; + + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + + // Perform the SSL shutdown + ws_.next_layer().async_shutdown( + boost::asio::bind_executor( + strand_, + std::bind( + &SslWebsocketSession::onShutdown, + shared_from_this(), + std::placeholders::_1))); + } + + void + onShutdown(boost::system::error_code ec) + { + // Happens when the shutdown times out + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(this, ec, "shutdown"); + return; + } + + connHandler.RemoveClient(this); + // At this point the connection is closed gracefully + } + + void + doTimeout() + { + // If this is true it means we timed out performing the shutdown + if(eof_) + return; + + // Start the timer again + timer_.expires_at( + (std::chrono::steady_clock::time_point::max)()); + onTimer({}); + doEof(); + } + }; + + template + void + makeWebsocketSession( + tcp::socket socket, + http::request> req, + RequestHandler requestHandler) + { + std::make_shared( + std::move(socket), requestHandler)->run(std::move(req)); + } + + template + void + makeWebsocketSession( + ssl_stream stream, + http::request> req, + RequestHandler requestHandler) + { + std::make_shared( + std::move(stream), requestHandler)->run(std::move(req)); + } + + //------------------------------------------------------------------------------ + + // Handles an HTTP server connection. + // This uses the Curiously Recurring Template Pattern so that + // the same code works with both SSL streams and regular sockets. + template + class HttpSession + { + // Access the derived class, this is part of + // the Curiously Recurring Template Pattern idiom. + Derived& + derived() + { + return static_cast(*this); + } + + // This queue is used for HTTP pipelining. + class queue + { + enum + { + // Maximum number of responses we will queue + limit = 8 + }; + + // The type-erased, saved work item + struct work + { + virtual ~work() = default; + virtual void operator()() = 0; + }; + + HttpSession& self_; + std::vector> items_; + + public: + explicit + queue(HttpSession& self) + : self_(self) + { + static_assert(limit > 0, "queue limit must be positive"); + items_.reserve(limit); + } + + // Returns `true` if we have reached the queue limit + bool + is_full() const + { + return items_.size() >= limit; + } + + // Called when a message finishes sending + // Returns `true` if the caller should initiate a read + bool + onWrite() + { + BOOST_ASSERT(! items_.empty()); + auto const was_full = is_full(); + items_.erase(items_.begin()); + if(! items_.empty()) + (*items_.front())(); + return was_full; + } + + // Called by the HTTP handler to send a response. + template + void + operator()(http::message&& msg) + { + // This holds a work item + struct work_impl : work + { + HttpSession& self_; + http::message msg_; + + work_impl( + HttpSession& self, + http::message&& msg) + : self_(self) + , msg_(std::move(msg)) + { + } + + void + operator()() + { + http::async_write( + self_.derived().stream(), + msg_, + boost::asio::bind_executor( + self_.strand_, + std::bind( + &HttpSession::onWrite, + self_.derived().shared_from_this(), + std::placeholders::_1, + msg_.need_eof()))); + } + }; + + // Allocate and store the work + items_.push_back( + boost::make_unique(self_, std::move(msg))); + + // If there was no previous work, start this one + if(items_.size() == 1) + (*items_.front())(); + } + }; + + std::string const& doc_root_; + http::request req_; + queue queue_; + + protected: + boost::asio::steady_timer timer_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + boost::beast::flat_buffer bufferRead_; + RequestHandler requestHandler_; + WsChannel channel; + + public: + // Construct the session + HttpSession( + boost::asio::io_context& ioc, + boost::beast::flat_buffer buffer, + std::string const& doc_root, + RequestHandler requestHandler) + : doc_root_(doc_root) + , queue_(*this) + , timer_(ioc, + (std::chrono::steady_clock::time_point::max)()) + , strand_(ioc.get_executor()) + , bufferRead_(std::move(buffer)) + , requestHandler_(requestHandler) + { + } + + void + doRead() + { + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + + // Make the request empty before reading, + // otherwise the operation behavior is undefined. + req_ = {}; + + // Read a request + http::async_read( + derived().stream(), + bufferRead_, + req_, + boost::asio::bind_executor( + strand_, + std::bind( + &HttpSession::onRead, + derived().shared_from_this(), + std::placeholders::_1))); + } + + // Called when the timer expires. + void + onTimer(boost::system::error_code ec) + { + if(ec && ec != boost::asio::error::operation_aborted) { + fail<>(&derived(), ec, "timer"); + return; + } + + // Verify that the timer really expired since the deadline may have moved. + if(timer_.expiry() <= std::chrono::steady_clock::now()) + return derived().doTimeout(); + + // Wait on the timer + timer_.async_wait( + boost::asio::bind_executor( + strand_, + std::bind( + &HttpSession::onTimer, + derived().shared_from_this(), + std::placeholders::_1))); + } + + void + onRead(boost::system::error_code ec) + { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + // This means they closed the connection + if(ec == http::error::end_of_stream) + return derived().doEof(); + + if(ec) + fail(ec, "read"); + + // See if it is a WebSocket Upgrade + if(websocket::is_upgrade(req_)) + { + // Transfer the stream to a new WebSocket session + return makeWebsocketSession( + derived().release_stream(), + std::move(req_), requestHandler_); + } + + // Otherwise handle pure HTTP connection + // TODO: call to appropriate REST API handler later (implemented as IVssCommandProcessor) + defaultHttpRequestHandler(doc_root_, std::move(req_), queue_); + + // If we aren't at the queue limit, try to pipeline another request + if(! queue_.is_full()) + doRead(); + } + + void + onWrite(boost::system::error_code ec, bool close) + { + // Happens when the timer closes the socket + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) { + fail<>(&derived(), ec, "write"); + return; + } + + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + return derived().doEof(); + } + + // Inform the queue that a write completed + if(queue_.onWrite()) + { + // Read another request + doRead(); + } + } + }; + + // Handles a plain HTTP connection + class PlainHttpSession + : public HttpSession + , public std::enable_shared_from_this + { + tcp::socket socket_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + + public: + // Create the http_session + PlainHttpSession( + tcp::socket socket, + boost::beast::flat_buffer buffer, + std::string const& doc_root, + RequestHandler requestHandler) + : HttpSession( + socket.get_executor().context(), + std::move(buffer), + doc_root, + requestHandler) + , socket_(std::move(socket)) + , strand_(socket_.get_executor()) + { + } + + // Called by the base class + tcp::socket& + stream() + { + return socket_; + } + + // Called by the base class + tcp::socket + release_stream() + { + return std::move(socket_); + } + + // Start the asynchronous operation + void + run() + { + channel = connHandler.AddClient(this); + + // Run the timer. The timer is operated + // continuously, this simplifies the code. + onTimer({}); + + doRead(); + } + + void + doEof() + { + // Send a TCP shutdown + boost::system::error_code ec; + socket_.shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully + } + + void + doTimeout() + { + // Closing the socket cancels all outstanding operations. They + // will complete with boost::asio::error::operation_aborted + boost::system::error_code ec; + socket_.shutdown(tcp::socket::shutdown_both, ec); + socket_.close(ec); + } + }; + + // Handles an SSL HTTP connection + class SslHttpSession + : public HttpSession + , public std::enable_shared_from_this + { + ssl_stream stream_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + bool eof_ = false; + + public: + // Create the http_session + SslHttpSession( + tcp::socket socket, + ssl::context& ctx, + boost::beast::flat_buffer buffer, + std::string const& doc_root, + RequestHandler requestHandler) + : HttpSession( + socket.get_executor().context(), + std::move(buffer), + doc_root, + requestHandler) + , stream_(std::move(socket), ctx) + , strand_(stream_.get_executor()) + { + } + + // Called by the base class + ssl_stream& + stream() + { + return stream_; + } + + // Called by the base class + ssl_stream + release_stream() + { + return std::move(stream_); + } + + // Start the asynchronous operation + void + run() + { + channel = connHandler.AddClient(this); + + // Run the timer. The timer is operated + // continuously, this simplifies the code. + /* TODO: There is a crash when using SSL to communicate + * when line below is un-commented.. Evaluate later + */ + //onTimer({}); + + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + + // Perform the SSL handshake + // Note, this is the buffered version of the handshake. + stream_.async_handshake( + ssl::stream_base::server, + bufferRead_.data(), + boost::asio::bind_executor( + strand_, + std::bind( + &SslHttpSession::onHandshake, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + void + onHandshake( + boost::system::error_code ec, + std::size_t bytes_used) + { + // Happens when the handshake times out + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) + return fail(ec, "handshake"); + + // Consume the portion of the buffer used by the handshake + bufferRead_.consume(bytes_used); + + doRead(); + } + + void + doEof() + { + eof_ = true; + + // Set the timer + timer_.expires_after(std::chrono::seconds(15)); + + // Perform the SSL shutdown + stream_.async_shutdown( + boost::asio::bind_executor( + strand_, + std::bind( + &SslHttpSession::onShutdown, + shared_from_this(), + std::placeholders::_1))); + } + + void + onShutdown(boost::system::error_code ec) + { + // Happens when the shutdown times out + if(ec == boost::asio::error::operation_aborted) + return; + + if(ec) + return fail(ec, "shutdown"); + + connHandler.RemoveClient(this); + // At this point the connection is closed gracefully + } + + void + doTimeout() + { + // If this is true it means we timed out performing the shutdown + if(eof_) + return; + + // Start the timer again + timer_.expires_at( + (std::chrono::steady_clock::time_point::max)()); + onTimer({}); + doEof(); + } + }; + + //------------------------------------------------------------------------------ + // Detects SSL handshakes + class DetectSession : public std::enable_shared_from_this + { + tcp::socket socket_; + ssl::context& ctx_; + boost::asio::strand< + boost::asio::io_context::executor_type> strand_; + std::string const& doc_root_; + boost::beast::flat_buffer buffer_; + RequestHandler requestHandler_; + + public: + explicit + DetectSession( + tcp::socket socket, + ssl::context& ctx, + std::string const& doc_root, + RequestHandler requestHandler) + : socket_(std::move(socket)) + , ctx_(ctx) + , strand_(socket_.get_executor()) + , doc_root_(doc_root) + , requestHandler_(requestHandler) + { + } + + // Launch the detector + void + run() + { + async_detect_ssl( + socket_, + buffer_, + boost::asio::bind_executor( + strand_, + std::bind( + &DetectSession::onDetect, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + + void + onDetect(boost::system::error_code ec, boost::tribool result) + { + if(ec) + return fail(ec, "detect"); + + if(result) + { + // Launch SSL session + std::make_shared( + std::move(socket_), + ctx_, + std::move(buffer_), + doc_root_, + requestHandler_)->run(); + return; + } + + if (allowInsecureConns) + { + // Launch plain session + std::make_shared( + std::move(socket_), + std::move(buffer_), + doc_root_, + requestHandler_)->run(); + } + else + { + logger->Log(LogLevel::ERROR, "Plain (non-SSL) connection not allowed. Stopping connection."); + } + } + }; + + // Accepts incoming connections and launches the sessions + class BeastListener : public std::enable_shared_from_this + { + ssl::context& ctx_; + tcp::acceptor acceptor_; + tcp::socket socket_; + std::string const& doc_root_; + RequestHandler requestHandler_; + + public: + BeastListener( + boost::asio::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + std::string const& doc_root, + RequestHandler requestHandler) + : ctx_(ctx) + , acceptor_(ioc) + , socket_(ioc) + , doc_root_(doc_root) + , requestHandler_(requestHandler) + { + boost::system::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(boost::asio::socket_base::reuse_address(true)); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + boost::asio::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + if(! acceptor_.is_open()) + return; + doAccept(); + } + + void + doAccept() + { + acceptor_.async_accept( + socket_, + std::bind( + &BeastListener::onAccept, + shared_from_this(), + std::placeholders::_1)); + } + + void + onAccept(boost::system::error_code ec) + { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the detector http_session and run it + std::make_shared( + std::move(socket_), + ctx_, + doc_root_, + requestHandler_)->run(); + } + + // Accept another connection + doAccept(); + } + }; +} // end namespace + + +WebSockHttpFlexServer::WebSockHttpFlexServer(std::shared_ptr loggerUtil) + : logger_(loggerUtil) { + logger = logger_; +} + +WebSockHttpFlexServer::~WebSockHttpFlexServer() { + ioc->stop(); // stop execution of io runner + + // wait to finish + for(auto& thread : iocRunners) { + thread.join(); + } +} +void WebSockHttpFlexServer::Initialize(std::string host, int port, bool allowInsecure) { + logger_->Log(LogLevel::INFO, "Initializing Boost.Beast web-socket and http server"); + + std::string const doc_root = "vss"; + + allowInsecureConns = allowInsecure; + + ioc = std::make_shared(NumOfThreads); + ctx.set_options(ssl::context::default_workarounds); + + boost::asio::ip::tcp::resolver resolver{*ioc}; + boost::asio::ip::tcp::resolver::query query(host, to_string(port)); + boost::asio::ip::tcp::resolver::iterator resolvedHost = resolver.resolve(query); + + // load required certificates for SSL connections + LoadCertData(ctx); + + // handling function redirecting requests to registered listeners + RequestHandler reqHndl = std::bind(&WebSockHttpFlexServer::HandleRequest, + this, + std::placeholders::_1, + std::placeholders::_2); + + // create listener for handling incoming connections + connListener = std::make_shared( + *ioc, + ctx, + resolvedHost->endpoint(), + std::move(doc_root), + std::bind(&WebSockHttpFlexServer::HandleRequest, + this, + std::placeholders::_1, + std::placeholders::_2)); +} + +std::string WebSockHttpFlexServer::HandleRequest(const std::string &req_json, WsChannel &channel) { + std::string response; + auto const type = channel.getType(); + ObserverType handlerType; + + if ((type == WsChannel::Type::WEBSOCKET_PLAIN) || + (type == WsChannel::Type::WEBSOCKET_SSL )) + { + handlerType = ObserverType::WEBSOCKET; + } + else + { + handlerType = ObserverType::HTTP; + } + + for (auto const& handler : listeners_) + { + if ((handler.first == ObserverType::ALL) || (handler.first == handlerType)) + { + response = handler.second->processQuery(req_json, channel); + } + } + + return response; +} + +void WebSockHttpFlexServer::AddListener(ObserverType type, + std::shared_ptr listener) { + listeners_.push_back(std::make_pair(type, listener)); +} + +void WebSockHttpFlexServer::RemoveListener(ObserverType type, + std::shared_ptr listener) { + auto listenerToRemove = std::make_pair(type, listener); + + for (auto item = std::begin(listeners_); item < std::end(listeners_); ++item) + { + if (*item == listenerToRemove) + { + listeners_.erase(item); + return; + } + } + + logger_->Log(LogLevel::WARNING, "Could not find listener to remove. Ignoring..."); +} + +void WebSockHttpFlexServer::LoadCertData(boost::asio::ssl::context& ctx) { + std::string cert; + std::string key; + std::string line; + + // read certicate + { + std::ifstream inputFile ("Server.pem"); + if (inputFile.is_open()) + { + while (getline(inputFile, line) ) + { + cert.append(line); + cert.append("\n"); + } + inputFile.close(); + } + } + // read key + { + std::ifstream inputFile ("Server.key"); + if (inputFile.is_open()) + { + while (getline(inputFile, line) ) + { + key.append(line); + key.append("\n"); + } + inputFile.close(); + } + } + + // add cert and key to security context for using with new connections + ctx.use_certificate_chain( + boost::asio::buffer(cert.data(), cert.size())); + + ctx.use_private_key( + boost::asio::buffer(key.data(), key.size()), + boost::asio::ssl::context::file_format::pem); + + isInitialized = true; +} + +void WebSockHttpFlexServer::SendToConnection(uint64_t connID, const std::string &message) { + if (!isInitialized) + { + std::string err("Cannot send to connection, server not initialized!"); + logger_->Log(LogLevel::ERROR, err); + throw std::runtime_error(err); + } + + bool isFound = false; + + // try to find active connection to send data to + + if (!isFound) { + auto session = reinterpret_cast(connID); + std::lock_guard lock(connHandler.mPlainWebSock_); + auto iter = connHandler.connPlainWebSock_.find(session); + if (iter != std::end(connHandler.connPlainWebSock_)) + { + isFound = true; + session->write(message); + } + } + if (!isFound) { + auto session = reinterpret_cast(connID); + std::lock_guard lock(connHandler.mSslWebSock_); + auto iter = connHandler.connSslWebSock_.find(session); + if (iter != std::end(connHandler.connSslWebSock_)) + { + isFound = true; + session->write(message); + } + } + if (!isFound) { + auto session = reinterpret_cast(connID); + std::lock_guard lock(connHandler.mPlainHttp_); + auto iter = connHandler.connPlainHttp_.find(session); + if (iter != std::end(connHandler.connPlainHttp_)) + { + isFound = true; + // TODO: check how we are going to handle ASYNC writes on HTTP connection? + } + } + if (!isFound) { + auto session = reinterpret_cast(connID); + std::lock_guard lock(connHandler.mSslHttp_); + auto iter = connHandler.connSslHttp_.find(session); + if (iter != std::end(connHandler.connSslHttp_)) + { + isFound = true; + // TODO: check how we are going to handle ASYNC writes on HTTP connection? + } + } +} + +void WebSockHttpFlexServer::Start() { + if (!isInitialized) + { + std::string err("Cannot start server, server not initialized!"); + logger_->Log(LogLevel::ERROR, err); + throw std::runtime_error(err); + } + + logger_->Log(LogLevel::INFO, "Starting Boost.Beast web-socket and http server"); + + // start listening for connections + connListener->run(); + + // run the I/O service on the requested number of threads + iocRunners.reserve(NumOfThreads); + for(auto i = 0; i < NumOfThreads; ++i) { + iocRunners.emplace_back( + [] + { + boost::system::error_code ec; + ioc->run(ec); + }); + } +} From 35969417f9ad4c34268bf9a7e162d8a293448616 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Fri, 11 Oct 2019 13:53:27 +0200 Subject: [PATCH 14/32] Updated w3c-visserver-api to use Boost.Beast server implementation - Integrated new server implementation supporting Web-Socket and HTTP on same port. Both plain and SSL connections possible. - Updated IServer interface to allow for server listeners and different connection types - Refactored SubscriptionHandler and WsChannel to streamline connection ID for different servers. Small cleanup done. - Updated test client to allow use of old client or new based on Boost.Beast Old and new implementations of client and server are left, but could be removed in future if there is no need for them anymore Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 2 + w3c-visserver-api/include/IServer.hpp | 18 +- .../include/ISubscriptionHandler.hpp | 3 +- .../include/SubscriptionHandler.hpp | 5 +- .../include/VssCommandProcessor.hpp | 3 +- w3c-visserver-api/include/WsChannel.hpp | 20 +- w3c-visserver-api/include/WsServer.hpp | 9 +- w3c-visserver-api/src/SubscriptionHandler.cpp | 42 +-- w3c-visserver-api/src/VssCommandProcessor.cpp | 7 +- .../src/WebSockHttpFlexServer.cpp | 6 - w3c-visserver-api/src/WsServer.cpp | 14 +- w3c-visserver-api/src/main.cpp | 42 ++- w3c-visserver-api/test/testclient.cpp | 251 ++++++++++++++++-- 13 files changed, 332 insertions(+), 90 deletions(-) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index 8a14e5e..4d6bf62 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -147,6 +147,8 @@ if(BUILD_TEST_CLIENT) add_executable(testclient ${CMAKE_CURRENT_SOURCE_DIR}/test/testclient.cpp) target_link_libraries(testclient simple-websocket-server) target_include_directories(testclient PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/jsoncons) + target_include_directories(testclient INTERFACE ${Boost_INCLUDE_DIR}) + target_link_libraries(testclient ${Boost_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../examples/demo-certificates/Client.pem ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../examples/demo-certificates/Client.key ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../examples/demo-certificates/CA.pem ${CMAKE_CURRENT_BINARY_DIR} COPYONLY) diff --git a/w3c-visserver-api/include/IServer.hpp b/w3c-visserver-api/include/IServer.hpp index 47d4529..b484a3c 100644 --- a/w3c-visserver-api/include/IServer.hpp +++ b/w3c-visserver-api/include/IServer.hpp @@ -16,14 +16,26 @@ #define __IWSSERVER_H__ #include +#include +class IVssCommandProcessor; + +/** + * \class ObserverType + * \brief Server traffic types which can be observed + */ +enum class ObserverType { + WEBSOCKET = 0x01, //!< Receive Web-Socket traffic data + HTTP = 0x02, //!< Receive HTTP traffic data + ALL = 0x03 //!< Receive all traffic +}; class IServer { public: virtual ~IServer() {} - virtual void startServer(const std::string &endpointName) = 0; - virtual void sendToConnection(uint32_t connID, const std::string &message) = 0; - virtual void start() = 0; + virtual void AddListener(ObserverType, std::shared_ptr) = 0; + virtual void RemoveListener(ObserverType, std::shared_ptr) = 0; + virtual void SendToConnection(uint64_t connID, const std::string &message) = 0; }; #endif diff --git a/w3c-visserver-api/include/ISubscriptionHandler.hpp b/w3c-visserver-api/include/ISubscriptionHandler.hpp index 98ae6d6..9c846c1 100644 --- a/w3c-visserver-api/include/ISubscriptionHandler.hpp +++ b/w3c-visserver-api/include/ISubscriptionHandler.hpp @@ -28,9 +28,8 @@ class ISubscriptionHandler { public: virtual ~ISubscriptionHandler() {} - virtual uint32_t subscribe(WsChannel& channel, + virtual uint64_t subscribe(WsChannel& channel, std::shared_ptr db, - uint32_t channelID, const std::string &path) = 0; virtual int unsubscribe(uint32_t subscribeID) = 0; virtual int unsubscribeAll(uint32_t connectionID) = 0; diff --git a/w3c-visserver-api/include/SubscriptionHandler.hpp b/w3c-visserver-api/include/SubscriptionHandler.hpp index 987cd83..23512c7 100644 --- a/w3c-visserver-api/include/SubscriptionHandler.hpp +++ b/w3c-visserver-api/include/SubscriptionHandler.hpp @@ -37,7 +37,7 @@ class WsServer; class ILogger; // Subscription ID: Client ID -typedef std::unordered_map subscriptions_t; +typedef std::unordered_map subscriptions_t; // Subscription UUID typedef std::string uuid_t; @@ -61,9 +61,8 @@ class SubscriptionHandler : public ISubscriptionHandler { std::shared_ptr checkAccess); ~SubscriptionHandler(); - uint32_t subscribe(WsChannel& channel, + uint64_t subscribe(WsChannel& channel, std::shared_ptr db, - uint32_t channelID, const std::string &path); int unsubscribe(uint32_t subscribeID); int unsubscribeAll(uint32_t connectionID); diff --git a/w3c-visserver-api/include/VssCommandProcessor.hpp b/w3c-visserver-api/include/VssCommandProcessor.hpp index 9e78d49..ebcb868 100644 --- a/w3c-visserver-api/include/VssCommandProcessor.hpp +++ b/w3c-visserver-api/include/VssCommandProcessor.hpp @@ -43,8 +43,7 @@ class VssCommandProcessor : public IVssCommandProcessor { std::string processGet(WsChannel& channel, uint32_t request_id, std::string path); std::string processSet(WsChannel& channel, uint32_t request_id, std::string path, jsoncons::json value); - std::string processSubscribe(WsChannel& channel, uint32_t request_id, - std::string path, uint32_t connectionID); + std::string processSubscribe(WsChannel& channel, uint32_t request_id, std::string path); std::string processUnsubscribe(uint32_t request_id, uint32_t subscribeID); std::string processGetMetaData(uint32_t request_id, std::string path); std::string processAuthorize(WsChannel& channel, uint32_t request_id, diff --git a/w3c-visserver-api/include/WsChannel.hpp b/w3c-visserver-api/include/WsChannel.hpp index 186f75b..d350e1b 100644 --- a/w3c-visserver-api/include/WsChannel.hpp +++ b/w3c-visserver-api/include/WsChannel.hpp @@ -23,24 +23,32 @@ using namespace jsoncons; using jsoncons::json; class WsChannel { + public: + enum class Type { + WEBSOCKET_PLAIN, + WEBSOCKET_SSL, + HTTP_PLAIN, + HTTP_SSL + }; private: - uint32_t connectionID; + uint64_t connectionID; bool authorized = false; string authToken; json permissions; + Type typeOfConnection; public: - void setConnID(uint32_t conID) { connectionID = conID; } + + void setConnID(uint64_t conID) { connectionID = conID; } void setAuthorized(bool isauth) { authorized = isauth; } void setAuthToken(string tok) { authToken = tok; } void setPermissions(json perm) { permissions = perm; } + void setType(Type type) { typeOfConnection = type; } - uint32_t getConnID() { return connectionID; } - + uint64_t getConnID() { return connectionID; } bool isAuthorized() { return authorized; } - string getAuthToken() { return authToken; } - json getPermissions() { return permissions; } + Type getType() { return typeOfConnection; } }; #endif diff --git a/w3c-visserver-api/include/WsServer.hpp b/w3c-visserver-api/include/WsServer.hpp index be283db..53b0ddb 100644 --- a/w3c-visserver-api/include/WsServer.hpp +++ b/w3c-visserver-api/include/WsServer.hpp @@ -43,7 +43,12 @@ class WsServer : public IServer { int port); ~WsServer(); void startServer(const std::string &endpointName); - void sendToConnection(uint32_t connID, const std::string &message); - void start(); + bool start(); + + // IServer + + void AddListener(ObserverType, std::shared_ptr); + void RemoveListener(ObserverType, std::shared_ptr); + void SendToConnection(uint64_t connID, const std::string &message); }; #endif diff --git a/w3c-visserver-api/src/SubscriptionHandler.cpp b/w3c-visserver-api/src/SubscriptionHandler.cpp index 11d0b77..78f9170 100644 --- a/w3c-visserver-api/src/SubscriptionHandler.cpp +++ b/w3c-visserver-api/src/SubscriptionHandler.cpp @@ -27,7 +27,6 @@ #include "ILogger.hpp" using namespace std; -// using namespace jsoncons; using namespace jsoncons::jsonpath; using jsoncons::json; @@ -46,14 +45,13 @@ SubscriptionHandler::~SubscriptionHandler() { stopThread(); } -uint32_t SubscriptionHandler::subscribe(WsChannel& channel, +uint64_t SubscriptionHandler::subscribe(WsChannel& channel, std::shared_ptr db, - uint32_t channelID, const string &path) { // generate subscribe ID "randomly". - uint32_t subId = rand() % 9999999; + uint64_t subId = rand() % 9999999; // embed connection ID into subID. - subId = channelID + subId; + subId = channel.getConnID() + subId; bool isBranch = false; string jPath = db->getVSSSpecificPath(path, isBranch, db->data_tree); @@ -66,7 +64,6 @@ uint32_t SubscriptionHandler::subscribe(WsChannel& channel, throw noPermissionException(msg.str()); } - int clientID = channelID / CLIENT_MASK; jsoncons::json resArray = jsonpath::json_query(db->data_tree, jPath); if (resArray.is_array() && resArray.size() == 1) { @@ -79,7 +76,7 @@ uint32_t SubscriptionHandler::subscribe(WsChannel& channel, + string("ID with a new one")); } - subscribeHandle[sigUUID][subId] = clientID; + subscribeHandle[sigUUID][subId] = channel.getConnID(); return subId; } else if (resArray.is_array()) { @@ -114,7 +111,7 @@ int SubscriptionHandler::unsubscribeAll(uint32_t connectionID) { for (auto& uuid : subscribeHandle) { auto subscriptions = &(uuid.second); for (auto& subscription : *subscriptions) { - if (subscription.second == (connectionID / CLIENT_MASK)) { + if (subscription.second == (connectionID)) { subscriptions->erase(subscription.first); } } @@ -131,11 +128,10 @@ int SubscriptionHandler::updateByUUID(const string &signalUUID, } for (auto subID : handle->second) { - subMutex.lock(); + std::lock_guard lock(subMutex); pair newSub; - newSub = std::make_pair(subID.first, value); + newSub = std::make_pair(subID.second, value); buffer.push(newSub); - subMutex.unlock(); } return 0; @@ -154,22 +150,20 @@ int SubscriptionHandler::updateByPath(const string &path, const json &value) { } void* SubscriptionHandler::subThreadRunner() { - // SubscriptionHandler* handler = (SubscriptionHandler*)instance; - logger->Log(LogLevel::VERBOSE, "SubscribeThread: Started Subscription Thread!"); while (isThreadRunning()) { - subMutex.lock(); if (buffer.size() > 0) { - pair newSub = buffer.front(); + std::lock_guard lock(subMutex); + auto newSub = buffer.front(); buffer.pop(); - uint32_t subID = newSub.first; + auto connId = newSub.first; jsoncons::json value = newSub.second; jsoncons::json answer; answer["action"] = "subscribe"; - answer["subscriptionId"] = subID; + answer["subscriptionId"] = connId; answer.insert_or_assign("value", value); answer["timestamp"] = time(NULL); @@ -177,10 +171,8 @@ void* SubscriptionHandler::subThreadRunner() { ss << pretty_print(answer); string message = ss.str(); - uint32_t connectionID = (subID / CLIENT_MASK) * CLIENT_MASK; - getServer()->sendToConnection(connectionID, message); + getServer()->SendToConnection(connId, message); } - subMutex.unlock(); // sleep 10 ms usleep(10000); } @@ -191,22 +183,14 @@ void* SubscriptionHandler::subThreadRunner() { int SubscriptionHandler::startThread() { subThread = thread(&SubscriptionHandler::subThreadRunner, this); - /* - if (pthread_create(&subscription_thread, NULL, &subThread, this)) { - logger->Log(LogLevel::ERROR, "SubscriptionHandler::startThread: Error creating subscription " - + "handler thread"); - return 1; - } - */ threadRun = true; return 0; } int SubscriptionHandler::stopThread() { - subMutex.lock(); + std::lock_guard lock(subMutex); threadRun = false; subThread.join(); - subMutex.unlock(); return 0; } diff --git a/w3c-visserver-api/src/VssCommandProcessor.cpp b/w3c-visserver-api/src/VssCommandProcessor.cpp index b3ad0c9..4ed9625 100644 --- a/w3c-visserver-api/src/VssCommandProcessor.cpp +++ b/w3c-visserver-api/src/VssCommandProcessor.cpp @@ -208,14 +208,13 @@ string VssCommandProcessor::processSet(WsChannel &channel, } string VssCommandProcessor::processSubscribe(WsChannel &channel, - uint32_t request_id, string path, - uint32_t connectionID) { + uint32_t request_id, string path) { logger->Log(LogLevel::VERBOSE, string("VssCommandProcessor::processSubscribe: path received from client ") + string("for subscription")); uint32_t subId = -1; try { - subId = subHandler->subscribe(channel, database, connectionID, path); + subId = subHandler->subscribe(channel, database, path); } catch (noPathFoundonTree &noPathFound) { logger->Log(LogLevel::ERROR, string(noPathFound.what())); return pathNotFoundResponse(request_id, "subscribe", path); @@ -462,7 +461,7 @@ string VssCommandProcessor::processQuery(const string &req_json, logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: subscribe query for " + path + " with request id " + to_string(request_id)); response = - processSubscribe(channel, request_id, path, channel.getConnID()); + processSubscribe(channel, request_id, path); } else if (action == "getMetadata") { logger->Log(LogLevel::VERBOSE, "VssCommandProcessor::processQuery: metadata query for " + path + " with request id " + to_string(request_id)); diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index 3f5ce3d..437a6a4 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -12,21 +12,15 @@ * ***************************************************************************** */ -#include #include #include -#include #include #include -#include #include #include #include #include -#include -#include #include -#include #include #include #include diff --git a/w3c-visserver-api/src/WsServer.cpp b/w3c-visserver-api/src/WsServer.cpp index 5993af2..bcbf08e 100644 --- a/w3c-visserver-api/src/WsServer.cpp +++ b/w3c-visserver-api/src/WsServer.cpp @@ -237,7 +237,7 @@ void WsServer::startServer(const string &endpointName) { } } -void WsServer::sendToConnection(uint32_t connectionID, const string &message) { +void WsServer::SendToConnection(uint64_t connectionID, const string &message) { if (isSecure_) { auto send_stream = make_shared(); *send_stream << message; @@ -268,11 +268,21 @@ void *startWSServer(void *arg) { return NULL; } -void WsServer::start() { +bool WsServer::start() { pthread_t startWSServer_thread; /* create the web socket server thread. */ if (pthread_create(&startWSServer_thread, NULL, &startWSServer, NULL)) { logger->Log(LogLevel::ERROR, "main: Error creating websocket server run thread"); } + + return true; +} + +void WsServer::AddListener(ObserverType, std::shared_ptr) +{ +} + +void WsServer::RemoveListener(ObserverType, std::shared_ptr) +{ } diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index 2f39209..cb9e302 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -32,6 +32,7 @@ #include "VssDatabase.hpp" #include "WsServer.hpp" +#include "WebSockHttpFlexServer.hpp" using namespace std; @@ -237,6 +238,7 @@ int main(int argc, const char *argv[]) { ("help,h", "Help screen") ("vss", program_options::value(), "vss_rel*.json file") ("insecure", "Run insecure") + ("wss-server", "Run old WSS server handler") ("port", program_options::value()->default_value(8090), "Port") ("address", program_options::value()->default_value("localhost"), "Address"); @@ -258,6 +260,7 @@ int main(int argc, const char *argv[]) { auto port = variables["port"].as(); auto secure = !variables.count("insecure"); auto vss_filename = variables["vss"].as(); + auto useNewServer = !variables.count("wss-server"); // Start D-Bus backend connection. guint owner_id; @@ -288,30 +291,45 @@ int main(int argc, const char *argv[]) { #else logLevelsActive = static_cast(LogLevel::INFO & LogLevel::WARNING & LogLevel::ERROR); #endif - auto server = std::make_shared(); + auto logger = std::make_shared(logLevelsActive); + // TODO: refactor out old server when we can remove it + auto oldServer = std::make_shared(); + auto newServer = std::make_shared(logger); + + std::shared_ptr server; + if (!useNewServer) { + server = std::static_pointer_cast(oldServer); + } + else { + server = std::static_pointer_cast(newServer); + } + auto tokenValidator = std::make_shared(logger, "appstacle", "RS256"); auto accessCheck = std::make_shared(tokenValidator); auto subHandler = std::make_shared(logger, server, tokenValidator, accessCheck); auto database = std::make_shared(logger, subHandler, accessCheck); auto cmdProcessor = std::make_shared(logger, database, tokenValidator, subHandler); - if (server->Initialize(logger, - cmdProcessor, - secure, - port)) - { - database->initJsonTree(vss_filename); - server->start(); + database->initJsonTree(vss_filename); - while (1) { - usleep(1000000); - }; + if (!useNewServer) + { + oldServer->Initialize(logger, cmdProcessor, secure, port); + oldServer->start(); } else { - cerr << "Failed to initialize server" << std::endl; + + newServer->AddListener(ObserverType::WEBSOCKET, cmdProcessor); + newServer->Initialize(variables["address"].as(), port, !secure); + newServer->Start(); } + + while (1) { + usleep(1000000); + } + } catch (const program_options::error &ex) { print_usage(argv[0], desc); cerr << ex.what() << std::endl; diff --git a/w3c-visserver-api/test/testclient.cpp b/w3c-visserver-api/test/testclient.cpp index 92f1946..01b1972 100644 --- a/w3c-visserver-api/test/testclient.cpp +++ b/w3c-visserver-api/test/testclient.cpp @@ -14,19 +14,29 @@ #include "client_wss.hpp" #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace std; using namespace jsoncons; +using tcp = boost::asio::ip::tcp; // from +namespace ssl = boost::asio::ssl; // from +namespace websocket = boost::beast::websocket; // from using WssClient = SimpleWeb::SocketClient; -string url = "localhost:8090/vss"; - bool subThread = false; string getRequest(string path ) { - json req; req["requestId"] = 1234; req["action"]= "get"; @@ -34,11 +44,9 @@ string getRequest(string path ) { stringstream ss; ss << pretty_print(req); return ss.str(); - } string setRequest(string path, int val) { - json req; req["requestId"] = 1235; req["action"]= "set"; @@ -47,11 +55,9 @@ string setRequest(string path, int val) { stringstream ss; ss << pretty_print(req); return ss.str(); - } string getMetarequest( string path ) { - json req; req["requestId"] = 1236; req["action"]= "getMetadata"; @@ -62,7 +68,6 @@ string getMetarequest( string path ) { } string getSubRequest (string path) { - json req; req["requestId"] = 1237; req["action"]= "subscribe"; @@ -70,11 +75,9 @@ string getSubRequest (string path) { stringstream ss; ss << pretty_print(req); return ss.str(); - } string getAuthRequest(string token) { - json req; req["requestId"] = 1238; req["action"]= "authorize"; @@ -82,7 +85,6 @@ string getAuthRequest(string token) { stringstream ss; ss << pretty_print(req); return ss.str(); - } string getKuksaAuthRequest(string clientID , string secret) { @@ -133,15 +135,15 @@ void sendRequest(shared_ptr connection) { getline (cin, secret); command = getKuksaAuthRequest(clientid, secret); } - + auto send_stream = make_shared(); *send_stream << command; connection->send(send_stream); - } - -void startWSClient() { +void startWSClient(std::string host, int port, std::string rootDoc) { + std::string url; + url = host + ":" + to_string(port) + "/" + rootDoc; WssClient client(url , true ,"Client.pem", "Client.key","CA.pem"); @@ -150,7 +152,7 @@ void startWSClient() { sendRequest(connection); }; - client.on_open = [](shared_ptr connection) { + client.on_open = [url](shared_ptr connection) { cout << "Connection with server at " << url << " opened" << endl; sendRequest(connection); }; @@ -164,10 +166,221 @@ void startWSClient() { cout << "Error: " << ec << ", message: " << ec.message() << endl; }; -client.start(); + client.start(); } +void LoadCertData(ssl::context& ctx) { + std::string cert; + std::string key; + std::string line; + + { + ifstream inputFile ("Client.pem"); + if (inputFile.is_open()) + { + while ( getline (inputFile,line) ) + { + cert.append(line); + cert.append("\n"); + } + inputFile.close(); + } + } + { + ifstream inputFile ("Client.key"); + if (inputFile.is_open()) + { + while ( getline (inputFile,line) ) + { + key.append(line); + key.append("\n"); + } + inputFile.close(); + } + } + + ctx.use_certificate_chain( + boost::asio::buffer(cert.data(), cert.size())); + + ctx.use_private_key( + boost::asio::buffer(key.data(), key.size()), + boost::asio::ssl::context::file_format::pem); +} + +void StartBeastClient(std::string host, int port, std::string rootDoc) { + // The io_context is required for all I/O + boost::asio::io_context ioc; + + + tcp::resolver resolver{ioc}; + websocket::stream ws{ioc}; + + // Look up the domain name + auto const results = resolver.resolve(host, to_string(port)); + + // Make the connection on the IP address we get from a lookup + boost::asio::connect(ws.next_layer(), results.begin(), results.end()); + + // Perform the websocket handshake + ws.handshake(host, "/" + rootDoc); + + for(;;) { + string path, function; + cout << "Enter vss path eg : Signal.Drivetrain.InternalCombustionEngine.RPM " <> ws{ioc, ctx}; + + // Look up the domain name + auto const results = resolver.resolve(host, to_string(port)); + + // Make the connection on the IP address we get from a lookup + boost::asio::connect(ws.next_layer().next_layer(), results.begin(), results.end()); + + startWSClient(host, port, rootDoc); + + // Perform the SSL handshake + ws.next_layer().handshake(ssl::stream_base::client); + + // Perform the websocket handshake + ws.handshake(host, "/" + rootDoc); + + for(;;) { + string path, function; + cout << "Enter vss path eg : Signal.Drivetrain.InternalCombustionEngine.RPM " <()->default_value("localhost"), "Address") + ("port", boost::program_options::value()->default_value(8090), "Port"); + + try { + boost::program_options::variables_map variables; + boost::program_options::store(parse_command_line(argc, argv, desc), variables); + boost::program_options::notify(variables); + + if (variables.count("help")) { + print_usage(argv[0], desc); + return 0; + } + + if (variables.count("wss-client")) { + std::cout << "Starting Simple WebSocket client" << std::endl; + startWSClient(variables["address"].as(), variables["port"].as(), "vss"); + } + else + { + if (variables.count("insecure") > 0) + { + std::cout << "Starting Boost.Beast WebSocket client" << std::endl; + StartBeastClient(variables["address"].as(), + variables["port"].as(), + "vss"); + } + else + { + + std::cout << "Starting Secured Boost.Beast WebSocket client" << std::endl; + StartSecuredBeastClient(variables["address"].as(), + variables["port"].as(), + "vss"); + } + } -int main() { - startWSClient(); + } catch (const boost::program_options::error &ex) { + print_usage(argv[0], desc); + cerr << ex.what() << std::endl; + return -1; + } } From 2553f4995cd5c05b751a9d9e073060814dce6bea Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Mon, 14 Oct 2019 14:11:31 +0200 Subject: [PATCH 15/32] Added initial rudimentary REST API support HTTP GET request is handled, but no response yet Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/include/IRestHandler.hpp | 48 +++++ .../include/IVssCommandProcessor.hpp | 13 ++ .../include/RestV1ApiHandler.hpp | 91 +++++++++ .../include/WebSockHttpFlexServer.hpp | 9 +- w3c-visserver-api/src/RestV1ApiHandler.cpp | 193 ++++++++++++++++++ .../src/WebSockHttpFlexServer.cpp | 37 +++- w3c-visserver-api/src/main.cpp | 16 +- 7 files changed, 393 insertions(+), 14 deletions(-) create mode 100644 w3c-visserver-api/include/IRestHandler.hpp create mode 100644 w3c-visserver-api/include/RestV1ApiHandler.hpp create mode 100644 w3c-visserver-api/src/RestV1ApiHandler.cpp diff --git a/w3c-visserver-api/include/IRestHandler.hpp b/w3c-visserver-api/include/IRestHandler.hpp new file mode 100644 index 0000000..ccc1005 --- /dev/null +++ b/w3c-visserver-api/include/IRestHandler.hpp @@ -0,0 +1,48 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __IREST2JSONCONVERTER_H__ +#define __IREST2JSONCONVERTER_H__ + +#include + +/** + * @class IRestHandler + * @brief Interface class for handling REST API and convert them to default JSON + * + * @note Any class implementing this interface shall output JSON requests as defined by + * https://www.w3.org/TR/vehicle-information-service/#message-structure + */ +class IRestHandler { + public: + virtual ~IRestHandler() = default; + + /** + * @brief Get JSON request string based on input REST API request + * @param restMethod REST request method string (get/set/...) + * @param restTarget REST request URI + * @param jsonRequest Output JSON request string for both valid and invalid REST requests + * @return true if REST request correct and JSON request generated + * false otherwise indicating error in REST request. \a jsonRequest shall be updated + * with JSON response providing error details + * + * @note Function can be updated/extended/overloaded to provide additional relevant information, + * (e.g. HTTP header information, body of request with JSON, etc...) + */ + virtual bool GetJson(std::string&& restMethod, + std::string&& restTarget, + std::string& jsonRequest) = 0; +}; + + +#endif diff --git a/w3c-visserver-api/include/IVssCommandProcessor.hpp b/w3c-visserver-api/include/IVssCommandProcessor.hpp index eefcea9..0a91ac8 100644 --- a/w3c-visserver-api/include/IVssCommandProcessor.hpp +++ b/w3c-visserver-api/include/IVssCommandProcessor.hpp @@ -20,10 +20,23 @@ class WsChannel; +/** + * @class IVssCommandProcessor + * @brief Interface class for handling input JSON requests and providing response + * + * @note Any class implementing this interface shall handle JSON requests as defined by + * https://www.w3.org/TR/vehicle-information-service/#message-structure + */ class IVssCommandProcessor { public: virtual ~IVssCommandProcessor() {} + /** + * @brief Process JSON request and provide response JSON string + * @param req_json JSON formated request + * @param channel Active channel information on which \a req_json was received + * @return JSON formated response string + */ virtual std::string processQuery(const std::string &req_json, WsChannel& channel) = 0; }; diff --git a/w3c-visserver-api/include/RestV1ApiHandler.hpp b/w3c-visserver-api/include/RestV1ApiHandler.hpp new file mode 100644 index 0000000..f220987 --- /dev/null +++ b/w3c-visserver-api/include/RestV1ApiHandler.hpp @@ -0,0 +1,91 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#ifndef __REST2JSONCONVERTER___ +#define __REST2JSONCONVERTER___ + +#include +#include +#include +#include +#include + +#include + +#include "IRestHandler.hpp" + + +class ILogger; + +/** + * @class RestV1ApiHandler + * @brief Handle initial implementation of REST API for VIS server + */ +class RestV1ApiHandler : public IRestHandler { + private: + std::shared_ptr logger_; + std::string docRoot_; + std::vector resourceHandleNames_; + //const std::unordered_map resourceHandlers_; + std::string regexResources_; + std::string regexHttpMethods_; + + public: + /** + * Enumeration of all available REST API resources + */ + enum class Resources { + Signals, //!< Signals + Metadata, //!< Metadata + Count + }; + + private: + /** + * @brief Handle HTTP GET request + * @param restTarget input HTTP target string + * @param jsonRequest output JSON string + * @return true if REST request valid and valid request JSON returned + * \c false otherwise and error response JSON returned + */ + bool handleGet(std::string && restTarget, std::string& jsonRequest); + /** + * @brief Handle HTTP SET request + * @param restTarget input HTTP target string + * @param jsonRequest output JSON string + * @return true if REST request valid and valid request JSON returned + * \c false otherwise and error response JSON returned + */ + bool handleSet(std::string && restTarget, std::string& jsonRequest); + /** + * @brief Verify that HTTP target begins with correct root path and remove it if found + * @param restTarget HTTP target path + * @param path Path to verify if \p restTarget begins with + * @return true if root path found with updated \p restTarget string, false if not found + */ + bool verifyPathAndStrip(std::string& restTarget, std::string& path); + + bool verifySignalResource(std::string& restTarget, jsoncons::json& res); + + public: + RestV1ApiHandler(std::shared_ptr loggerUtil, std::string& docRoot); + ~RestV1ApiHandler(); + + // IRest2JsonConverter + + bool GetJson(std::string&& restMethod, + std::string&& restTarget, + std::string& jsonRequest); +}; + +#endif diff --git a/w3c-visserver-api/include/WebSockHttpFlexServer.hpp b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp index f29e75f..377030d 100644 --- a/w3c-visserver-api/include/WebSockHttpFlexServer.hpp +++ b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp @@ -22,6 +22,7 @@ #include class WsChannel; +class IRestHandler; class ILogger; /** @@ -35,9 +36,11 @@ class WebSockHttpFlexServer : public IServer { std::vector>> listeners_; std::mutex mutex_; std::shared_ptr logger_; + std::shared_ptr rest2json_; const uint8_t NumOfThreads = 1; bool isInitialized = false; + std::string docRoot_; /** * @brief Load server SSL certificates @@ -53,16 +56,18 @@ class WebSockHttpFlexServer : public IServer { */ std::string HandleRequest(const std::string &req_json, WsChannel &channel); public: - explicit WebSockHttpFlexServer(std::shared_ptr loggerUtil); + WebSockHttpFlexServer(std::shared_ptr loggerUtil, + std::shared_ptr rest2jsonUtil); ~WebSockHttpFlexServer(); /** * @brief Initialize Boost.Beast server * @param host Hostname for server connection * @param port Port where to wait for server connections + * @param docRoot URL path that is handled * @param allowInsecure If true, plain connections are allowed, otherwise SSL is mandatory */ - void Initialize(std::string host, int port, bool allowInsecure = false); + void Initialize(std::string host, int port, std::string && docRoot, bool allowInsecure = false); /** * @brief Start server * Server needs to be initialized before is started diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp new file mode 100644 index 0000000..ec3cab0 --- /dev/null +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -0,0 +1,193 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ + +#include "RestV1ApiHandler.hpp" + +#include +#include +#include + +#include "ILogger.hpp" + + +RestV1ApiHandler::RestV1ApiHandler(std::shared_ptr loggerUtil, std::string& docRoot) + : logger_(loggerUtil), + docRoot_(docRoot) { + + // Supported HTTP methods + regexHttpMethods_ = "^(GET|SET)"; + + // Resource strings for REST API hooks. Order must match order in RestV1ApiHandler::Resources enum + resourceHandleNames_ = std::vector{std::string{"signals"}, std::string{"metadata"}}; + // verify that sizes match + assert(resourceHandleNames_.size() == static_cast(Resources::Count)); + + // fill regex automatically with all resources + regexResources_= std::string("^("); + for (unsigned i = 0; i < resourceHandleNames_.size() - 1u; ++i) { + regexResources_ += resourceHandleNames_[i] + std::string("|"); + } + regexResources_ += resourceHandleNames_[resourceHandleNames_.size() - 1u] + std::string(")"); + +} + +RestV1ApiHandler::~RestV1ApiHandler() { +} + +bool RestV1ApiHandler::verifySignalResource(std::string& restTarget, jsoncons::json& res) { + (void) restTarget; + (void) res; + return false; +} + +bool RestV1ApiHandler::verifyPathAndStrip(std::string& restTarget, std::string& path) { + size_t pos = restTarget.find(path); + + // found doc root at correct position + if (pos == 0) { + // remove root path to leave only next level + restTarget.erase(pos, path.length()); + return true; + } + + return false; +} + +bool RestV1ApiHandler::handleGet(std::string && restTarget, std::string& jsonRequest) { + (void) jsonRequest; + (void) restTarget; + +return false; +} + +bool RestV1ApiHandler::handleSet(std::string && restTarget, std::string& jsonRequest) { + logger_->Log(LogLevel::VERBOSE, "handleGet()"); + (void)restTarget; + (void) jsonRequest; + return false; +} + +// Basic implementation of REST API handling +// In future, it should be re-factored to handle independently different methods and resources +// for easier maintenance +bool RestV1ApiHandler::GetJson(std::string&& restMethod, + std::string&& restTarget, + std::string& jsonRequest) { + bool ret = true; + std::smatch sm; + jsoncons::json json; + + logger_->Log(LogLevel::INFO, "GetJson " + restTarget); + + json["requestId"] = 1; // TODO: dummy value for now + + // search for supported HTTP requests + const std::regex regSupportedHttpActions(regexHttpMethods_, std::regex_constants::icase); + // check REST action method + std::regex_match (restMethod, sm, regSupportedHttpActions); + // if supported methods found, parse further + if (sm.size()) { + std::string foundStr = sm.str(1); + boost::algorithm::to_lower(foundStr); + json["action"] = foundStr; + + if (verifyPathAndStrip(restTarget, docRoot_)) { + const std::regex regResources(regexResources_, std::regex_constants::icase); + + // get requested resource type + std::regex_search(restTarget, sm, regResources); + if (sm.size()) { + foundStr = sm.str(1); + if (verifyPathAndStrip(restTarget, foundStr)) { + // for now KISS, as we only support single signal GET or SET HTTP method + // in future, we could allow for more complex use-cases (wildcards, multi-json set, ...) + if (foundStr == "signals") { + std::string signalPath; + std::string delimiter("/"); + + if (restTarget.size() && verifyPathAndStrip(restTarget, delimiter)) { + while (restTarget.size()) { + const std::regex regexValidWord("^([A-Za-z]+)"); + + if (std::regex_search(restTarget, sm, regexValidWord)) { + foundStr = sm.str(1); + signalPath += foundStr; + if (verifyPathAndStrip(restTarget, foundStr)) { + if ((restTarget.size() == 0)) { + break; + } + else if (verifyPathAndStrip(restTarget, delimiter)) { + signalPath += '.'; + } + else { + logger_->Log(LogLevel::ERROR, "1"); + // provide error JSON + ret = false; + } + } + else { + logger_->Log(LogLevel::ERROR, "2"); + // provide error JSON + ret = false; + } + } + else { + logger_->Log(LogLevel::ERROR, "3"); + // provide error JSON + ret = false; + } + } + } + else { + logger_->Log(LogLevel::ERROR, "4"); + // not supporting retrieving of all signals by default + // provide error JSON + ret = false; + } + + + if (ret) { + json["path"] = signalPath; + } + + } + else if (sm.str(1) == "metadata") { + + } + } + else { + // provide error JSON + ret = false; + } + } + else + { + // provide error JSON + } + } + } + else + { + // provide error JSON + } + + std::stringstream ss; + ss << pretty_print(json); + + jsonRequest = ss.str(); + + logger_->Log(LogLevel::INFO, "JSON OUTPUT " + jsonRequest); + + return ret; +} diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index 437a6a4..5e8cea5 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -34,6 +34,8 @@ #include "ssl_stream.hpp" #include "WebSockHttpFlexServer.hpp" + +#include "IRestHandler.hpp" #include "IVssCommandProcessor.hpp" #include "WsChannel.hpp" #include "ILogger.hpp" @@ -195,6 +197,7 @@ namespace { bool allowInsecureConns = false; std::shared_ptr logger; + std::shared_ptr rest2json; /**** Boost.Beast implementation below ****/ @@ -216,6 +219,7 @@ namespace { connHandler.RemoveClient(fromType); } +#if 0 // Return a reasonable mime type based on the extension of a file. boost::beast::string_view mimeType(boost::beast::string_view path) @@ -386,7 +390,7 @@ namespace { res.keep_alive(req.keep_alive()); return send(std::move(res)); } - +#endif //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ @@ -1062,8 +1066,20 @@ namespace { } // Otherwise handle pure HTTP connection - // TODO: call to appropriate REST API handler later (implemented as IVssCommandProcessor) - defaultHttpRequestHandler(doc_root_, std::move(req_), queue_); + std::string jsonRequest; + + // if we got correct JSON request back, send it to handler + if (rest2json->GetJson(std::string(req_.method_string()), + std::string(req_.target()), + jsonRequest)) + { + requestHandler_(jsonRequest, channel); + } + else + { + // write jsonRequest back as error response + //queue_(bad_request(jsonRequest)); + } // If we aren't at the queue limit, try to pipeline another request if(! queue_.is_full()) @@ -1075,7 +1091,7 @@ namespace { { // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) - return; + return;// provide error JSON if(ec) { fail<>(&derived(), ec, "write"); @@ -1487,9 +1503,12 @@ namespace { } // end namespace -WebSockHttpFlexServer::WebSockHttpFlexServer(std::shared_ptr loggerUtil) - : logger_(loggerUtil) { +WebSockHttpFlexServer::WebSockHttpFlexServer(std::shared_ptr loggerUtil, + std::shared_ptr rest2jsonUtil) + : logger_(loggerUtil), + rest2json_(rest2jsonUtil) { logger = logger_; + rest2json = rest2json_; } WebSockHttpFlexServer::~WebSockHttpFlexServer() { @@ -1500,10 +1519,10 @@ WebSockHttpFlexServer::~WebSockHttpFlexServer() { thread.join(); } } -void WebSockHttpFlexServer::Initialize(std::string host, int port, bool allowInsecure) { +void WebSockHttpFlexServer::Initialize(std::string host, int port, std::string && docRoot, bool allowInsecure) { logger_->Log(LogLevel::INFO, "Initializing Boost.Beast web-socket and http server"); - std::string const doc_root = "vss"; + docRoot_ = docRoot; allowInsecureConns = allowInsecure; @@ -1528,7 +1547,7 @@ void WebSockHttpFlexServer::Initialize(std::string host, int port, bool allowIns *ioc, ctx, resolvedHost->endpoint(), - std::move(doc_root), + std::move(docRoot_), std::bind(&WebSockHttpFlexServer::HandleRequest, this, std::placeholders::_1, diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index cb9e302..295a105 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -24,6 +24,7 @@ #include "VssDatabase.hpp" #include "exception.hpp" +#include "RestV1ApiHandler.hpp" #include "BasicLogger.hpp" #include "Authenticator.hpp" #include "AccessChecker.hpp" @@ -293,9 +294,18 @@ int main(int argc, const char *argv[]) { #endif auto logger = std::make_shared(logLevelsActive); + + // define doc root path for server.. + // for now also add 'v1' to designate version 1 of REST API as default + // in future, we can add/update REST API with new versions but also support older + // by having API versioning through URIs + std::string docRoot{"/vss/api/v1/"}; + // TODO: refactor out old server when we can remove it auto oldServer = std::make_shared(); - auto newServer = std::make_shared(logger); + + auto rest2JsonConverter = std::make_shared(logger, docRoot); + auto newServer = std::make_shared(logger, std::move(rest2JsonConverter)); std::shared_ptr server; if (!useNewServer) { @@ -321,8 +331,8 @@ int main(int argc, const char *argv[]) { else { - newServer->AddListener(ObserverType::WEBSOCKET, cmdProcessor); - newServer->Initialize(variables["address"].as(), port, !secure); + newServer->AddListener(ObserverType::ALL, cmdProcessor); + newServer->Initialize(variables["address"].as(), port, std::move(docRoot), !secure); newServer->Start(); } From 9bc5503cdc4e70f13fe57fc4e87870236c2c6c04 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Wed, 16 Oct 2019 12:37:41 +0200 Subject: [PATCH 16/32] Moved JSON response definition to separate unit Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/include/JsonResponses.hpp | 42 +++++++ w3c-visserver-api/src/JsonResponses.cpp | 104 ++++++++++++++++++ w3c-visserver-api/src/RestV1ApiHandler.cpp | 37 ++----- w3c-visserver-api/src/VssCommandProcessor.cpp | 102 +++-------------- .../src/WebSockHttpFlexServer.cpp | 22 ++-- 5 files changed, 181 insertions(+), 126 deletions(-) create mode 100644 w3c-visserver-api/include/JsonResponses.hpp create mode 100644 w3c-visserver-api/src/JsonResponses.cpp diff --git a/w3c-visserver-api/include/JsonResponses.hpp b/w3c-visserver-api/include/JsonResponses.hpp new file mode 100644 index 0000000..38db0f7 --- /dev/null +++ b/w3c-visserver-api/include/JsonResponses.hpp @@ -0,0 +1,42 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ + +#ifndef __DEFAULTJSONRESPONSES___ +#define __DEFAULTJSONRESPONSES___ + +#include +#include + +namespace JsonResponses { + std::string malFormedRequest(uint32_t request_id, + const std::string action, + std::string message); + + std::string malFormedRequest(std::string message); + + /** A API call requested a non-existant path */ + std::string pathNotFound(uint32_t request_id, + const std::string action, + const std::string path); + + std::string noAccess(uint32_t request_id, + const std::string action, + std::string message); + + std::string valueOutOfBounds(uint32_t request_id, + const std::string action, + const std::string message); +} + +#endif diff --git a/w3c-visserver-api/src/JsonResponses.cpp b/w3c-visserver-api/src/JsonResponses.cpp new file mode 100644 index 0000000..0d70daa --- /dev/null +++ b/w3c-visserver-api/src/JsonResponses.cpp @@ -0,0 +1,104 @@ +/* + * ****************************************************************************** + * Copyright (c) 2019 Robert Bosch GmbH. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/index.php + * + * Contributors: + * Robert Bosch GmbH - initial API and functionality + * ***************************************************************************** + */ +#include "JsonResponses.hpp" + +namespace JsonResponses { + std::string malFormedRequest(uint32_t request_id, + const std::string action, + std::string message) { + jsoncons::json answer; + answer["action"] = action; + answer["requestId"] = request_id; + jsoncons::json error; + error["number"] = 400; + error["reason"] = "Bad Request"; + error["message"] = message; + answer["error"] = error; + answer["timestamp"] = time(NULL); + + std::stringstream ss; + ss << pretty_print(answer); + return ss.str(); + } + + std::string malFormedRequest(std::string message) { + jsoncons::json answer; + jsoncons::json error; + + error["number"] = 400; + error["reason"] = "Bad Request"; + error["message"] = message; + answer["error"] = error; + answer["timestamp"] = time(NULL); + + std::stringstream ss; + ss << pretty_print(answer); + return ss.str(); + } + + /** A API call requested a non-existant path */ + std::string pathNotFound(uint32_t request_id, + const std::string action, + const std::string path) { + jsoncons::json answer; + answer["action"] = action; + answer["requestId"] = request_id; + jsoncons::json error; + error["number"] = 404; + error["reason"] = "Path not found"; + error["message"] = "I can not find " + path + " in my db"; + answer["error"] = error; + answer["timestamp"] = time(NULL); + + std::stringstream ss; + ss << pretty_print(answer); + return ss.str(); + } + + std::string noAccess(uint32_t request_id, + const std::string action, + std::string message) { + jsoncons::json result; + jsoncons::json error; + result["action"] = action; + result["requestId"] = request_id; + error["number"] = 403; + error["reason"] = "Forbidden"; + error["message"] = message; + result["error"] = error; + result["timestamp"] = time(NULL); + + std::stringstream ss; + ss << pretty_print(result); + return ss.str(); + } + + std::string valueOutOfBounds(uint32_t request_id, + const std::string action, + const std::string message) { + jsoncons::json answer; + answer["action"] = action; + answer["requestId"] = request_id; + jsoncons::json error; + error["number"] = 400; + error["reason"] = "Value passed is out of bounds"; + error["message"] = message; + answer["error"] = error; + answer["timestamp"] = time(NULL); + + std::stringstream ss; + ss << pretty_print(answer); + return ss.str(); + } +} diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp index ec3cab0..bfc0154 100644 --- a/w3c-visserver-api/src/RestV1ApiHandler.cpp +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -18,6 +18,7 @@ #include #include +#include "JsonResponses.hpp" #include "ILogger.hpp" @@ -45,12 +46,6 @@ RestV1ApiHandler::RestV1ApiHandler(std::shared_ptr loggerUtil, std::str RestV1ApiHandler::~RestV1ApiHandler() { } -bool RestV1ApiHandler::verifySignalResource(std::string& restTarget, jsoncons::json& res) { - (void) restTarget; - (void) res; - return false; -} - bool RestV1ApiHandler::verifyPathAndStrip(std::string& restTarget, std::string& path) { size_t pos = restTarget.find(path); @@ -64,20 +59,6 @@ bool RestV1ApiHandler::verifyPathAndStrip(std::string& restTarget, std::string& return false; } -bool RestV1ApiHandler::handleGet(std::string && restTarget, std::string& jsonRequest) { - (void) jsonRequest; - (void) restTarget; - -return false; -} - -bool RestV1ApiHandler::handleSet(std::string && restTarget, std::string& jsonRequest) { - logger_->Log(LogLevel::VERBOSE, "handleGet()"); - (void)restTarget; - (void) jsonRequest; - return false; -} - // Basic implementation of REST API handling // In future, it should be re-factored to handle independently different methods and resources // for easier maintenance @@ -132,19 +113,19 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, } else { logger_->Log(LogLevel::ERROR, "1"); - // provide error JSON + // TODO: provide error JSON ret = false; } } else { logger_->Log(LogLevel::ERROR, "2"); - // provide error JSON + // TODO: provide error JSON ret = false; } } else { logger_->Log(LogLevel::ERROR, "3"); - // provide error JSON + // TODO: provide error JSON ret = false; } } @@ -152,7 +133,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, else { logger_->Log(LogLevel::ERROR, "4"); // not supporting retrieving of all signals by default - // provide error JSON + // TODO: provide error JSON ret = false; } @@ -163,23 +144,23 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, } else if (sm.str(1) == "metadata") { - + // TODO: } } else { - // provide error JSON + // TODO: provide error JSON ret = false; } } else { - // provide error JSON + // TODO: provide error JSON } } } else { - // provide error JSON + // TODO: provide error JSON } std::stringstream ss; diff --git a/w3c-visserver-api/src/VssCommandProcessor.cpp b/w3c-visserver-api/src/VssCommandProcessor.cpp index 4ed9625..06472e2 100644 --- a/w3c-visserver-api/src/VssCommandProcessor.cpp +++ b/w3c-visserver-api/src/VssCommandProcessor.cpp @@ -22,6 +22,8 @@ #include "permmclient.hpp" #include "exception.hpp" #include "server_ws.hpp" + +#include "JsonResponses.hpp" #include "visconf.hpp" #include "VssDatabase.hpp" #include "AccessChecker.hpp" @@ -37,84 +39,6 @@ using namespace std; -string malFormedRequestResponse(uint32_t request_id, const string action, string message) { - jsoncons::json answer; - answer["action"] = action; - answer["requestId"] = request_id; - jsoncons::json error; - error["number"] = 400; - error["reason"] = "Bad Request"; - error["message"] = message; - answer["error"] = error; - answer["timestamp"] = time(NULL); - stringstream ss; - ss << pretty_print(answer); - return ss.str(); -} - -string malFormedRequestResponse(string message) { - jsoncons::json answer; - jsoncons::json error; - - error["number"] = 400; - error["reason"] = "Bad Request"; - error["message"] = message; - answer["error"] = error; - answer["timestamp"] = time(NULL); - stringstream ss; - ss << pretty_print(answer); - return ss.str(); -} - -/** A API call requested a non-existant path */ -string pathNotFoundResponse(uint32_t request_id, const string action, - const string path) { - jsoncons::json answer; - answer["action"] = action; - answer["requestId"] = request_id; - jsoncons::json error; - error["number"] = 404; - error["reason"] = "Path not found"; - error["message"] = "I can not find " + path + " in my db"; - answer["error"] = error; - answer["timestamp"] = time(NULL); - stringstream ss; - ss << pretty_print(answer); - return ss.str(); -} - -string noAccessResponse(uint32_t request_id, const string action, - string message) { - jsoncons::json result; - jsoncons::json error; - result["action"] = action; - result["requestId"] = request_id; - error["number"] = 403; - error["reason"] = "Forbidden"; - error["message"] = message; - result["error"] = error; - result["timestamp"] = time(NULL); - std::stringstream ss; - ss << pretty_print(result); - return ss.str(); -} - -string valueOutOfBoundsResponse(uint32_t request_id, const string action, - const string message) { - jsoncons::json answer; - answer["action"] = action; - answer["requestId"] = request_id; - jsoncons::json error; - error["number"] = 400; - error["reason"] = "Value passed is out of bounds"; - error["message"] = message; - answer["error"] = error; - answer["timestamp"] = time(NULL); - stringstream ss; - ss << pretty_print(answer); - return ss.str(); -} - VssCommandProcessor::VssCommandProcessor( std::shared_ptr loggerUtil, std::shared_ptr dbase, @@ -147,10 +71,10 @@ string VssCommandProcessor::processGet(WsChannel &channel, res = database->getSignal(channel, path); } catch (noPermissionException &nopermission) { logger->Log(LogLevel::ERROR, string(nopermission.what())); - return noAccessResponse(request_id, "get", nopermission.what()); + return JsonResponses::noAccess(request_id, "get", nopermission.what()); } if (!res.has_key("value")) { - return pathNotFoundResponse(request_id, "get", path); + return JsonResponses::pathNotFound(request_id, "get", path); } else { res["action"] = "get"; res["requestId"] = request_id; @@ -188,13 +112,13 @@ string VssCommandProcessor::processSet(WsChannel &channel, return ss.str(); } catch (noPathFoundonTree &e) { logger->Log(LogLevel::ERROR, string(e.what())); - return pathNotFoundResponse(request_id, "set", path); + return JsonResponses::pathNotFound(request_id, "set", path); } catch (outOfBoundException &outofboundExp) { logger->Log(LogLevel::ERROR, string(outofboundExp.what())); - return valueOutOfBoundsResponse(request_id, "set", outofboundExp.what()); + return JsonResponses::valueOutOfBounds(request_id, "set", outofboundExp.what()); } catch (noPermissionException &nopermission) { logger->Log(LogLevel::ERROR, string(nopermission.what())); - return noAccessResponse(request_id, "set", nopermission.what()); + return JsonResponses::noAccess(request_id, "set", nopermission.what()); } jsoncons::json answer; @@ -217,14 +141,14 @@ string VssCommandProcessor::processSubscribe(WsChannel &channel, subId = subHandler->subscribe(channel, database, path); } catch (noPathFoundonTree &noPathFound) { logger->Log(LogLevel::ERROR, string(noPathFound.what())); - return pathNotFoundResponse(request_id, "subscribe", path); + return JsonResponses::pathNotFound(request_id, "subscribe", path); } catch (genException &outofboundExp) { logger->Log(LogLevel::ERROR, string(outofboundExp.what())); - return valueOutOfBoundsResponse(request_id, "subscribe", + return JsonResponses::valueOutOfBounds(request_id, "subscribe", outofboundExp.what()); } catch (noPermissionException &nopermission) { logger->Log(LogLevel::ERROR, string(nopermission.what())); - return noAccessResponse(request_id, "subscribe", nopermission.what()); + return JsonResponses::noAccess(request_id, "subscribe", nopermission.what()); } if (subId > 0) { @@ -471,11 +395,11 @@ string VssCommandProcessor::processQuery(const string &req_json, } } } catch (jsoncons::json_parse_exception e) { - return malFormedRequestResponse(e.what()); + return JsonResponses::malFormedRequest(e.what()); } catch (jsoncons::key_not_found e) { - return malFormedRequestResponse(e.what()); + return JsonResponses::malFormedRequest(e.what()); } catch (jsoncons::not_an_object e) { - return malFormedRequestResponse(e.what()); + return JsonResponses::malFormedRequest(e.what()); } diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index 5e8cea5..b560c7f 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -199,6 +199,10 @@ namespace { std::shared_ptr logger; std::shared_ptr rest2json; + const unsigned DEFAULT_TIMEOUT_VALUE = 60u; // in seconds + const unsigned WEBSOCKET_TIMEOUT_VALUE = DEFAULT_TIMEOUT_VALUE; + const unsigned HTTP_TIMEOUT_VALUE = DEFAULT_TIMEOUT_VALUE; + /**** Boost.Beast implementation below ****/ @@ -446,7 +450,7 @@ namespace { std::placeholders::_2)); // Set the timer - timer_.expires_after(std::chrono::seconds(15)); + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); // Accept the websocket handshake derived().ws().async_accept( @@ -495,7 +499,7 @@ namespace { ping_state_ = 1; // Set the timer - timer_.expires_after(std::chrono::seconds(15)); + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); // Now send the ping derived().ws().async_ping({}, @@ -535,7 +539,7 @@ namespace { ping_state_ = 0; // Set the timer - timer_.expires_after(std::chrono::seconds(15)); + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); } // Called after a ping is sent. @@ -715,7 +719,7 @@ namespace { close_ = true; // Set the timer - timer_.expires_after(std::chrono::seconds(15)); + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); // Close the WebSocket Connection ws_.async_close( @@ -793,7 +797,7 @@ namespace { eof_ = true; // Set the timer - timer_.expires_after(std::chrono::seconds(15)); + timer_.expires_after(std::chrono::seconds(WEBSOCKET_TIMEOUT_VALUE)); // Perform the SSL shutdown ws_.next_layer().async_shutdown( @@ -1000,7 +1004,7 @@ namespace { doRead() { // Set the timer - timer_.expires_after(std::chrono::seconds(15)); + timer_.expires_after(std::chrono::seconds(HTTP_TIMEOUT_VALUE)); // Make the request empty before reading, // otherwise the operation behavior is undefined. @@ -1244,7 +1248,7 @@ namespace { //onTimer({}); // Set the timer - timer_.expires_after(std::chrono::seconds(15)); + timer_.expires_after(std::chrono::seconds(HTTP_TIMEOUT_VALUE)); // Perform the SSL handshake // Note, this is the buffered version of the handshake. @@ -1283,7 +1287,7 @@ namespace { eof_ = true; // Set the timer - timer_.expires_after(std::chrono::seconds(15)); + timer_.expires_after(std::chrono::seconds(HTTP_TIMEOUT_VALUE)); // Perform the SSL shutdown stream_.async_shutdown( @@ -1320,7 +1324,7 @@ namespace { timer_.expires_at( (std::chrono::steady_clock::time_point::max)()); onTimer({}); - doEof(); + //doEof(); } }; From e13bd0e265b11fbada0a48d8c4f6f628db381f0e Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Wed, 16 Oct 2019 13:58:41 +0200 Subject: [PATCH 17/32] REST API supports GET HTTP requests for signals and return results When server is started, REST resource is now available through /vss/api/v1/signals/PATH/TO/SIGNAL Result of request is returned as 'text/json' content type in a same way as for web-socket. Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/include/IRestHandler.hpp | 9 +- .../include/RestV1ApiHandler.hpp | 2 +- .../include/WebSockHttpFlexServer.hpp | 2 +- w3c-visserver-api/src/RestV1ApiHandler.cpp | 73 +++--- .../src/WebSockHttpFlexServer.cpp | 212 +++--------------- w3c-visserver-api/src/main.cpp | 7 + 6 files changed, 90 insertions(+), 215 deletions(-) diff --git a/w3c-visserver-api/include/IRestHandler.hpp b/w3c-visserver-api/include/IRestHandler.hpp index ccc1005..7cccb1e 100644 --- a/w3c-visserver-api/include/IRestHandler.hpp +++ b/w3c-visserver-api/include/IRestHandler.hpp @@ -11,8 +11,8 @@ * Robert Bosch GmbH - initial API and functionality * ***************************************************************************** */ -#ifndef __IREST2JSONCONVERTER_H__ -#define __IREST2JSONCONVERTER_H__ +#ifndef __IRESTHANDLER_H__ +#define __IRESTHANDLER_H__ #include @@ -31,7 +31,8 @@ class IRestHandler { * @brief Get JSON request string based on input REST API request * @param restMethod REST request method string (get/set/...) * @param restTarget REST request URI - * @param jsonRequest Output JSON request string for both valid and invalid REST requests + * @param resultJson Output JSON string for both valid and invalid REST requests. If invalid REST + * request, parameter shall contain JSON describing error * @return true if REST request correct and JSON request generated * false otherwise indicating error in REST request. \a jsonRequest shall be updated * with JSON response providing error details @@ -41,7 +42,7 @@ class IRestHandler { */ virtual bool GetJson(std::string&& restMethod, std::string&& restTarget, - std::string& jsonRequest) = 0; + std::string& resultJson) = 0; }; diff --git a/w3c-visserver-api/include/RestV1ApiHandler.hpp b/w3c-visserver-api/include/RestV1ApiHandler.hpp index f220987..e6463c7 100644 --- a/w3c-visserver-api/include/RestV1ApiHandler.hpp +++ b/w3c-visserver-api/include/RestV1ApiHandler.hpp @@ -85,7 +85,7 @@ class RestV1ApiHandler : public IRestHandler { bool GetJson(std::string&& restMethod, std::string&& restTarget, - std::string& jsonRequest); + std::string& resultJson); }; #endif diff --git a/w3c-visserver-api/include/WebSockHttpFlexServer.hpp b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp index 377030d..7d9a155 100644 --- a/w3c-visserver-api/include/WebSockHttpFlexServer.hpp +++ b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp @@ -36,7 +36,7 @@ class WebSockHttpFlexServer : public IServer { std::vector>> listeners_; std::mutex mutex_; std::shared_ptr logger_; - std::shared_ptr rest2json_; + std::shared_ptr restHandler_; const uint8_t NumOfThreads = 1; bool isInitialized = false; diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp index bfc0154..7585fce 100644 --- a/w3c-visserver-api/src/RestV1ApiHandler.cpp +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "JsonResponses.hpp" #include "ILogger.hpp" @@ -34,13 +35,12 @@ RestV1ApiHandler::RestV1ApiHandler(std::shared_ptr loggerUtil, std::str // verify that sizes match assert(resourceHandleNames_.size() == static_cast(Resources::Count)); - // fill regex automatically with all resources + // fill regex automatically with all available resources regexResources_= std::string("^("); for (unsigned i = 0; i < resourceHandleNames_.size() - 1u; ++i) { regexResources_ += resourceHandleNames_[i] + std::string("|"); } regexResources_ += resourceHandleNames_[resourceHandleNames_.size() - 1u] + std::string(")"); - } RestV1ApiHandler::~RestV1ApiHandler() { @@ -60,8 +60,9 @@ bool RestV1ApiHandler::verifyPathAndStrip(std::string& restTarget, std::string& } // Basic implementation of REST API handling -// In future, it should be re-factored to handle independently different methods and resources -// for easier maintenance +// For now governed by KISS principle, but in future, it should be re-factored +// to handle independently different methods and resources for easier maintenance.. +// possibly by providing hooks for each resource and API version bool RestV1ApiHandler::GetJson(std::string&& restMethod, std::string&& restTarget, std::string& jsonRequest) { @@ -69,9 +70,9 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, std::smatch sm; jsoncons::json json; - logger_->Log(LogLevel::INFO, "GetJson " + restTarget); - - json["requestId"] = 1; // TODO: dummy value for now + // TODO: should client provide request ID when using REST API? + uint32_t requestId = std::rand() % std::numeric_limits::max(); + json["requestId"] = requestId; // search for supported HTTP requests const std::regex regSupportedHttpActions(regexHttpMethods_, std::regex_constants::icase); @@ -81,6 +82,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, if (sm.size()) { std::string foundStr = sm.str(1); boost::algorithm::to_lower(foundStr); + json["action"] = foundStr; if (verifyPathAndStrip(restTarget, docRoot_)) { @@ -90,9 +92,8 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, std::regex_search(restTarget, sm, regResources); if (sm.size()) { foundStr = sm.str(1); + if (verifyPathAndStrip(restTarget, foundStr)) { - // for now KISS, as we only support single signal GET or SET HTTP method - // in future, we could allow for more complex use-cases (wildcards, multi-json set, ...) if (foundStr == "signals") { std::string signalPath; std::string delimiter("/"); @@ -112,63 +113,83 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, signalPath += '.'; } else { - logger_->Log(LogLevel::ERROR, "1"); - // TODO: provide error JSON + jsonRequest = JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path delimiter not valid"); ret = false; } } else { - logger_->Log(LogLevel::ERROR, "2"); - // TODO: provide error JSON + jsonRequest = JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path not valid"); ret = false; } } else { - logger_->Log(LogLevel::ERROR, "3"); - // TODO: provide error JSON + jsonRequest = JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path URI not valid"); ret = false; } } } else { - logger_->Log(LogLevel::ERROR, "4"); // not supporting retrieving of all signals by default - // TODO: provide error JSON + jsonRequest = JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signals cannot be retrieved in bulk request, only through single signal requests"); ret = false; } - + // update signal path if all is OK if (ret) { json["path"] = signalPath; } - } else if (sm.str(1) == "metadata") { - // TODO: + // TODO: add support for metadata + jsonRequest = JsonResponses::noAccess( + requestId, + json["action"].as_string(), + "Access to metadata not yet supported"); } } else { - // TODO: provide error JSON + jsonRequest = JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path URI not valid"); ret = false; } } else { - // TODO: provide error JSON + jsonRequest = JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Requested resource do not exist"); + ret = false; } } } else { - // TODO: provide error JSON + // TODO: evaluate what and how we should support HTTP methods (put, patch, ...) + jsonRequest = JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Requested HTTP method is not supported"); + ret = false; } std::stringstream ss; ss << pretty_print(json); - jsonRequest = ss.str(); - logger_->Log(LogLevel::INFO, "JSON OUTPUT " + jsonRequest); - return ret; } diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index b560c7f..7659e4a 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -197,7 +197,7 @@ namespace { bool allowInsecureConns = false; std::shared_ptr logger; - std::shared_ptr rest2json; + std::shared_ptr restHandler; const unsigned DEFAULT_TIMEOUT_VALUE = 60u; // in seconds const unsigned WEBSOCKET_TIMEOUT_VALUE = DEFAULT_TIMEOUT_VALUE; @@ -223,182 +223,7 @@ namespace { connHandler.RemoveClient(fromType); } -#if 0 - // Return a reasonable mime type based on the extension of a file. - boost::beast::string_view - mimeType(boost::beast::string_view path) - { - using boost::beast::iequals; - auto const ext = [&path] - { - auto const pos = path.rfind("."); - if(pos == boost::beast::string_view::npos) - return boost::beast::string_view{}; - return path.substr(pos); - }(); - if(iequals(ext, ".htm")) return "text/html"; - if(iequals(ext, ".html")) return "text/html"; - if(iequals(ext, ".php")) return "text/html"; - if(iequals(ext, ".css")) return "text/css"; - if(iequals(ext, ".txt")) return "text/plain"; - if(iequals(ext, ".js")) return "application/javascript"; - if(iequals(ext, ".json")) return "application/json"; - if(iequals(ext, ".xml")) return "application/xml"; - if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; - if(iequals(ext, ".flv")) return "video/x-flv"; - if(iequals(ext, ".png")) return "image/png"; - if(iequals(ext, ".jpe")) return "image/jpeg"; - if(iequals(ext, ".jpeg")) return "image/jpeg"; - if(iequals(ext, ".jpg")) return "image/jpeg"; - if(iequals(ext, ".gif")) return "image/gif"; - if(iequals(ext, ".bmp")) return "image/bmp"; - if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; - if(iequals(ext, ".tiff")) return "image/tiff"; - if(iequals(ext, ".tif")) return "image/tiff"; - if(iequals(ext, ".svg")) return "image/svg+xml"; - if(iequals(ext, ".svgz")) return "image/svg+xml"; - return "application/text"; - } - - // Append an HTTP rel-path to a local filesystem path. - // The returned path is normalized for the platform. - std::string - fixPath( - boost::beast::string_view base, - boost::beast::string_view path) - { - if(base.empty()) - return path.to_string(); - std::string result = base.to_string(); - #if BOOST_MSVC - char constexpr path_separator = '\\'; - if(result.back() == path_separator) - result.resize(result.size() - 1); - result.append(path.data(), path.size()); - for(auto& c : result) - if(c == '/') - c = path_separator; - #else - char constexpr path_separator = '/'; - if(result.back() == path_separator) - result.resize(result.size() - 1); - result.append(path.data(), path.size()); - #endif - return result; - } - - // This function produces an HTTP response for the given - // request. The type of the response object depends on the - // contents of the request, so the interface requires the - // caller to pass a generic lambda for receiving the response. - // TODO: in future this shall be replaced with REST handler as IVssCommandProcessor listener - template< - class Body, class Allocator, - class Send> - void - defaultHttpRequestHandler( - boost::beast::string_view doc_root, - http::request>&& req, - Send&& send) - { - // Returns a bad request response - auto const bad_request = - [&req](boost::beast::string_view why) - { - http::response res{http::status::bad_request, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = why.to_string(); - res.prepare_payload(); - return res; - }; - - // Returns a not found response - auto const not_found = - [&req](boost::beast::string_view target) - { - http::response res{http::status::not_found, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "The resource '" + target.to_string() + "' was not found."; - res.prepare_payload(); - return res; - }; - - // Returns a server error response - auto const server_error = - [&req](boost::beast::string_view what) - { - http::response res{http::status::internal_server_error, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "An error occurred: '" + what.to_string() + "'"; - res.prepare_payload(); - return res; - }; - - // Make sure we can handle the method - if( req.method() != http::verb::get && - req.method() != http::verb::head) - return send(bad_request("Unknown HTTP-method")); - - // Request path must be absolute and not contain "..". - if( req.target().empty() || - req.target()[0] != '/' || - req.target().find("..") != boost::beast::string_view::npos) - return send(bad_request("Illegal request-target")); - - // Build the path to the requested file - std::string path = fixPath(doc_root, req.target()); - if(req.target().back() == '/') - path.append("index.html"); - - // Attempt to open the file - boost::beast::error_code ec; - http::file_body::value_type body; - body.open(path.c_str(), boost::beast::file_mode::scan, ec); - - // Handle the case where the file doesn't exist - if(ec == boost::system::errc::no_such_file_or_directory) - return send(not_found(req.target())); - - // Handle an unknown error - if(ec) - return send(server_error(ec.message())); - - // Cache the size since we need it after the move - auto const size = body.size(); - - // Respond to HEAD request - if(req.method() == http::verb::head) - { - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, mimeType(path)); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); - } - - // Respond to GET request - http::response res{ - std::piecewise_construct, - std::make_tuple(std::move(body)), - std::make_tuple(http::status::ok, req.version())}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, mimeType(path)); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); - } -#endif //------------------------------------------------------------------------------ - - //------------------------------------------------------------------------------ - // Echoes back all received WebSocket messages. // This uses the Curiously Recurring Template Pattern so that // the same code works with both SSL streams and regular sockets. @@ -1073,16 +898,37 @@ namespace { std::string jsonRequest; // if we got correct JSON request back, send it to handler - if (rest2json->GetJson(std::string(req_.method_string()), + if (restHandler->GetJson(std::string(req_.method_string()), std::string(req_.target()), jsonRequest)) { - requestHandler_(jsonRequest, channel); + auto response = requestHandler_(jsonRequest, channel); + // Respond to GET request + http::response res{ + std::piecewise_construct, + std::make_tuple(response), + std::make_tuple(http::status::ok, req_.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/json"); + res.content_length(response.size()); + res.keep_alive(req_.keep_alive()); + queue_(std::move(res)); } else { // write jsonRequest back as error response - //queue_(bad_request(jsonRequest)); + auto const bad_request = + [=](std::string why) + { + http::response res{http::status::bad_request, req_.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/json"); + res.keep_alive(req_.keep_alive()); + res.body() = why; + res.prepare_payload(); + return res; + }; + queue_(bad_request(jsonRequest)); } // If we aren't at the queue limit, try to pipeline another request @@ -1508,11 +1354,11 @@ namespace { WebSockHttpFlexServer::WebSockHttpFlexServer(std::shared_ptr loggerUtil, - std::shared_ptr rest2jsonUtil) + std::shared_ptr restHandlerUtil) : logger_(loggerUtil), - rest2json_(rest2jsonUtil) { + restHandler_(restHandlerUtil) { logger = logger_; - rest2json = rest2json_; + restHandler = restHandler_; } WebSockHttpFlexServer::~WebSockHttpFlexServer() { @@ -1610,7 +1456,7 @@ void WebSockHttpFlexServer::LoadCertData(boost::asio::ssl::context& ctx) { std::string key; std::string line; - // read certicate + // read certificate { std::ifstream inputFile ("Server.pem"); if (inputFile.is_open()) diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index 295a105..2f245c0 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include #include @@ -258,6 +261,10 @@ int main(int argc, const char *argv[]) { print_usage(argv[0], desc); return -1; } + + // initialize pseudo random number generator + std::srand(std::time(nullptr)); + auto port = variables["port"].as(); auto secure = !variables.count("insecure"); auto vss_filename = variables["vss"].as(); From ea9a6d968d694e52e549177dad8c189e8d64fe50 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Wed, 16 Oct 2019 13:58:41 +0200 Subject: [PATCH 18/32] REST API supports GET HTTP requests for signals and return results When server is started, REST resource is now available through /vss/api/v1/signals/PATH/TO/SIGNAL Result of request is returned as 'text/json' content type in a same way as for web-socket. Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 1 + w3c-visserver-api/src/main.cpp | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index 4d6bf62..198342d 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -116,6 +116,7 @@ if(UNIT_TEST) ${CMAKE_CURRENT_SOURCE_DIR}/src/WsServer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/BasicLogger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/permmclient.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/JsonResponses.cpp # ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/VssDatabase_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/unit-test/w3cunittest.cpp ) diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index 2f245c0..8dd351c 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -34,8 +34,6 @@ #include "SubscriptionHandler.hpp" #include "VssCommandProcessor.hpp" #include "VssDatabase.hpp" - -#include "WsServer.hpp" #include "WebSockHttpFlexServer.hpp" From 77a3f2616df8c8b6a8dbc785568b70540b9a64e3 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Thu, 17 Oct 2019 09:13:42 +0200 Subject: [PATCH 19/32] Input params update for W3C VIS server * Added log level as input. Different log levels can be combined by providing param multiple times with different params * Added configuration file option as input parallel to command line options * Added certificates path option where 'Server.pem' and 'Server.key' are located Signed-off-by: Miladinovic Bojan --- .../include/WebSockHttpFlexServer.hpp | 18 ++++- .../src/WebSockHttpFlexServer.cpp | 40 ++++++++-- w3c-visserver-api/src/main.cpp | 80 ++++++++++++++++--- 3 files changed, 116 insertions(+), 22 deletions(-) diff --git a/w3c-visserver-api/include/WebSockHttpFlexServer.hpp b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp index 7d9a155..7c28289 100644 --- a/w3c-visserver-api/include/WebSockHttpFlexServer.hpp +++ b/w3c-visserver-api/include/WebSockHttpFlexServer.hpp @@ -38,16 +38,23 @@ class WebSockHttpFlexServer : public IServer { std::shared_ptr logger_; std::shared_ptr restHandler_; - const uint8_t NumOfThreads = 1; bool isInitialized = false; std::string docRoot_; + const uint8_t NumOfThreads = 1; + + /// Default name for server certificate file + static const std::string serverCertFilename_; + /// Default name for server key file + static const std::string serverKeyFilename_; + /** * @brief Load server SSL certificates + * @param certPath Directory path where 'Server.pem' and 'Server.key' are located * @param ctx ssl context to which certificates will be added * @note 'Server.pem' and 'Server.key' needs to be located with executable */ - void LoadCertData(boost::asio::ssl::context& ctx); + void LoadCertData(std::string & certPath, boost::asio::ssl::context& ctx); /** * @brief Handle incoming data requests * @param req_json Request message from connection @@ -65,9 +72,14 @@ class WebSockHttpFlexServer : public IServer { * @param host Hostname for server connection * @param port Port where to wait for server connections * @param docRoot URL path that is handled + * @param certPath Directory path where 'Server.pem' and 'Server.key' are located * @param allowInsecure If true, plain connections are allowed, otherwise SSL is mandatory */ - void Initialize(std::string host, int port, std::string && docRoot, bool allowInsecure = false); + void Initialize(std::string host, + int port, + std::string && docRoot, + std::string certPath, + bool allowInsecure = false); /** * @brief Start server * Server needs to be initialized before is started diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index 7659e4a..4727c16 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -1353,6 +1353,10 @@ namespace { } // end namespace +const std::string WebSockHttpFlexServer::serverCertFilename_ = "Server.pem"; +const std::string WebSockHttpFlexServer::serverKeyFilename_ = "Server.key"; + + WebSockHttpFlexServer::WebSockHttpFlexServer(std::shared_ptr loggerUtil, std::shared_ptr restHandlerUtil) : logger_(loggerUtil), @@ -1369,7 +1373,11 @@ WebSockHttpFlexServer::~WebSockHttpFlexServer() { thread.join(); } } -void WebSockHttpFlexServer::Initialize(std::string host, int port, std::string && docRoot, bool allowInsecure) { +void WebSockHttpFlexServer::Initialize(std::string host, + int port, + std::string && docRoot, + std::string certPath, + bool allowInsecure) { logger_->Log(LogLevel::INFO, "Initializing Boost.Beast web-socket and http server"); docRoot_ = docRoot; @@ -1384,7 +1392,7 @@ void WebSockHttpFlexServer::Initialize(std::string host, int port, std::string & boost::asio::ip::tcp::resolver::iterator resolvedHost = resolver.resolve(query); // load required certificates for SSL connections - LoadCertData(ctx); + LoadCertData(certPath, ctx); // handling function redirecting requests to registered listeners RequestHandler reqHndl = std::bind(&WebSockHttpFlexServer::HandleRequest, @@ -1451,15 +1459,26 @@ void WebSockHttpFlexServer::RemoveListener(ObserverType type, logger_->Log(LogLevel::WARNING, "Could not find listener to remove. Ignoring..."); } -void WebSockHttpFlexServer::LoadCertData(boost::asio::ssl::context& ctx) { +void WebSockHttpFlexServer::LoadCertData(std::string & certPath, boost::asio::ssl::context& ctx) { std::string cert; std::string key; std::string line; + bool isValid = false; + + std::string delimiter = "/"; + + // if path not passed with '/' at the end, no update needed + // otherwise we need to provide path delimiter + if (certPath[certPath.size() - 1] == '/') + { + delimiter = ""; + } // read certificate { - std::ifstream inputFile ("Server.pem"); - if (inputFile.is_open()) + std::ifstream inputFile (certPath + delimiter + serverCertFilename_); + isValid = inputFile.good(); + if (isValid && inputFile.is_open()) { while (getline(inputFile, line) ) { @@ -1468,11 +1487,15 @@ void WebSockHttpFlexServer::LoadCertData(boost::asio::ssl::context& ctx) { } inputFile.close(); } + else { + throw std::runtime_error("Could not load server certificate"); + } } // read key { - std::ifstream inputFile ("Server.key"); - if (inputFile.is_open()) + std::ifstream inputFile (certPath + delimiter + serverKeyFilename_); + isValid = inputFile.good(); + if (isValid && inputFile.is_open()) { while (getline(inputFile, line) ) { @@ -1481,6 +1504,9 @@ void WebSockHttpFlexServer::LoadCertData(boost::asio::ssl::context& ctx) { } inputFile.close(); } + else { + throw std::runtime_error("Could not load server key"); + } } // add cert and key to security context for using with new connections diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index 8dd351c..85b7481 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -229,39 +230,88 @@ on_name_lost (GDBusConnection *connection, static void print_usage(const char *prog_name, - program_options::options_description desc) { + program_options::options_description& desc) { cerr << "Usage: " << prog_name << " OPTIONS" << endl; cerr << desc << std::endl; } int main(int argc, const char *argv[]) { + string configFile; + vector logLevels{"NONE"}; + uint8_t logLevelsActive = static_cast(LogLevel::NONE); + program_options::options_description desc{"Options"}; desc.add_options() ("help,h", "Help screen") + ("config-file,cfg", program_options::value(&configFile), + "Configuration file path for program parameters. " + "Can be provided instead of command line options") ("vss", program_options::value(), "vss_rel*.json file") + ("cert-path", program_options::value(), + "Path to directory where 'Server.pem' and 'Server.key' are located") ("insecure", "Run insecure") ("wss-server", "Run old WSS server handler") + ("address", program_options::value()->default_value("localhost"), "Address") ("port", program_options::value()->default_value(8090), "Port") - ("address", program_options::value()->default_value("localhost"), "Address"); + ("log-level", program_options::value>(&logLevels)->composing(), + "Log level event type to be enabled. " + "To enable different log levels, provide this option multiple times with required log levels. \n" + "Supported log levels: NONE, VERBOSE, INFO, WARNING, ERROR, ALL"); try { program_options::variables_map variables; program_options::store(parse_command_line(argc, argv, desc), variables); program_options::notify(variables); + // if config file passed, get configuration from it + if (configFile.size()) { + cout << configFile << std::endl; + std::ifstream ifs(configFile.c_str()); + if(!ifs) + { + std::cerr << "Could not open config file: " << configFile << std::endl; + return -1; + } + program_options::store(parse_config_file(ifs, desc), variables); + } + program_options::notify(variables); + + // verify parameters if (!variables.count("vss")) { print_usage(argv[0], desc); - cerr << "vss file (--vss) must be specified" << std::endl; + cerr << "the option '--vss' is required but missing" << std::endl; + return -1; + } + if (!variables.count("cert-path")) { + cerr << "the option '--cert-path' is required but missing" << std::endl; + print_usage(argv[0], desc); return -1; } - if (variables.count("help")) { print_usage(argv[0], desc); return -1; } - // initialize pseudo random number generator - std::srand(std::time(nullptr)); + for (auto const& token : logLevels) { + if (token == "NONE") + logLevelsActive |= static_cast(LogLevel::NONE); + else if (token == "VERBOSE") + logLevelsActive |= static_cast(LogLevel::VERBOSE); + else if (token == "INFO") + logLevelsActive |= static_cast(LogLevel::INFO); + else if (token == "WARNING") + logLevelsActive |= static_cast(LogLevel::WARNING); + else if (token == "ERROR") + logLevelsActive |= static_cast(LogLevel::ERROR); + else if (token == "ALL") + logLevelsActive |= static_cast(LogLevel::ALL); + else { + cerr << "Invalid input parameter for LogLevel" << std::endl; + return -1; + } + } + + // initialize server auto port = variables["port"].as(); auto secure = !variables.count("insecure"); @@ -297,6 +347,8 @@ int main(int argc, const char *argv[]) { #else logLevelsActive = static_cast(LogLevel::INFO & LogLevel::WARNING & LogLevel::ERROR); #endif + // initialize pseudo random number generator + std::srand(std::time(nullptr)); auto logger = std::make_shared(logLevelsActive); @@ -328,16 +380,17 @@ int main(int argc, const char *argv[]) { database->initJsonTree(vss_filename); - if (!useNewServer) - { + if (!useNewServer) { oldServer->Initialize(logger, cmdProcessor, secure, port); oldServer->start(); } - else - { - + else { newServer->AddListener(ObserverType::ALL, cmdProcessor); - newServer->Initialize(variables["address"].as(), port, std::move(docRoot), !secure); + newServer->Initialize(variables["address"].as(), + port, + std::move(docRoot), + variables["cert-path"].as(), + !secure); newServer->Start(); } @@ -349,6 +402,9 @@ int main(int argc, const char *argv[]) { print_usage(argv[0], desc); cerr << ex.what() << std::endl; return -1; + } catch (const std::runtime_error &ex) { + cerr << ex.what() << std::endl; + return -1; } return 0; } From 0d83e2a3f52cf5152e150018a72278b375b98d8c Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Fri, 18 Oct 2019 09:29:55 +0200 Subject: [PATCH 20/32] Code style streamline of WebSockHttpFlexServer implementation Signed-off-by: Miladinovic Bojan --- .../src/WebSockHttpFlexServer.cpp | 472 ++++++------------ 1 file changed, 159 insertions(+), 313 deletions(-) diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index 4727c16..f328274 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -60,8 +60,8 @@ namespace { * @brief Helper class to handle connection between \ref WsChannel and actual sessions */ class ConnectionHandler { - // Allow access to connection information from this file implementation details - friend class ::WebSockHttpFlexServer; + // Allow access to connection information from this file implementation details + friend class ::WebSockHttpFlexServer; private: std::mutex mPlainWebSock_; @@ -78,9 +78,9 @@ namespace { std::unordered_map> connSslHttp_; - public: ConnectionHandler() = default; + ~ConnectionHandler() = default; /** * @brief Add new client for plain Web-Socket session @@ -185,7 +185,6 @@ namespace { /**** Local variables ****/ - // Boost.Beast helper state variables ConnectionHandler connHandler; std::shared_ptr connListener; @@ -215,26 +214,20 @@ namespace { /// Report a failure and remove client from active connections that are tracked template - void - fail(const T* fromType, boost::system::error_code ec, char const* what) - { + void fail(const T* fromType, boost::system::error_code ec, char const* what) { fail(ec, what); logger->Log(LogLevel::ERROR, "Connection error detected, remove client from active connections"); connHandler.RemoveClient(fromType); } //------------------------------------------------------------------------------ - // Echoes back all received WebSocket messages. // This uses the Curiously Recurring Template Pattern so that // the same code works with both SSL streams and regular sockets. template - class WebSocketSession - { + class WebSocketSession { // Access the derived class, this is part of // the Curiously Recurring Template Pattern idiom. - Derived& - derived() - { + Derived& derived() { return static_cast(*this); } @@ -250,21 +243,17 @@ namespace { WsChannel channel; public: // Construct the session - explicit - WebSocketSession(boost::asio::io_context& ioc, + explicit WebSocketSession(boost::asio::io_context& ioc, RequestHandler requestHandler) - : strand_(ioc.get_executor()) - , timer_(ioc, - (std::chrono::steady_clock::time_point::max)()) - , requestHandler_(requestHandler) - { + : strand_(ioc.get_executor()) + , timer_(ioc, + (std::chrono::steady_clock::time_point::max)()) + , requestHandler_(requestHandler) { } // Start the asynchronous operation template - void - doAccept(http::request> req) - { + void doAccept(http::request> req) { // Set the control callback. This will be called // on every incoming ping, pong, and close frame. derived().ws().control_callback( @@ -288,9 +277,7 @@ namespace { std::placeholders::_1))); } - void - onAccept(boost::system::error_code ec) - { + void onAccept(boost::system::error_code ec) { // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) return; @@ -305,21 +292,17 @@ namespace { } // Called when the timer expires. - void - onTimer(boost::system::error_code ec) - { + void onTimer(boost::system::error_code ec) { if(ec && ec != boost::asio::error::operation_aborted) { fail<>(&derived(), ec, "timer"); return; } // See if the timer really expired since the deadline may have moved. - if(timer_.expiry() <= std::chrono::steady_clock::now()) - { + if(timer_.expiry() <= std::chrono::steady_clock::now()) { // If this is the first time the timer expired, // send a ping to see if the other end is there. - if(derived().ws().is_open() && ping_state_ == 0) - { + if(derived().ws().is_open() && ping_state_ == 0) { // Note that we are sending a ping ping_state_ = 1; @@ -335,8 +318,7 @@ namespace { derived().shared_from_this(), std::placeholders::_1))); } - else - { + else { // The timer expired while trying to handshake, // or we sent a ping and it never completed or // we never got back a control frame, so close. @@ -357,9 +339,7 @@ namespace { } // Called to indicate activity from the remote peer - void - activity() - { + void activity() { // Note that the connection is alive ping_state_ = 0; @@ -368,9 +348,7 @@ namespace { } // Called after a ping is sent. - void - onPing(boost::system::error_code ec) - { + void onPing(boost::system::error_code ec) { // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) return; @@ -381,12 +359,10 @@ namespace { } // Note that the ping was sent. - if(ping_state_ == 1) - { + if(ping_state_ == 1) { ping_state_ = 2; } - else - { + else { // ping_state_ could have been set to 0 // if an incoming control frame was received // at exactly the same time we sent a ping. @@ -394,20 +370,16 @@ namespace { } } - void - on_control_callback( + void on_control_callback( websocket::frame_type kind, - boost::beast::string_view payload) - { + boost::beast::string_view payload) { boost::ignore_unused(kind, payload); // Note that there is activity activity(); } - void - doRead() - { + void doRead() { // Read a message into our buffer derived().ws().async_read( bufferRead_, @@ -420,11 +392,7 @@ namespace { std::placeholders::_2))); } - void - onRead( - boost::system::error_code ec, - std::size_t bytesTransferred) - { + void onRead(boost::system::error_code ec, std::size_t bytesTransferred) { boost::ignore_unused(bytesTransferred); // Happens when the timer closes the socket @@ -453,8 +421,7 @@ namespace { doRead(); } - void - write(const std::string &message) { + void write(const std::string &message) { // TODO: add queuing as async_write should be called one at a time boost::asio::buffer_copy(bufferWrite_.prepare(message.size()), boost::asio::buffer(message)); bufferWrite_.commit(message.size()); // commit copied data for write @@ -471,11 +438,7 @@ namespace { std::placeholders::_2))); } - void - onWrite( - boost::system::error_code ec, - std::size_t bytesTransferred) - { + void onWrite(boost::system::error_code ec, std::size_t bytesTransferred) { boost::ignore_unused(bytesTransferred); // Happens when the timer closes the socket @@ -489,42 +452,30 @@ namespace { // Clear the buffer bufferWrite_.consume(bytesTransferred); - - // Do another read - //doRead(); } }; // Handles a plain WebSocket connection - class PlainWebsocketSession - : public WebSocketSession - , public std::enable_shared_from_this - { + class PlainWebsocketSession : public WebSocketSession, + public std::enable_shared_from_this { websocket::stream ws_; bool close_ = false; public: // Create the session - explicit - PlainWebsocketSession(tcp::socket socket, RequestHandler requestHandler) - : WebSocketSession( - socket.get_executor().context(), requestHandler) - , ws_(std::move(socket)) - { - } + explicit PlainWebsocketSession(tcp::socket socket, RequestHandler requestHandler) + : WebSocketSession(socket.get_executor().context(), requestHandler), + ws_(std::move(socket)) { + } // Called by the base class - websocket::stream& - ws() - { + websocket::stream& ws() { return ws_; } // Start the asynchronous operation template - void - run(http::request> req) - { + void run(http::request> req) { channel = connHandler.AddClient(this); // Run the timer. The timer is operated @@ -535,9 +486,7 @@ namespace { doAccept(std::move(req)); } - void - doTimeout() - { + void doTimeout() { // This is so the close can have a timeout if(close_) return; @@ -557,9 +506,7 @@ namespace { std::placeholders::_1))); } - void - on_close(boost::system::error_code ec) - { + void on_close(boost::system::error_code ec) { // Happens when close times out if(ec == boost::asio::error::operation_aborted) return; @@ -574,10 +521,8 @@ namespace { }; // Handles an SSL WebSocket connection - class SslWebsocketSession - : public WebSocketSession - , public std::enable_shared_from_this - { + class SslWebsocketSession : public WebSocketSession, + public std::enable_shared_from_this { websocket::stream> ws_; boost::asio::strand< boost::asio::io_context::executor_type> strand_; @@ -585,14 +530,12 @@ namespace { public: // Create the http_session - explicit - SslWebsocketSession(ssl_stream stream, RequestHandler requestHandler) - : WebSocketSession( + explicit SslWebsocketSession(ssl_stream stream, RequestHandler requestHandler) + : WebSocketSession( stream.get_executor().context(), requestHandler) , ws_(std::move(stream)) - , strand_(ws_.get_executor()) - { - } + , strand_(ws_.get_executor()) { + } // Called by the base class websocket::stream>& @@ -603,9 +546,7 @@ namespace { // Start the asynchronous operation template - void - run(http::request> req) - { + void run(http::request> req) { channel = connHandler.AddClient(this); // Run the timer. The timer is operated @@ -616,9 +557,7 @@ namespace { doAccept(std::move(req)); } - void - doEof() - { + void doEof() { eof_ = true; // Set the timer @@ -634,9 +573,7 @@ namespace { std::placeholders::_1))); } - void - onShutdown(boost::system::error_code ec) - { + void onShutdown(boost::system::error_code ec) { // Happens when the shutdown times out if(ec == boost::asio::error::operation_aborted) return; @@ -650,9 +587,7 @@ namespace { // At this point the connection is closed gracefully } - void - doTimeout() - { + void doTimeout() { // If this is true it means we timed out performing the shutdown if(eof_) return; @@ -666,25 +601,19 @@ namespace { }; template - void - makeWebsocketSession( - tcp::socket socket, - http::request> req, - RequestHandler requestHandler) - { - std::make_shared( - std::move(socket), requestHandler)->run(std::move(req)); + void makeWebsocketSession(tcp::socket socket, + http::request> req, + RequestHandler requestHandler) { + std::make_shared( + std::move(socket), requestHandler)->run(std::move(req)); } template - void - makeWebsocketSession( - ssl_stream stream, - http::request> req, - RequestHandler requestHandler) - { - std::make_shared( - std::move(stream), requestHandler)->run(std::move(req)); + void makeWebsocketSession(ssl_stream stream, + http::request> req, + RequestHandler requestHandler) { + std::make_shared( + std::move(stream), requestHandler)->run(std::move(req)); } //------------------------------------------------------------------------------ @@ -693,28 +622,22 @@ namespace { // This uses the Curiously Recurring Template Pattern so that // the same code works with both SSL streams and regular sockets. template - class HttpSession - { + class HttpSession { // Access the derived class, this is part of // the Curiously Recurring Template Pattern idiom. - Derived& - derived() - { + Derived& derived() { return static_cast(*this); } // This queue is used for HTTP pipelining. - class queue - { - enum - { + class queue { + enum { // Maximum number of responses we will queue limit = 8 }; // The type-erased, saved work item - struct work - { + struct work { virtual ~work() = default; virtual void operator()() = 0; }; @@ -723,26 +646,19 @@ namespace { std::vector> items_; public: - explicit - queue(HttpSession& self) - : self_(self) - { + explicit queue(HttpSession& self) : self_(self) { static_assert(limit > 0, "queue limit must be positive"); items_.reserve(limit); } // Returns `true` if we have reached the queue limit - bool - is_full() const - { + bool is_full() const { return items_.size() >= limit; } // Called when a message finishes sending // Returns `true` if the caller should initiate a read - bool - onWrite() - { + bool onWrite() { BOOST_ASSERT(! items_.empty()); auto const was_full = is_full(); items_.erase(items_.begin()); @@ -753,12 +669,9 @@ namespace { // Called by the HTTP handler to send a response. template - void - operator()(http::message&& msg) - { + void operator()(http::message&& msg) { // This holds a work item - struct work_impl : work - { + struct work_impl : work { HttpSession& self_; http::message msg_; @@ -766,13 +679,11 @@ namespace { HttpSession& self, http::message&& msg) : self_(self) - , msg_(std::move(msg)) - { + , msg_(std::move(msg)) { } void - operator()() - { + operator()() { http::async_write( self_.derived().stream(), msg_, @@ -810,24 +721,20 @@ namespace { public: // Construct the session - HttpSession( - boost::asio::io_context& ioc, - boost::beast::flat_buffer buffer, - std::string const& doc_root, - RequestHandler requestHandler) - : doc_root_(doc_root) - , queue_(*this) - , timer_(ioc, - (std::chrono::steady_clock::time_point::max)()) - , strand_(ioc.get_executor()) - , bufferRead_(std::move(buffer)) - , requestHandler_(requestHandler) - { - } + HttpSession(boost::asio::io_context& ioc, + boost::beast::flat_buffer buffer, + std::string const& doc_root, + RequestHandler requestHandler) + : doc_root_(doc_root) + , queue_(*this) + , timer_(ioc, + (std::chrono::steady_clock::time_point::max)()) + , strand_(ioc.get_executor()) + , bufferRead_(std::move(buffer)) + , requestHandler_(requestHandler) { + } - void - doRead() - { + void doRead() { // Set the timer timer_.expires_after(std::chrono::seconds(HTTP_TIMEOUT_VALUE)); @@ -849,9 +756,7 @@ namespace { } // Called when the timer expires. - void - onTimer(boost::system::error_code ec) - { + void onTimer(boost::system::error_code ec) { if(ec && ec != boost::asio::error::operation_aborted) { fail<>(&derived(), ec, "timer"); return; @@ -871,9 +776,7 @@ namespace { std::placeholders::_1))); } - void - onRead(boost::system::error_code ec) - { + void onRead(boost::system::error_code ec) { // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) return; @@ -886,8 +789,7 @@ namespace { fail(ec, "read"); // See if it is a WebSocket Upgrade - if(websocket::is_upgrade(req_)) - { + if(websocket::is_upgrade(req_)) { // Transfer the stream to a new WebSocket session return makeWebsocketSession( derived().release_stream(), @@ -900,8 +802,7 @@ namespace { // if we got correct JSON request back, send it to handler if (restHandler->GetJson(std::string(req_.method_string()), std::string(req_.target()), - jsonRequest)) - { + jsonRequest)) { auto response = requestHandler_(jsonRequest, channel); // Respond to GET request http::response res{ @@ -914,8 +815,7 @@ namespace { res.keep_alive(req_.keep_alive()); queue_(std::move(res)); } - else - { + else { // write jsonRequest back as error response auto const bad_request = [=](std::string why) @@ -936,9 +836,7 @@ namespace { doRead(); } - void - onWrite(boost::system::error_code ec, bool close) - { + void onWrite(boost::system::error_code ec, bool close) { // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) return;// provide error JSON @@ -948,16 +846,14 @@ namespace { return; } - if(close) - { + if(close) { // This means we should close the connection, usually because // the response indicated the "Connection: close" semantic. return derived().doEof(); } // Inform the queue that a write completed - if(queue_.onWrite()) - { + if(queue_.onWrite()) { // Read another request doRead(); } @@ -965,49 +861,39 @@ namespace { }; // Handles a plain HTTP connection - class PlainHttpSession - : public HttpSession - , public std::enable_shared_from_this - { + class PlainHttpSession : public HttpSession, + public std::enable_shared_from_this { tcp::socket socket_; boost::asio::strand< boost::asio::io_context::executor_type> strand_; public: // Create the http_session - PlainHttpSession( - tcp::socket socket, - boost::beast::flat_buffer buffer, - std::string const& doc_root, - RequestHandler requestHandler) - : HttpSession( - socket.get_executor().context(), - std::move(buffer), - doc_root, - requestHandler) - , socket_(std::move(socket)) - , strand_(socket_.get_executor()) - { - } + PlainHttpSession(tcp::socket socket, + boost::beast::flat_buffer buffer, + std::string const& doc_root, + RequestHandler requestHandler) + : HttpSession( + socket.get_executor().context(), + std::move(buffer), + doc_root, + requestHandler) + , socket_(std::move(socket)) + , strand_(socket_.get_executor()) { + } // Called by the base class - tcp::socket& - stream() - { + tcp::socket& stream() { return socket_; } // Called by the base class - tcp::socket - release_stream() - { + tcp::socket release_stream() { return std::move(socket_); } // Start the asynchronous operation - void - run() - { + void run() { channel = connHandler.AddClient(this); // Run the timer. The timer is operated @@ -1017,9 +903,7 @@ namespace { doRead(); } - void - doEof() - { + void doEof() { // Send a TCP shutdown boost::system::error_code ec; socket_.shutdown(tcp::socket::shutdown_send, ec); @@ -1027,9 +911,7 @@ namespace { // At this point the connection is closed gracefully } - void - doTimeout() - { + void doTimeout() { // Closing the socket cancels all outstanding operations. They // will complete with boost::asio::error::operation_aborted boost::system::error_code ec; @@ -1039,10 +921,8 @@ namespace { }; // Handles an SSL HTTP connection - class SslHttpSession - : public HttpSession - , public std::enable_shared_from_this - { + class SslHttpSession : public HttpSession, + public std::enable_shared_from_this { ssl_stream stream_; boost::asio::strand< boost::asio::io_context::executor_type> strand_; @@ -1050,40 +930,32 @@ namespace { public: // Create the http_session - SslHttpSession( - tcp::socket socket, - ssl::context& ctx, - boost::beast::flat_buffer buffer, - std::string const& doc_root, - RequestHandler requestHandler) - : HttpSession( - socket.get_executor().context(), - std::move(buffer), - doc_root, - requestHandler) - , stream_(std::move(socket), ctx) - , strand_(stream_.get_executor()) - { - } + SslHttpSession(tcp::socket socket, + ssl::context& ctx, + boost::beast::flat_buffer buffer, + std::string const& doc_root, + RequestHandler requestHandler) + : HttpSession( + socket.get_executor().context(), + std::move(buffer), + doc_root, + requestHandler) + , stream_(std::move(socket), ctx) + , strand_(stream_.get_executor()) { + } // Called by the base class - ssl_stream& - stream() - { + ssl_stream& stream() { return stream_; } // Called by the base class - ssl_stream - release_stream() - { + ssl_stream release_stream() { return std::move(stream_); } // Start the asynchronous operation - void - run() - { + void run() { channel = connHandler.AddClient(this); // Run the timer. The timer is operated @@ -1109,11 +981,8 @@ namespace { std::placeholders::_1, std::placeholders::_2))); } - void - onHandshake( - boost::system::error_code ec, - std::size_t bytes_used) - { + void onHandshake(boost::system::error_code ec, + std::size_t bytes_used) { // Happens when the handshake times out if(ec == boost::asio::error::operation_aborted) return; @@ -1127,9 +996,7 @@ namespace { doRead(); } - void - doEof() - { + void doEof() { eof_ = true; // Set the timer @@ -1145,9 +1012,7 @@ namespace { std::placeholders::_1))); } - void - onShutdown(boost::system::error_code ec) - { + void onShutdown(boost::system::error_code ec) { // Happens when the shutdown times out if(ec == boost::asio::error::operation_aborted) return; @@ -1159,9 +1024,7 @@ namespace { // At this point the connection is closed gracefully } - void - doTimeout() - { + void doTimeout() { // If this is true it means we timed out performing the shutdown if(eof_) return; @@ -1176,8 +1039,7 @@ namespace { //------------------------------------------------------------------------------ // Detects SSL handshakes - class DetectSession : public std::enable_shared_from_this - { + class DetectSession : public std::enable_shared_from_this { tcp::socket socket_; ssl::context& ctx_; boost::asio::strand< @@ -1187,24 +1049,19 @@ namespace { RequestHandler requestHandler_; public: - explicit - DetectSession( - tcp::socket socket, - ssl::context& ctx, - std::string const& doc_root, - RequestHandler requestHandler) - : socket_(std::move(socket)) - , ctx_(ctx) - , strand_(socket_.get_executor()) - , doc_root_(doc_root) - , requestHandler_(requestHandler) - { + explicit DetectSession(tcp::socket socket, + ssl::context& ctx, + std::string const& doc_root, + RequestHandler requestHandler) + : socket_(std::move(socket)) + , ctx_(ctx) + , strand_(socket_.get_executor()) + , doc_root_(doc_root) + , requestHandler_(requestHandler) { } // Launch the detector - void - run() - { + void run() { async_detect_ssl( socket_, buffer_, @@ -1217,9 +1074,7 @@ namespace { std::placeholders::_2))); } - void - onDetect(boost::system::error_code ec, boost::tribool result) - { + void onDetect(boost::system::error_code ec, boost::tribool result) { if(ec) return fail(ec, "detect"); @@ -1252,8 +1107,7 @@ namespace { }; // Accepts incoming connections and launches the sessions - class BeastListener : public std::enable_shared_from_this - { + class BeastListener : public std::enable_shared_from_this { ssl::context& ctx_; tcp::acceptor acceptor_; tcp::socket socket_; @@ -1261,18 +1115,16 @@ namespace { RequestHandler requestHandler_; public: - BeastListener( - boost::asio::io_context& ioc, - ssl::context& ctx, - tcp::endpoint endpoint, - std::string const& doc_root, - RequestHandler requestHandler) - : ctx_(ctx) - , acceptor_(ioc) - , socket_(ioc) - , doc_root_(doc_root) - , requestHandler_(requestHandler) - { + BeastListener(boost::asio::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + std::string const& doc_root, + RequestHandler requestHandler) + : ctx_(ctx) + , acceptor_(ioc) + , socket_(ioc) + , doc_root_(doc_root) + , requestHandler_(requestHandler) { boost::system::error_code ec; // Open the acceptor @@ -1307,20 +1159,16 @@ namespace { fail(ec, "listen"); return; } - } + } // Start accepting incoming connections - void - run() - { + void run() { if(! acceptor_.is_open()) return; doAccept(); } - void - doAccept() - { + void doAccept() { acceptor_.async_accept( socket_, std::bind( @@ -1329,9 +1177,7 @@ namespace { std::placeholders::_1)); } - void - onAccept(boost::system::error_code ec) - { + void onAccept(boost::system::error_code ec) { if(ec) { fail(ec, "accept"); From 3a0c2caab8093f8a71199b78ff07a77c64a6698d Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Fri, 18 Oct 2019 14:05:40 +0200 Subject: [PATCH 21/32] Added test client HTTP page for easier testing of VIS server Updated server to respond with proper MIME type for JSON Added header to allow for localhost testing Added support for generating JWT authorization token Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/src/RestV1ApiHandler.cpp | 1 + .../src/WebSockHttpFlexServer.cpp | 7 +- w3c-visserver-api/test/web-client/index.html | 97 +++++ .../test/web-client/jsonview.css | 77 ++++ w3c-visserver-api/test/web-client/jsonview.js | 331 ++++++++++++++++++ .../test/web-client/rest-client.js | 91 +++++ w3c-visserver-api/test/web-client/style.css | 136 +++++++ 7 files changed, 738 insertions(+), 2 deletions(-) create mode 100644 w3c-visserver-api/test/web-client/index.html create mode 100644 w3c-visserver-api/test/web-client/jsonview.css create mode 100644 w3c-visserver-api/test/web-client/jsonview.js create mode 100644 w3c-visserver-api/test/web-client/rest-client.js create mode 100644 w3c-visserver-api/test/web-client/style.css diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp index 7585fce..d0d0ae6 100644 --- a/w3c-visserver-api/src/RestV1ApiHandler.cpp +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -100,6 +100,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, if (restTarget.size() && verifyPathAndStrip(restTarget, delimiter)) { while (restTarget.size()) { + // we only accept clean printable characters const std::regex regexValidWord("^([A-Za-z]+)"); if (std::regex_search(restTarget, sm, regexValidWord)) { diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index f328274..688d45a 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -810,7 +810,10 @@ namespace { std::make_tuple(response), std::make_tuple(http::status::ok, req_.version())}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/json"); + res.set(http::field::content_type, "application/json"); + // allow cross-domain calls with setting below header + // TODO: evaluate this header for production level SW + res.set(http::field::access_control_allow_origin, "*"); res.content_length(response.size()); res.keep_alive(req_.keep_alive()); queue_(std::move(res)); @@ -822,7 +825,7 @@ namespace { { http::response res{http::status::bad_request, req_.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/json"); + res.set(http::field::content_type, "application/json"); res.keep_alive(req_.keep_alive()); res.body() = why; res.prepare_payload(); diff --git a/w3c-visserver-api/test/web-client/index.html b/w3c-visserver-api/test/web-client/index.html new file mode 100644 index 0000000..a873b36 --- /dev/null +++ b/w3c-visserver-api/test/web-client/index.html @@ -0,0 +1,97 @@ + + + + + + + + Tester for REST API support for W3C-VISServer-API + + + + + + + + + + + + + +
+
+
+ +
+

W3C Vehicle Information Specification test client for REST and Web-Socket interfaces

+
+
+ +
+
+ Current authorization token:
+ +
+
+ +
+
+
+ Input resource request to fetch for the W3C VIS Server below: +
+
+ + +
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+
+ Server parameters: + Address:
+ Port:
+ Doc root:
+
+
+
+
+
+
+
+ Client token and certificates configuration + Client token content: +
+ Client PEM certificate content: +
+ Client key certificate content: + +
+
+
+
+ +
+
+ + + + diff --git a/w3c-visserver-api/test/web-client/jsonview.css b/w3c-visserver-api/test/web-client/jsonview.css new file mode 100644 index 0000000..d9697cb --- /dev/null +++ b/w3c-visserver-api/test/web-client/jsonview.css @@ -0,0 +1,77 @@ +.line { + margin: 4px 0; + display: flex; + justify-content: flex-start; +} + +.caret-icon { + width: 18px; + text-align: center; + cursor: pointer; +} + +.empty-icon { + width: 18px; +} + +.json-type { + margin-right: 4px; + margin-left: 4px; +} + +.json-key { + color: #444; + margin-right: 4px; + margin-left: 4px; +} + +.json-index { + margin-right: 4px; + margin-left: 4px; +} + +.json-separator { + +} + +.json-value { + margin-left: 8px; +} + +.json-number { + color: #f9ae58; +} + +.json-boolean { + color: #ec5f66; +} + +.json-string { + color: #86b25c; +} + +.json-size { + margin-right: 4px; + margin-left: 4px; +} + +.hide { + display: none; +} + +.fas { + display: inline-block; + width: 0; + height: 0; + border-style: solid; +} + +.fa-caret-down { + border-width: 6px 5px 0 5px; + border-color: #808080 transparent +} + +.fa-caret-right { + border-width: 5px 0 5px 6px; + border-color: transparent transparent transparent #808080; +} diff --git a/w3c-visserver-api/test/web-client/jsonview.js b/w3c-visserver-api/test/web-client/jsonview.js new file mode 100644 index 0000000..885f6a4 --- /dev/null +++ b/w3c-visserver-api/test/web-client/jsonview.js @@ -0,0 +1,331 @@ +(function() { +'use strict'; +/** + * Create html element + * @param {String} type html element + * @param {Object} config + */ +function createElement(type, config) { + const htmlElement = document.createElement(type); + + if (config === undefined) { + return htmlElement; + } + + if (config.className) { + htmlElement.className = config.className; + } + + if (config.content) { + htmlElement.textContent = config.content; + } + + if (config.children) { + config.children.forEach((el) => { + if (el !== null) { + htmlElement.appendChild(el); + } + }); + } + + return htmlElement; +} + + +/** + * @param {Object} node + * @return {HTMLElement} + */ +function createExpandedElement(node) { + const iElem = createElement('i'); + + if (node.expanded) { + iElem.className = 'fas fa-caret-down'; + } else { + iElem.className = 'fas fa-caret-right'; + } + + const caretElem = createElement('div', { + className: 'caret-icon', + children: [iElem], + }); + + const handleClick = node.toggle.bind(node); + caretElem.addEventListener('click', handleClick); + + const indexElem = createElement('div', { + className: 'json-index', + content: node.key, + }); + + const typeElem = createElement('div', { + className: 'json-type', + content: node.type, + }); + + const keyElem = createElement('div', { + className: 'json-key', + content: node.key, + }); + + const sizeElem = createElement('div', { + className: 'json-size' + }); + + if (node.type === 'array') { + sizeElem.innerText = '[' + node.children.length + ']'; + } else if (node.type === 'object') { + sizeElem.innerText = '{' + node.children.length + '}'; + } + + let lineChildren; + if (node.key === null) { + lineChildren = [caretElem, typeElem, sizeElem] + } else if (node.parent.type === 'array') { + lineChildren = [caretElem, indexElem, sizeElem] + } else { + lineChildren = [caretElem, keyElem, sizeElem] + } + + const lineElem = createElement('div', { + className: 'line', + children: lineChildren + }); + + if (node.depth > 0) { + lineElem.style = 'margin-left: ' + node.depth * 24 + 'px;'; + } + + return lineElem; +} + + +/** + * @param {Object} node + * @return {HTMLElement} + */ +function createNotExpandedElement(node) { + const caretElem = createElement('div', { + className: 'empty-icon', + }); + + const keyElem = createElement('div', { + className: 'json-key', + content: node.key + }); + + const separatorElement = createElement('div', { + className: 'json-separator', + content: ':' + }); + + const valueType = ' json-' + typeof node.value; + const valueContent = String(node.value); + const valueElement = createElement('div', { + className: 'json-value' + valueType, + content: valueContent + }); + + const lineElem = createElement('div', { + className: 'line', + children: [caretElem, keyElem, separatorElement, valueElement] + }); + + if (node.depth > 0) { + lineElem.style = 'margin-left: ' + node.depth * 24 + 'px;'; + } + + return lineElem; +} + + +/** + * create tree node + * @return {Object} + */ +function createNode() { + return { + key: null, + parent: null, + value: null, + expanded: false, + type: null, + children: null, + elem: null, + depth: 0, + + setCaretIconRight() { + const icon = this.elem.querySelector('.fas'); + icon.classList.replace('fa-caret-down', 'fa-caret-right'); + }, + + setCaretIconDown() { + const icon = this.elem.querySelector('.fas'); + icon.classList.replace('fa-caret-right', 'fa-caret-down'); + }, + + hideChildren() { + if (this.children !== null) { + this.children.forEach((item) => { + item.elem.classList.add('hide'); + if (item.expanded) { + item.hideChildren(); + } + }); + } + }, + + showChildren() { + if (this.children !== null) { + this.children.forEach((item) => { + item.elem.classList.remove('hide'); + if (item.expanded) { + item.showChildren(); + } + }); + } + }, + + toggle: function() { + if (this.expanded) { + this.expanded = false; + this.hideChildren(); + this.setCaretIconRight(); + } else { + this.expanded = true; + this.showChildren(); + this.setCaretIconDown(); + } + } + } +} + + +/** + * Return object length + * @param {Object} obj + * @return {number} + */ +function getLength(obj) { + let length = 0; + for (let key in obj) { + length += 1; + }; + return length; +} + + +/** + * Return variable type + * @param {*} val + */ +function getType(val) { + let type = typeof val; + if (Array.isArray(val)) { + type = 'array'; + } else if (val === null) { + type = 'null'; + } + return type; +} + + +/** + * Recursively traverse json object + * @param {Object} obj parsed json object + * @param {Object} parent of object tree + */ +function traverseObject(obj, parent) { + for (let key in obj) { + const child = createNode(); + child.parent = parent; + child.key = key; + child.type = getType(obj[key]); + child.depth = parent.depth + 1; + child.expanded = false; + + if (typeof obj[key] === 'object') { + child.children = []; + parent.children.push(child); + traverseObject(obj[key], child); + child.elem = createExpandedElement(child); + } else { + child.value = obj[key]; + child.elem = createNotExpandedElement(child); + parent.children.push(child); + } + } +} + + +/** + * Create root of a tree + * @param {Object} obj Json object + * @return {Object} + */ +function createTree(obj) { + const tree = createNode(); + tree.type = getType(obj); + tree.children = []; + tree.expanded = true; + + traverseObject(obj, tree); + tree.elem = createExpandedElement(tree); + + return tree; +} + + +/** + * Recursively traverse Tree object + * @param {Object} node + * @param {Callback} callback + */ +function traverseTree(node, callback) { + callback(node); + if (node.children !== null) { + node.children.forEach((item) => { + traverseTree(item, callback); + }); + } +} + + +/** + * Render Tree object + * @param {Object} tree + * @param {String} targetElem + */ +function render(tree, targetElem) { + let rootElem; + if (targetElem) { + rootElem = document.querySelector(targetElem); + } else { + rootElem = document.body; + } + + traverseTree(tree, (node) => { + if (!node.expanded) { + node.hideChildren(); + } + rootElem.appendChild(node.elem); + }); +} + + +/* Export jsonView object */ +window.jsonView = { + /** + * Render JSON into DOM container + * @param {String} jsonData + * @param {String} targetElem + */ + format: function(jsonData, targetElem) { + let parsedData = jsonData; + if (typeof jsonData === 'string' || jsonData instanceof String) parsedData = JSON.parse(jsonData); + const tree = createTree(parsedData); + render(tree, targetElem); + } +} + +})(); diff --git a/w3c-visserver-api/test/web-client/rest-client.js b/w3c-visserver-api/test/web-client/rest-client.js new file mode 100644 index 0000000..9eca1dd --- /dev/null +++ b/w3c-visserver-api/test/web-client/rest-client.js @@ -0,0 +1,91 @@ +var requestObj = null; +var requestUrl = ''; + +// JWT header +var jwtHeader = { + "alg": "RS256", + "typ": "JWT" +}; + +//////////////////////// +// Startup configuration + +// setup default values for server +document.getElementById('server-address').value = "localhost"; +document.getElementById('server-port').value = 8090; +document.getElementById('server-doc-root').value = "vss/api/v1"; + +// setup event handlers +document.getElementById('make-get-request').onclick = function req() {return makeRestRequest('GET');}; +document.getElementById('make-set-request').onclick = function req() {return makeRestRequest('SET');}; + + +document.getElementById('client-token').addEventListener('paste', onTokenUpdate); +//document.getElementById('client-token').addEventListener('keyup', onTokenUpdate); +document.getElementById('client-token').addEventListener('change', onTokenUpdate); + +document.getElementById('client-key-cert').addEventListener('paste', onTokenUpdate); +//document.getElementById('client-key-cert').addEventListener('keyup', onTokenUpdate); +document.getElementById('client-key-cert').addEventListener('change', onTokenUpdate); + +/////////////////// +// Implementation + +function onTokenUpdate() { + getEncodedToken(); + return false; +} + +function getBase64url(source) { + // make source base64 encoded + encodedSource = CryptoJS.enc.Base64.stringify(source); + // convert base64 base64url specifications + encodedSource = encodedSource.replace(/=+$/, ''); + encodedSource = encodedSource.replace(/\+/g, '-'); + encodedSource = encodedSource.replace(/\//g, '_'); + + return encodedSource; +} + +function getEncodedToken() { + var stringifiedHeader = CryptoJS.enc.Utf8.parse(JSON.stringify(jwtHeader)); + var encodedHeader = getBase64url(stringifiedHeader); + + var data = document.getElementById("client-token").value; + var encodedData = getBase64url(CryptoJS.enc.Utf8.parse(data)); + + var signatureInput = encodedHeader + "." + encodedData; + var secret = document.getElementById("client-key-cert").value; + var signature = CryptoJS.SHA256(signatureInput, secret); + signature = getBase64url(signature); + + // update on-screen token for user + document.getElementById("auth-token-string").value = signatureInput + "." + signature; + + return false; +} + +function makeRestRequest(type) { + let server_address = document.getElementById('server-address').value; + let server_port = document.getElementById('server-port').value; + let doc_root = document.getElementById('server-doc-root').value; + let resource = document.getElementById('resource-path').value; + + if (server_address != "" && server_port != "") { + requestObj = new XMLHttpRequest(); + requestUrl = 'http://' + server_address + ':' + server_port + '/' + doc_root + '/' + resource; + requestObj.open(type, requestUrl, true); + + // callback handler when positive response received + requestObj.onload = function () { + if (this.readyState == 4 && this.status == 200) { + document.getElementById('json-raw-output').innerHTML = this.responseText; + let target = '#json-raw-output'; + jsonView.format(this.responseText, target); + } + }; + requestObj.send(null); + } + // prevent reloadig of page + return false; +} diff --git a/w3c-visserver-api/test/web-client/style.css b/w3c-visserver-api/test/web-client/style.css new file mode 100644 index 0000000..a98e653 --- /dev/null +++ b/w3c-visserver-api/test/web-client/style.css @@ -0,0 +1,136 @@ +html { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-family: 'Dosis', sans-serif; + font-size: 13px; + line-height: 1.6; + color: #444; + background-color: white; + box-sizing: border-box; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +#root { + width: auto; +} + +#container { + /*background-color: #f6feff; */ + border-color: #1e9b8a; +} + +#main-content-tab { + margin-right: -300px; + width: 100%; + float: left; +} + +#config-content-tab { + width: 300px; + float: left; +} + +.clear { + clear: both; +} + +.row { + margin-right: 320px; +} + +.row::after { + content: ""; + clear: both; + display: table; +} + +[class*="col-"] { + float: left; + padding: 15px; +} + +.col-logo { +} + +.col-logo img { + width: 100px; + height: 100px; +} + +.col-title { + box-sizing: border-box; +} + +#auth-token { + margin-left: 30px; +} +#auth-token-string { + background-color: #F0F8FF; + width: 100%; + border: 1px solid; +} + +.col-app { + width: 100%; +} + +.col-form { + width: 100%; +} + +fieldset { + border: 1px solid; + border-color: #1e9b8a; + border-radius: 6px; + width: auto; +} +input { + font-family: 'Dosis', sans-serif; + font-size: 13px; + line-height: 1.6; + color: #444; + border: 1px solid; + border-color: #1e9b8a; + border-radius: 6px; + margin: 4px; +} +input:[type=text]focus { + color: #fffbf0; +} +input:[type=number]focus { + color: #fffbf0; +} + +textarea { + font-family: monospace; + font-size: 7px; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + border: 1px solid; + border-color: #1e9b8a; +} + +#auth-token { + width: 100%; +} +#auth-token textarea{ + width: 96%; + resize: none; + /*user-select: none;*/ + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + border: 1px solid; + border-color: #1e9b8a; +} + +#resource-path { + width: 96%; +} + +#make-get-request #make-set-request { + width: 100px; +} From eac499c69536f6da4fd7a57cf3cef707f897b78d Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Mon, 21 Oct 2019 12:45:08 +0200 Subject: [PATCH 22/32] Handle gracefully when JWT token decode fails Signed-off-by: Miladinovic Bojan From f4b0531bdf24592d4400b8d1797a82d3affec3ce Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Tue, 22 Oct 2019 11:23:26 +0200 Subject: [PATCH 23/32] Handled GET requests of signals and metadata SET requests are disabled for now. Update of JsonResponses to return directly json object beside string Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/include/JsonResponses.hpp | 18 ++ .../include/RestV1ApiHandler.hpp | 19 +- w3c-visserver-api/src/JsonResponses.cpp | 101 ++++++---- w3c-visserver-api/src/RestV1ApiHandler.cpp | 189 ++++++++++++------ 4 files changed, 208 insertions(+), 119 deletions(-) diff --git a/w3c-visserver-api/include/JsonResponses.hpp b/w3c-visserver-api/include/JsonResponses.hpp index 38db0f7..da4edb3 100644 --- a/w3c-visserver-api/include/JsonResponses.hpp +++ b/w3c-visserver-api/include/JsonResponses.hpp @@ -22,21 +22,39 @@ namespace JsonResponses { std::string malFormedRequest(uint32_t request_id, const std::string action, std::string message); + void malFormedRequest(uint32_t request_id, + const std::string action, + std::string message, + jsoncons::json& jsonResponse); std::string malFormedRequest(std::string message); + void malFormedRequest(std::string message, + jsoncons::json& jsonResponse); /** A API call requested a non-existant path */ std::string pathNotFound(uint32_t request_id, const std::string action, const std::string path); + void pathNotFound(uint32_t request_id, + const std::string action, + const std::string path, + jsoncons::json& jsonResponse); std::string noAccess(uint32_t request_id, const std::string action, std::string message); + void noAccess(uint32_t request_id, + const std::string action, + std::string message, + jsoncons::json& jsonResponse); std::string valueOutOfBounds(uint32_t request_id, const std::string action, const std::string message); + void valueOutOfBounds(uint32_t request_id, + const std::string action, + const std::string message, + jsoncons::json& jsonResponse); } #endif diff --git a/w3c-visserver-api/include/RestV1ApiHandler.hpp b/w3c-visserver-api/include/RestV1ApiHandler.hpp index e6463c7..aabf71b 100644 --- a/w3c-visserver-api/include/RestV1ApiHandler.hpp +++ b/w3c-visserver-api/include/RestV1ApiHandler.hpp @@ -51,22 +51,9 @@ class RestV1ApiHandler : public IRestHandler { }; private: - /** - * @brief Handle HTTP GET request - * @param restTarget input HTTP target string - * @param jsonRequest output JSON string - * @return true if REST request valid and valid request JSON returned - * \c false otherwise and error response JSON returned - */ - bool handleGet(std::string && restTarget, std::string& jsonRequest); - /** - * @brief Handle HTTP SET request - * @param restTarget input HTTP target string - * @param jsonRequest output JSON string - * @return true if REST request valid and valid request JSON returned - * \c false otherwise and error response JSON returned - */ - bool handleSet(std::string && restTarget, std::string& jsonRequest); + bool GetSignalPath(uint32_t requestId, + jsoncons::json& json, + std::string&& restTarget); /** * @brief Verify that HTTP target begins with correct root path and remove it if found * @param restTarget HTTP target path diff --git a/w3c-visserver-api/src/JsonResponses.cpp b/w3c-visserver-api/src/JsonResponses.cpp index 0d70daa..3f790c5 100644 --- a/w3c-visserver-api/src/JsonResponses.cpp +++ b/w3c-visserver-api/src/JsonResponses.cpp @@ -14,33 +14,42 @@ #include "JsonResponses.hpp" namespace JsonResponses { - std::string malFormedRequest(uint32_t request_id, - const std::string action, - std::string message) { - jsoncons::json answer; - answer["action"] = action; - answer["requestId"] = request_id; + void malFormedRequest(uint32_t request_id, + const std::string action, + std::string message, + jsoncons::json& jsonResponse) { + jsonResponse["action"] = action; + jsonResponse["requestId"] = request_id; jsoncons::json error; error["number"] = 400; error["reason"] = "Bad Request"; error["message"] = message; - answer["error"] = error; - answer["timestamp"] = time(NULL); + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string malFormedRequest(uint32_t request_id, + const std::string action, + std::string message) { + jsoncons::json answer; + malFormedRequest(request_id, action, message, answer); std::stringstream ss; ss << pretty_print(answer); return ss.str(); } - std::string malFormedRequest(std::string message) { - jsoncons::json answer; + void malFormedRequest(std::string message, jsoncons::json& jsonResponse) { jsoncons::json error; error["number"] = 400; error["reason"] = "Bad Request"; error["message"] = message; - answer["error"] = error; - answer["timestamp"] = time(NULL); + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string malFormedRequest(std::string message) { + jsoncons::json answer; + malFormedRequest(message, answer); std::stringstream ss; ss << pretty_print(answer); @@ -48,54 +57,72 @@ namespace JsonResponses { } /** A API call requested a non-existant path */ - std::string pathNotFound(uint32_t request_id, - const std::string action, - const std::string path) { - jsoncons::json answer; - answer["action"] = action; - answer["requestId"] = request_id; + void pathNotFound(uint32_t request_id, + const std::string action, + const std::string path, + jsoncons::json& jsonResponse) { + jsonResponse["action"] = action; + jsonResponse["requestId"] = request_id; jsoncons::json error; error["number"] = 404; error["reason"] = "Path not found"; error["message"] = "I can not find " + path + " in my db"; - answer["error"] = error; - answer["timestamp"] = time(NULL); + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string pathNotFound(uint32_t request_id, + const std::string action, + const std::string path) { + jsoncons::json answer; + pathNotFound(request_id, action, path, answer); std::stringstream ss; ss << pretty_print(answer); return ss.str(); } - std::string noAccess(uint32_t request_id, - const std::string action, - std::string message) { - jsoncons::json result; + void noAccess(uint32_t request_id, + const std::string action, + std::string message, + jsoncons::json& jsonResponse) { jsoncons::json error; - result["action"] = action; - result["requestId"] = request_id; + jsonResponse["action"] = action; + jsonResponse["requestId"] = request_id; error["number"] = 403; error["reason"] = "Forbidden"; error["message"] = message; - result["error"] = error; - result["timestamp"] = time(NULL); + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string noAccess(uint32_t request_id, + const std::string action, + std::string message) { + jsoncons::json answer; + noAccess(request_id, action, message, answer); std::stringstream ss; - ss << pretty_print(result); + ss << pretty_print(answer); return ss.str(); } - std::string valueOutOfBounds(uint32_t request_id, - const std::string action, - const std::string message) { - jsoncons::json answer; - answer["action"] = action; - answer["requestId"] = request_id; + void valueOutOfBounds(uint32_t request_id, + const std::string action, + const std::string message, + jsoncons::json& jsonResponse) { + jsonResponse["action"] = action; + jsonResponse["requestId"] = request_id; jsoncons::json error; error["number"] = 400; error["reason"] = "Value passed is out of bounds"; error["message"] = message; - answer["error"] = error; - answer["timestamp"] = time(NULL); + jsonResponse["error"] = error; + jsonResponse["timestamp"] = time(NULL); + } + std::string valueOutOfBounds(uint32_t request_id, + const std::string action, + const std::string message) { + jsoncons::json answer; + valueOutOfBounds(request_id, action, message, answer); std::stringstream ss; ss << pretty_print(answer); diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp index d0d0ae6..a7ca4b7 100644 --- a/w3c-visserver-api/src/RestV1ApiHandler.cpp +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -59,10 +59,80 @@ bool RestV1ApiHandler::verifyPathAndStrip(std::string& restTarget, std::string& return false; } +bool RestV1ApiHandler::GetSignalPath(uint32_t requestId, + jsoncons::json& json, + std::string&& restTarget) { + std::string signalPath; + std::string foundStr; + std::string delimiter("/"); + std::smatch sm; + bool ret = true; + + if (restTarget.size() && verifyPathAndStrip(restTarget, delimiter)) { + while (restTarget.size()) { + // we only accept clean printable characters + const std::regex regexValidWord("^([A-Za-z]+)"); + + if (std::regex_search(restTarget, sm, regexValidWord)) { + foundStr = sm.str(1); + signalPath += foundStr; + if (verifyPathAndStrip(restTarget, foundStr)) { + if ((restTarget.size() == 0)) { + break; + } + else if (verifyPathAndStrip(restTarget, delimiter)) { + signalPath += '.'; + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path delimiter not valid", + json); + ret = false; + } + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path not valid", + json); + ret = false; + } + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path URI not valid", + json); + ret = false; + } + } + } + else { + // not supporting retrieving of all signals by default + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signals cannot be retrieved in bulk request for now, only through single signal requests", + json); + ret = false; + } + + // update signal path if all is OK + if (ret) { + json["path"] = signalPath; + } + + return ret; +} + // Basic implementation of REST API handling // For now governed by KISS principle, but in future, it should be re-factored // to handle independently different methods and resources for easier maintenance.. -// possibly by providing hooks for each resource and API version +// possibly by providing hooks for each resource and|or API version, etc... bool RestV1ApiHandler::GetJson(std::string&& restMethod, std::string&& restTarget, std::string& jsonRequest) { @@ -80,10 +150,9 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, std::regex_match (restMethod, sm, regSupportedHttpActions); // if supported methods found, parse further if (sm.size()) { - std::string foundStr = sm.str(1); - boost::algorithm::to_lower(foundStr); + std::string httpMethod = sm.str(1); + boost::algorithm::to_lower(httpMethod); - json["action"] = foundStr; if (verifyPathAndStrip(restTarget, docRoot_)) { const std::regex regResources(regexResources_, std::regex_constants::icase); @@ -91,89 +160,76 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, // get requested resource type std::regex_search(restTarget, sm, regResources); if (sm.size()) { - foundStr = sm.str(1); + std::string foundStr = sm.str(1); if (verifyPathAndStrip(restTarget, foundStr)) { + // ////// + // signals handler if (foundStr == "signals") { - std::string signalPath; - std::string delimiter("/"); - - if (restTarget.size() && verifyPathAndStrip(restTarget, delimiter)) { - while (restTarget.size()) { - // we only accept clean printable characters - const std::regex regexValidWord("^([A-Za-z]+)"); - - if (std::regex_search(restTarget, sm, regexValidWord)) { - foundStr = sm.str(1); - signalPath += foundStr; - if (verifyPathAndStrip(restTarget, foundStr)) { - if ((restTarget.size() == 0)) { - break; - } - else if (verifyPathAndStrip(restTarget, delimiter)) { - signalPath += '.'; - } - else { - jsonRequest = JsonResponses::malFormedRequest( - requestId, - json["action"].as_string(), - "Signal path delimiter not valid"); - ret = false; - } - } - else { - jsonRequest = JsonResponses::malFormedRequest( - requestId, - json["action"].as_string(), - "Signal path not valid"); - ret = false; - } - } - else { - jsonRequest = JsonResponses::malFormedRequest( - requestId, - json["action"].as_string(), - "Signal path URI not valid"); - ret = false; - } - } + if (httpMethod == "get") { + ret = GetSignalPath(requestId, json, std::move(restTarget)); + + json["action"] = "get"; } else { - // not supporting retrieving of all signals by default - jsonRequest = JsonResponses::malFormedRequest( + // TODO: handle signal SET + JsonResponses::malFormedRequest( requestId, json["action"].as_string(), - "Signals cannot be retrieved in bulk request, only through single signal requests"); - ret = false; + "SET method not yet supported for 'signals' resource", + json); } + } + // ////// + // metadata handler + else if (foundStr == "metadata") { + if (httpMethod == "get") { + ret = GetSignalPath(requestId, json, std::move(restTarget)); - // update signal path if all is OK - if (ret) { - json["path"] = signalPath; + json["action"] = "getMetadata"; + } + else { + // TODO: handle signal SET + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "SET method not supported for 'metadata' resource", + json); } } - else if (sm.str(1) == "metadata") { - // TODO: add support for metadata - jsonRequest = JsonResponses::noAccess( - requestId, - json["action"].as_string(), - "Access to metadata not yet supported"); + // ////// + // authorize handler + else if (foundStr == "authorize") { + if (httpMethod == "set") { + json["action"] = "authorize"; + } + else { + // GET not supported for authorize resource + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "GET method not supported for 'authorize' resource", + json); + ret = false; + } } } else { - jsonRequest = JsonResponses::malFormedRequest( + JsonResponses::malFormedRequest( requestId, json["action"].as_string(), - "Signal path URI not valid"); + "Signal path URI not valid", + json); ret = false; } } else { - jsonRequest = JsonResponses::malFormedRequest( + JsonResponses::malFormedRequest( requestId, json["action"].as_string(), - "Requested resource do not exist"); + "Requested resource do not exist", + json); ret = false; } } @@ -181,10 +237,11 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, else { // TODO: evaluate what and how we should support HTTP methods (put, patch, ...) - jsonRequest = JsonResponses::malFormedRequest( + JsonResponses::malFormedRequest( requestId, json["action"].as_string(), - "Requested HTTP method is not supported"); + "Requested HTTP method is not supported", + json); ret = false; } From a7c1b469482464bd8ccb5b20c48ed5798220544c Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Wed, 23 Oct 2019 08:10:07 +0200 Subject: [PATCH 24/32] Added handling of 'authorize' action for REST API Evaluate and accept POST request for: /vss/api/v1/authorize?token=TOKEN_DATA Added web-test client support to automatically generate token based on input data Signed-off-by: Miladinovic Bojan --- .../include/RestV1ApiHandler.hpp | 2 + w3c-visserver-api/src/RestV1ApiHandler.cpp | 54 +++- w3c-visserver-api/test/web-client/index.html | 7 +- .../web-client/jsrsasign-latest-all-min.js | 246 ++++++++++++++++++ .../test/web-client/rest-client.js | 34 +-- w3c-visserver-api/test/web-client/style.css | 4 +- 6 files changed, 318 insertions(+), 29 deletions(-) create mode 100644 w3c-visserver-api/test/web-client/jsrsasign-latest-all-min.js diff --git a/w3c-visserver-api/include/RestV1ApiHandler.hpp b/w3c-visserver-api/include/RestV1ApiHandler.hpp index aabf71b..442c56f 100644 --- a/w3c-visserver-api/include/RestV1ApiHandler.hpp +++ b/w3c-visserver-api/include/RestV1ApiHandler.hpp @@ -39,6 +39,7 @@ class RestV1ApiHandler : public IRestHandler { //const std::unordered_map resourceHandlers_; std::string regexResources_; std::string regexHttpMethods_; + std::string regexToken_; public: /** @@ -47,6 +48,7 @@ class RestV1ApiHandler : public IRestHandler { enum class Resources { Signals, //!< Signals Metadata, //!< Metadata + Authorize, //!< Authorize Count }; diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp index a7ca4b7..b1416ef 100644 --- a/w3c-visserver-api/src/RestV1ApiHandler.cpp +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -28,10 +28,12 @@ RestV1ApiHandler::RestV1ApiHandler(std::shared_ptr loggerUtil, std::str docRoot_(docRoot) { // Supported HTTP methods - regexHttpMethods_ = "^(GET|SET)"; + regexHttpMethods_ = "^(GET|POST)"; // Resource strings for REST API hooks. Order must match order in RestV1ApiHandler::Resources enum - resourceHandleNames_ = std::vector{std::string{"signals"}, std::string{"metadata"}}; + resourceHandleNames_ = std::vector{std::string{"signals"}, + std::string{"metadata"}, + std::string{"authorize"}}; // verify that sizes match assert(resourceHandleNames_.size() == static_cast(Resources::Count)); @@ -41,6 +43,9 @@ RestV1ApiHandler::RestV1ApiHandler(std::shared_ptr loggerUtil, std::str regexResources_ += resourceHandleNames_[i] + std::string("|"); } regexResources_ += resourceHandleNames_[resourceHandleNames_.size() - 1u] + std::string(")"); + + // base64 url allowed characters + regexToken_ = std::string("[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+"); } RestV1ApiHandler::~RestV1ApiHandler() { @@ -153,7 +158,6 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, std::string httpMethod = sm.str(1); boost::algorithm::to_lower(httpMethod); - if (verifyPathAndStrip(restTarget, docRoot_)) { const std::regex regResources(regexResources_, std::regex_constants::icase); @@ -172,11 +176,11 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, json["action"] = "get"; } else { - // TODO: handle signal SET + // TODO: handle signal POST JsonResponses::malFormedRequest( requestId, json["action"].as_string(), - "SET method not yet supported for 'signals' resource", + "POST method not yet supported for 'signals' resource", json); } } @@ -189,19 +193,43 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, json["action"] = "getMetadata"; } else { - // TODO: handle signal SET + // TODO: handle signal POST JsonResponses::malFormedRequest( requestId, json["action"].as_string(), - "SET method not supported for 'metadata' resource", + "POST method not supported for 'metadata' resource", json); } } // ////// // authorize handler else if (foundStr == "authorize") { - if (httpMethod == "set") { - json["action"] = "authorize"; + if (httpMethod == "post") { + std::string tokenParam("?token="); + if (verifyPathAndStrip(restTarget, tokenParam)) { + const std::regex regToken(regexToken_); + std::regex_search(restTarget, sm, regToken); + + sleep(1); + if (sm.size()) { + json["action"] = "authorize"; + json["tokens"] = sm.str(0); + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Token for 'authorize' not valid", + json); + } + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Parameters for 'authorize' not valid", + json); + } } else { // GET not supported for authorize resource @@ -213,6 +241,14 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, ret = false; } } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Requested resource do not exist", + json); + ret = false; + } } else { JsonResponses::malFormedRequest( diff --git a/w3c-visserver-api/test/web-client/index.html b/w3c-visserver-api/test/web-client/index.html index a873b36..cc14988 100644 --- a/w3c-visserver-api/test/web-client/index.html +++ b/w3c-visserver-api/test/web-client/index.html @@ -9,13 +9,12 @@ - + - - + @@ -44,7 +43,7 @@

- +
diff --git a/w3c-visserver-api/test/web-client/jsrsasign-latest-all-min.js b/w3c-visserver-api/test/web-client/jsrsasign-latest-all-min.js new file mode 100644 index 0000000..cf81f44 --- /dev/null +++ b/w3c-visserver-api/test/web-client/jsrsasign-latest-all-min.js @@ -0,0 +1,246 @@ +/* + * jsrsasign(all) 8.0.12 (2018-04-22) (c) 2010-2018 Kenji Urushima | kjur.github.com/jsrsasign/license + */ + +/*! +Copyright (c) 2011, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.9.0 +*/ +if(YAHOO===undefined){var YAHOO={}}YAHOO.lang={extend:function(g,h,f){if(!h||!g){throw new Error("YAHOO.lang.extend failed, please check that all dependencies are included.")}var d=function(){};d.prototype=h.prototype;g.prototype=new d();g.prototype.constructor=g;g.superclass=h.prototype;if(h.prototype.constructor==Object.prototype.constructor){h.prototype.constructor=h}if(f){var b;for(b in f){g.prototype[b]=f[b]}var e=function(){},c=["toString","valueOf"];try{if(/MSIE/.test(navigator.userAgent)){e=function(j,i){for(b=0;b>>2]>>>(24-(r%4)*8))&255;q[(n+r)>>>2]|=o<<(24-((n+r)%4)*8)}}else{for(var r=0;r>>2]=p[r>>>2]}}this.sigBytes+=s;return this},clamp:function(){var o=this.words;var n=this.sigBytes;o[n>>>2]&=4294967295<<(32-(n%4)*8);o.length=e.ceil(n/4)},clone:function(){var n=j.clone.call(this);n.words=this.words.slice(0);return n},random:function(p){var o=[];for(var n=0;n>>2]>>>(24-(n%4)*8))&255;q.push((s>>>4).toString(16));q.push((s&15).toString(16))}return q.join("")},parse:function(p){var n=p.length;var q=[];for(var o=0;o>>3]|=parseInt(p.substr(o,2),16)<<(24-(o%8)*4)}return new l.init(q,n/2)}};var d=m.Latin1={stringify:function(q){var r=q.words;var p=q.sigBytes;var n=[];for(var o=0;o>>2]>>>(24-(o%4)*8))&255;n.push(String.fromCharCode(s))}return n.join("")},parse:function(p){var n=p.length;var q=[];for(var o=0;o>>2]|=(p.charCodeAt(o)&255)<<(24-(o%4)*8)}return new l.init(q,n)}};var c=m.Utf8={stringify:function(n){try{return decodeURIComponent(escape(d.stringify(n)))}catch(o){throw new Error("Malformed UTF-8 data")}},parse:function(n){return d.parse(unescape(encodeURIComponent(n)))}};var i=b.BufferedBlockAlgorithm=j.extend({reset:function(){this._data=new l.init();this._nDataBytes=0},_append:function(n){if(typeof n=="string"){n=c.parse(n)}this._data.concat(n);this._nDataBytes+=n.sigBytes},_process:function(w){var q=this._data;var x=q.words;var n=q.sigBytes;var t=this.blockSize;var v=t*4;var u=n/v;if(w){u=e.ceil(u)}else{u=e.max((u|0)-this._minBufferSize,0)}var s=u*t;var r=e.min(s*4,n);if(s){for(var p=0;p>>2]&255}};f.BlockCipher=n.extend({cfg:n.cfg.extend({mode:m,padding:h}),reset:function(){n.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1; +this._mode=c.call(a,this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var p=f.CipherParams=k.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),m=(g.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt; +return(a?l.create([1398893684,1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=l.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return p.create({ciphertext:a,salt:c})}},j=f.SerializableCipher=k.extend({cfg:k.extend({format:m}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var e=a.createEncryptor(c,d);b=e.finalize(b);e=e.cfg;return p.create({ciphertext:b,key:c,iv:e.iv,algorithm:a,mode:e.mode,padding:e.padding, +blockSize:a.blockSize,formatter:d.format})},decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),g=(g.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=l.random(8));a=v.create({keySize:b+c}).compute(a,d);c=l.create(a.words.slice(b),4*c);a.sigBytes=4*b;return p.create({key:a,iv:c,salt:d})}},s=f.PasswordBasedCipher=j.extend({cfg:j.cfg.extend({kdf:g}),encrypt:function(a, +b,c,d){d=this.cfg.extend(d);c=d.kdf.execute(c,a.keySize,a.ivSize);d.iv=c.iv;a=j.encrypt.call(this,a,b,c.key,d);a.mixIn(c);return a},decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);c=d.kdf.execute(c,a.keySize,a.ivSize,b.salt);d.iv=c.iv;return j.decrypt.call(this,a,b,c.key,d)}})}(); + +/* +CryptoJS v3.1.2 aes.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){for(var q=CryptoJS,x=q.lib.BlockCipher,r=q.algo,j=[],y=[],z=[],A=[],B=[],C=[],s=[],u=[],v=[],w=[],g=[],k=0;256>k;k++)g[k]=128>k?k<<1:k<<1^283;for(var n=0,l=0,k=0;256>k;k++){var f=l^l<<1^l<<2^l<<3^l<<4,f=f>>>8^f&255^99;j[n]=f;y[f]=n;var t=g[n],D=g[t],E=g[D],b=257*g[f]^16843008*f;z[n]=b<<24|b>>>8;A[n]=b<<16|b>>>16;B[n]=b<<8|b>>>24;C[n]=b;b=16843009*E^65537*D^257*t^16843008*n;s[f]=b<<24|b>>>8;u[f]=b<<16|b>>>16;v[f]=b<<8|b>>>24;w[f]=b;n?(n=t^g[g[g[E^t]]],l^=g[g[l]]):n=l=1}var F=[0,1,2,4,8, +16,32,64,128,27,54],r=r.AES=x.extend({_doReset:function(){for(var c=this._key,e=c.words,a=c.sigBytes/4,c=4*((this._nRounds=a+6)+1),b=this._keySchedule=[],h=0;h>>24]<<24|j[d>>>16&255]<<16|j[d>>>8&255]<<8|j[d&255]):(d=d<<8|d>>>24,d=j[d>>>24]<<24|j[d>>>16&255]<<16|j[d>>>8&255]<<8|j[d&255],d^=F[h/a|0]<<24);b[h]=b[h-a]^d}e=this._invKeySchedule=[];for(a=0;aa||4>=h?d:s[j[d>>>24]]^u[j[d>>>16&255]]^v[j[d>>> +8&255]]^w[j[d&255]]},encryptBlock:function(c,e){this._doCryptBlock(c,e,this._keySchedule,z,A,B,C,j)},decryptBlock:function(c,e){var a=c[e+1];c[e+1]=c[e+3];c[e+3]=a;this._doCryptBlock(c,e,this._invKeySchedule,s,u,v,w,y);a=c[e+1];c[e+1]=c[e+3];c[e+3]=a},_doCryptBlock:function(c,e,a,b,h,d,j,m){for(var n=this._nRounds,f=c[e]^a[0],g=c[e+1]^a[1],k=c[e+2]^a[2],p=c[e+3]^a[3],l=4,t=1;t>>24]^h[g>>>16&255]^d[k>>>8&255]^j[p&255]^a[l++],r=b[g>>>24]^h[k>>>16&255]^d[p>>>8&255]^j[f&255]^a[l++],s= +b[k>>>24]^h[p>>>16&255]^d[f>>>8&255]^j[g&255]^a[l++],p=b[p>>>24]^h[f>>>16&255]^d[g>>>8&255]^j[k&255]^a[l++],f=q,g=r,k=s;q=(m[f>>>24]<<24|m[g>>>16&255]<<16|m[k>>>8&255]<<8|m[p&255])^a[l++];r=(m[g>>>24]<<24|m[k>>>16&255]<<16|m[p>>>8&255]<<8|m[f&255])^a[l++];s=(m[k>>>24]<<24|m[p>>>16&255]<<16|m[f>>>8&255]<<8|m[g&255])^a[l++];p=(m[p>>>24]<<24|m[f>>>16&255]<<16|m[g>>>8&255]<<8|m[k&255])^a[l++];c[e]=q;c[e+1]=r;c[e+2]=s;c[e+3]=p},keySize:8});q.AES=x._createHelper(r)})(); + +/* +CryptoJS v3.1.2 tripledes-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){function j(b,c){var a=(this._lBlock>>>b^this._rBlock)&c;this._rBlock^=a;this._lBlock^=a<>>b^this._lBlock)&c;this._lBlock^=a;this._rBlock^=a<a;a++){var f=q[a]-1;c[a]=b[f>>>5]>>>31-f%32&1}b=this._subKeys=[];for(f=0;16>f;f++){for(var d=b[f]=[],e=r[f],a=0;24>a;a++)d[a/6|0]|=c[(p[a]-1+e)%28]<<31-a%6,d[4+(a/6|0)]|=c[28+(p[a+24]-1+e)%28]<<31-a%6;d[0]=d[0]<<1|d[0]>>>31;for(a=1;7>a;a++)d[a]>>>= +4*(a-1)+3;d[7]=d[7]<<5|d[7]>>>27}c=this._invSubKeys=[];for(a=0;16>a;a++)c[a]=b[15-a]},encryptBlock:function(b,c){this._doCryptBlock(b,c,this._subKeys)},decryptBlock:function(b,c){this._doCryptBlock(b,c,this._invSubKeys)},_doCryptBlock:function(b,c,a){this._lBlock=b[c];this._rBlock=b[c+1];j.call(this,4,252645135);j.call(this,16,65535);l.call(this,2,858993459);l.call(this,8,16711935);j.call(this,1,1431655765);for(var f=0;16>f;f++){for(var d=a[f],e=this._lBlock,h=this._rBlock,g=0,k=0;8>k;k++)g|=s[k][((h^ +d[k])&t[k])>>>0];this._lBlock=h;this._rBlock=e^g}a=this._lBlock;this._lBlock=this._rBlock;this._rBlock=a;j.call(this,1,1431655765);l.call(this,8,16711935);l.call(this,2,858993459);j.call(this,16,65535);j.call(this,4,252645135);b[c]=this._lBlock;b[c+1]=this._rBlock},keySize:2,ivSize:2,blockSize:2});h.DES=e._createHelper(m);g=g.TripleDES=e.extend({_doReset:function(){var b=this._key.words;this._des1=m.createEncryptor(n.create(b.slice(0,2)));this._des2=m.createEncryptor(n.create(b.slice(2,4)));this._des3= +m.createEncryptor(n.create(b.slice(4,6)))},encryptBlock:function(b,c){this._des1.encryptBlock(b,c);this._des2.decryptBlock(b,c);this._des3.encryptBlock(b,c)},decryptBlock:function(b,c){this._des3.decryptBlock(b,c);this._des2.encryptBlock(b,c);this._des1.decryptBlock(b,c)},keySize:6,ivSize:2,blockSize:2});h.TripleDES=e._createHelper(g)})(); + +/* +CryptoJS v3.1.2 enc-base64.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var h=CryptoJS,j=h.lib.WordArray;h.enc.Base64={stringify:function(b){var e=b.words,f=b.sigBytes,c=this._map;b.clamp();b=[];for(var a=0;a>>2]>>>24-8*(a%4)&255)<<16|(e[a+1>>>2]>>>24-8*((a+1)%4)&255)<<8|e[a+2>>>2]>>>24-8*((a+2)%4)&255,g=0;4>g&&a+0.75*g>>6*(3-g)&63));if(e=c.charAt(64))for(;b.length%4;)b.push(e);return b.join("")},parse:function(b){var e=b.length,f=this._map,c=f.charAt(64);c&&(c=b.indexOf(c),-1!=c&&(e=c));for(var c=[],a=0,d=0;d< +e;d++)if(d%4){var g=f.indexOf(b.charAt(d-1))<<2*(d%4),h=f.indexOf(b.charAt(d))>>>6-2*(d%4);c[a>>>2]|=(g|h)<<24-8*(a%4);a++}return j.create(c,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})(); + +/* +CryptoJS v3.1.2 md5.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(E){function h(a,f,g,j,p,h,k){a=a+(f&g|~f&j)+p+k;return(a<>>32-h)+f}function k(a,f,g,j,p,h,k){a=a+(f&j|g&~j)+p+k;return(a<>>32-h)+f}function l(a,f,g,j,h,k,l){a=a+(f^g^j)+h+l;return(a<>>32-k)+f}function n(a,f,g,j,h,k,l){a=a+(g^(f|~j))+h+l;return(a<>>32-k)+f}for(var r=CryptoJS,q=r.lib,F=q.WordArray,s=q.Hasher,q=r.algo,a=[],t=0;64>t;t++)a[t]=4294967296*E.abs(E.sin(t+1))|0;q=q.MD5=s.extend({_doReset:function(){this._hash=new F.init([1732584193,4023233417,2562383102,271733878])}, +_doProcessBlock:function(m,f){for(var g=0;16>g;g++){var j=f+g,p=m[j];m[j]=(p<<8|p>>>24)&16711935|(p<<24|p>>>8)&4278255360}var g=this._hash.words,j=m[f+0],p=m[f+1],q=m[f+2],r=m[f+3],s=m[f+4],t=m[f+5],u=m[f+6],v=m[f+7],w=m[f+8],x=m[f+9],y=m[f+10],z=m[f+11],A=m[f+12],B=m[f+13],C=m[f+14],D=m[f+15],b=g[0],c=g[1],d=g[2],e=g[3],b=h(b,c,d,e,j,7,a[0]),e=h(e,b,c,d,p,12,a[1]),d=h(d,e,b,c,q,17,a[2]),c=h(c,d,e,b,r,22,a[3]),b=h(b,c,d,e,s,7,a[4]),e=h(e,b,c,d,t,12,a[5]),d=h(d,e,b,c,u,17,a[6]),c=h(c,d,e,b,v,22,a[7]), +b=h(b,c,d,e,w,7,a[8]),e=h(e,b,c,d,x,12,a[9]),d=h(d,e,b,c,y,17,a[10]),c=h(c,d,e,b,z,22,a[11]),b=h(b,c,d,e,A,7,a[12]),e=h(e,b,c,d,B,12,a[13]),d=h(d,e,b,c,C,17,a[14]),c=h(c,d,e,b,D,22,a[15]),b=k(b,c,d,e,p,5,a[16]),e=k(e,b,c,d,u,9,a[17]),d=k(d,e,b,c,z,14,a[18]),c=k(c,d,e,b,j,20,a[19]),b=k(b,c,d,e,t,5,a[20]),e=k(e,b,c,d,y,9,a[21]),d=k(d,e,b,c,D,14,a[22]),c=k(c,d,e,b,s,20,a[23]),b=k(b,c,d,e,x,5,a[24]),e=k(e,b,c,d,C,9,a[25]),d=k(d,e,b,c,r,14,a[26]),c=k(c,d,e,b,w,20,a[27]),b=k(b,c,d,e,B,5,a[28]),e=k(e,b, +c,d,q,9,a[29]),d=k(d,e,b,c,v,14,a[30]),c=k(c,d,e,b,A,20,a[31]),b=l(b,c,d,e,t,4,a[32]),e=l(e,b,c,d,w,11,a[33]),d=l(d,e,b,c,z,16,a[34]),c=l(c,d,e,b,C,23,a[35]),b=l(b,c,d,e,p,4,a[36]),e=l(e,b,c,d,s,11,a[37]),d=l(d,e,b,c,v,16,a[38]),c=l(c,d,e,b,y,23,a[39]),b=l(b,c,d,e,B,4,a[40]),e=l(e,b,c,d,j,11,a[41]),d=l(d,e,b,c,r,16,a[42]),c=l(c,d,e,b,u,23,a[43]),b=l(b,c,d,e,x,4,a[44]),e=l(e,b,c,d,A,11,a[45]),d=l(d,e,b,c,D,16,a[46]),c=l(c,d,e,b,q,23,a[47]),b=n(b,c,d,e,j,6,a[48]),e=n(e,b,c,d,v,10,a[49]),d=n(d,e,b,c, +C,15,a[50]),c=n(c,d,e,b,t,21,a[51]),b=n(b,c,d,e,A,6,a[52]),e=n(e,b,c,d,r,10,a[53]),d=n(d,e,b,c,y,15,a[54]),c=n(c,d,e,b,p,21,a[55]),b=n(b,c,d,e,w,6,a[56]),e=n(e,b,c,d,D,10,a[57]),d=n(d,e,b,c,u,15,a[58]),c=n(c,d,e,b,B,21,a[59]),b=n(b,c,d,e,s,6,a[60]),e=n(e,b,c,d,z,10,a[61]),d=n(d,e,b,c,q,15,a[62]),c=n(c,d,e,b,x,21,a[63]);g[0]=g[0]+b|0;g[1]=g[1]+c|0;g[2]=g[2]+d|0;g[3]=g[3]+e|0},_doFinalize:function(){var a=this._data,f=a.words,g=8*this._nDataBytes,j=8*a.sigBytes;f[j>>>5]|=128<<24-j%32;var h=E.floor(g/ +4294967296);f[(j+64>>>9<<4)+15]=(h<<8|h>>>24)&16711935|(h<<24|h>>>8)&4278255360;f[(j+64>>>9<<4)+14]=(g<<8|g>>>24)&16711935|(g<<24|g>>>8)&4278255360;a.sigBytes=4*(f.length+1);this._process();a=this._hash;f=a.words;for(g=0;4>g;g++)j=f[g],f[g]=(j<<8|j>>>24)&16711935|(j<<24|j>>>8)&4278255360;return a},clone:function(){var a=s.clone.call(this);a._hash=this._hash.clone();return a}});r.MD5=s._createHelper(q);r.HmacMD5=s._createHmacHelper(q)})(Math); + +/* +CryptoJS v3.1.2 sha1-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var k=CryptoJS,b=k.lib,m=b.WordArray,l=b.Hasher,d=[],b=k.algo.SHA1=l.extend({_doReset:function(){this._hash=new m.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(n,p){for(var a=this._hash.words,e=a[0],f=a[1],h=a[2],j=a[3],b=a[4],c=0;80>c;c++){if(16>c)d[c]=n[p+c]|0;else{var g=d[c-3]^d[c-8]^d[c-14]^d[c-16];d[c]=g<<1|g>>>31}g=(e<<5|e>>>27)+b+d[c];g=20>c?g+((f&h|~f&j)+1518500249):40>c?g+((f^h^j)+1859775393):60>c?g+((f&h|f&j|h&j)-1894007588):g+((f^h^ +j)-899497514);b=j;j=h;h=f<<30|f>>>2;f=e;e=g}a[0]=a[0]+e|0;a[1]=a[1]+f|0;a[2]=a[2]+h|0;a[3]=a[3]+j|0;a[4]=a[4]+b|0},_doFinalize:function(){var b=this._data,d=b.words,a=8*this._nDataBytes,e=8*b.sigBytes;d[e>>>5]|=128<<24-e%32;d[(e+64>>>9<<4)+14]=Math.floor(a/4294967296);d[(e+64>>>9<<4)+15]=a;b.sigBytes=4*d.length;this._process();return this._hash},clone:function(){var b=l.clone.call(this);b._hash=this._hash.clone();return b}});k.SHA1=l._createHelper(b);k.HmacSHA1=l._createHmacHelper(b)})(); + +/* +CryptoJS v3.1.2 sha256-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(k){for(var g=CryptoJS,h=g.lib,v=h.WordArray,j=h.Hasher,h=g.algo,s=[],t=[],u=function(q){return 4294967296*(q-(q|0))|0},l=2,b=0;64>b;){var d;a:{d=l;for(var w=k.sqrt(d),r=2;r<=w;r++)if(!(d%r)){d=!1;break a}d=!0}d&&(8>b&&(s[b]=u(k.pow(l,0.5))),t[b]=u(k.pow(l,1/3)),b++);l++}var n=[],h=h.SHA256=j.extend({_doReset:function(){this._hash=new v.init(s.slice(0))},_doProcessBlock:function(q,h){for(var a=this._hash.words,c=a[0],d=a[1],b=a[2],k=a[3],f=a[4],g=a[5],j=a[6],l=a[7],e=0;64>e;e++){if(16>e)n[e]= +q[h+e]|0;else{var m=n[e-15],p=n[e-2];n[e]=((m<<25|m>>>7)^(m<<14|m>>>18)^m>>>3)+n[e-7]+((p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10)+n[e-16]}m=l+((f<<26|f>>>6)^(f<<21|f>>>11)^(f<<7|f>>>25))+(f&g^~f&j)+t[e]+n[e];p=((c<<30|c>>>2)^(c<<19|c>>>13)^(c<<10|c>>>22))+(c&d^c&b^d&b);l=j;j=g;g=f;f=k+m|0;k=b;b=d;d=c;c=m+p|0}a[0]=a[0]+c|0;a[1]=a[1]+d|0;a[2]=a[2]+b|0;a[3]=a[3]+k|0;a[4]=a[4]+f|0;a[5]=a[5]+g|0;a[6]=a[6]+j|0;a[7]=a[7]+l|0},_doFinalize:function(){var d=this._data,b=d.words,a=8*this._nDataBytes,c=8*d.sigBytes; +b[c>>>5]|=128<<24-c%32;b[(c+64>>>9<<4)+14]=k.floor(a/4294967296);b[(c+64>>>9<<4)+15]=a;d.sigBytes=4*b.length;this._process();return this._hash},clone:function(){var b=j.clone.call(this);b._hash=this._hash.clone();return b}});g.SHA256=j._createHelper(h);g.HmacSHA256=j._createHmacHelper(h)})(Math); + +/* +CryptoJS v3.1.2 sha224-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var b=CryptoJS,d=b.lib.WordArray,a=b.algo,c=a.SHA256,a=a.SHA224=c.extend({_doReset:function(){this._hash=new d.init([3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428])},_doFinalize:function(){var a=c._doFinalize.call(this);a.sigBytes-=4;return a}});b.SHA224=c._createHelper(a);b.HmacSHA224=c._createHmacHelper(a)})(); + +/* +CryptoJS v3.1.2 sha512-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){function a(){return d.create.apply(d,arguments)}for(var n=CryptoJS,r=n.lib.Hasher,e=n.x64,d=e.Word,T=e.WordArray,e=n.algo,ea=[a(1116352408,3609767458),a(1899447441,602891725),a(3049323471,3964484399),a(3921009573,2173295548),a(961987163,4081628472),a(1508970993,3053834265),a(2453635748,2937671579),a(2870763221,3664609560),a(3624381080,2734883394),a(310598401,1164996542),a(607225278,1323610764),a(1426881987,3590304994),a(1925078388,4068182383),a(2162078206,991336113),a(2614888103,633803317), +a(3248222580,3479774868),a(3835390401,2666613458),a(4022224774,944711139),a(264347078,2341262773),a(604807628,2007800933),a(770255983,1495990901),a(1249150122,1856431235),a(1555081692,3175218132),a(1996064986,2198950837),a(2554220882,3999719339),a(2821834349,766784016),a(2952996808,2566594879),a(3210313671,3203337956),a(3336571891,1034457026),a(3584528711,2466948901),a(113926993,3758326383),a(338241895,168717936),a(666307205,1188179964),a(773529912,1546045734),a(1294757372,1522805485),a(1396182291, +2643833823),a(1695183700,2343527390),a(1986661051,1014477480),a(2177026350,1206759142),a(2456956037,344077627),a(2730485921,1290863460),a(2820302411,3158454273),a(3259730800,3505952657),a(3345764771,106217008),a(3516065817,3606008344),a(3600352804,1432725776),a(4094571909,1467031594),a(275423344,851169720),a(430227734,3100823752),a(506948616,1363258195),a(659060556,3750685593),a(883997877,3785050280),a(958139571,3318307427),a(1322822218,3812723403),a(1537002063,2003034995),a(1747873779,3602036899), +a(1955562222,1575990012),a(2024104815,1125592928),a(2227730452,2716904306),a(2361852424,442776044),a(2428436474,593698344),a(2756734187,3733110249),a(3204031479,2999351573),a(3329325298,3815920427),a(3391569614,3928383900),a(3515267271,566280711),a(3940187606,3454069534),a(4118630271,4000239992),a(116418474,1914138554),a(174292421,2731055270),a(289380356,3203993006),a(460393269,320620315),a(685471733,587496836),a(852142971,1086792851),a(1017036298,365543100),a(1126000580,2618297676),a(1288033470, +3409855158),a(1501505948,4234509866),a(1607167915,987167468),a(1816402316,1246189591)],v=[],w=0;80>w;w++)v[w]=a();e=e.SHA512=r.extend({_doReset:function(){this._hash=new T.init([new d.init(1779033703,4089235720),new d.init(3144134277,2227873595),new d.init(1013904242,4271175723),new d.init(2773480762,1595750129),new d.init(1359893119,2917565137),new d.init(2600822924,725511199),new d.init(528734635,4215389547),new d.init(1541459225,327033209)])},_doProcessBlock:function(a,d){for(var f=this._hash.words, +F=f[0],e=f[1],n=f[2],r=f[3],G=f[4],H=f[5],I=f[6],f=f[7],w=F.high,J=F.low,X=e.high,K=e.low,Y=n.high,L=n.low,Z=r.high,M=r.low,$=G.high,N=G.low,aa=H.high,O=H.low,ba=I.high,P=I.low,ca=f.high,Q=f.low,k=w,g=J,z=X,x=K,A=Y,y=L,U=Z,B=M,l=$,h=N,R=aa,C=O,S=ba,D=P,V=ca,E=Q,m=0;80>m;m++){var s=v[m];if(16>m)var j=s.high=a[d+2*m]|0,b=s.low=a[d+2*m+1]|0;else{var j=v[m-15],b=j.high,p=j.low,j=(b>>>1|p<<31)^(b>>>8|p<<24)^b>>>7,p=(p>>>1|b<<31)^(p>>>8|b<<24)^(p>>>7|b<<25),u=v[m-2],b=u.high,c=u.low,u=(b>>>19|c<<13)^(b<< +3|c>>>29)^b>>>6,c=(c>>>19|b<<13)^(c<<3|b>>>29)^(c>>>6|b<<26),b=v[m-7],W=b.high,t=v[m-16],q=t.high,t=t.low,b=p+b.low,j=j+W+(b>>>0

>>0?1:0),b=b+c,j=j+u+(b>>>0>>0?1:0),b=b+t,j=j+q+(b>>>0>>0?1:0);s.high=j;s.low=b}var W=l&R^~l&S,t=h&C^~h&D,s=k&z^k&A^z&A,T=g&x^g&y^x&y,p=(k>>>28|g<<4)^(k<<30|g>>>2)^(k<<25|g>>>7),u=(g>>>28|k<<4)^(g<<30|k>>>2)^(g<<25|k>>>7),c=ea[m],fa=c.high,da=c.low,c=E+((h>>>14|l<<18)^(h>>>18|l<<14)^(h<<23|l>>>9)),q=V+((l>>>14|h<<18)^(l>>>18|h<<14)^(l<<23|h>>>9))+(c>>>0>>0?1: +0),c=c+t,q=q+W+(c>>>0>>0?1:0),c=c+da,q=q+fa+(c>>>0>>0?1:0),c=c+b,q=q+j+(c>>>0>>0?1:0),b=u+T,s=p+s+(b>>>0>>0?1:0),V=S,E=D,S=R,D=C,R=l,C=h,h=B+c|0,l=U+q+(h>>>0>>0?1:0)|0,U=A,B=y,A=z,y=x,z=k,x=g,g=c+b|0,k=q+s+(g>>>0>>0?1:0)|0}J=F.low=J+g;F.high=w+k+(J>>>0>>0?1:0);K=e.low=K+x;e.high=X+z+(K>>>0>>0?1:0);L=n.low=L+y;n.high=Y+A+(L>>>0>>0?1:0);M=r.low=M+B;r.high=Z+U+(M>>>0>>0?1:0);N=G.low=N+h;G.high=$+l+(N>>>0>>0?1:0);O=H.low=O+C;H.high=aa+R+(O>>>0>>0?1:0);P=I.low=P+D; +I.high=ba+S+(P>>>0>>0?1:0);Q=f.low=Q+E;f.high=ca+V+(Q>>>0>>0?1:0)},_doFinalize:function(){var a=this._data,d=a.words,f=8*this._nDataBytes,e=8*a.sigBytes;d[e>>>5]|=128<<24-e%32;d[(e+128>>>10<<5)+30]=Math.floor(f/4294967296);d[(e+128>>>10<<5)+31]=f;a.sigBytes=4*d.length;this._process();return this._hash.toX32()},clone:function(){var a=r.clone.call(this);a._hash=this._hash.clone();return a},blockSize:32});n.SHA512=r._createHelper(e);n.HmacSHA512=r._createHmacHelper(e)})(); + +/* +CryptoJS v3.1.2 sha384-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var c=CryptoJS,a=c.x64,b=a.Word,e=a.WordArray,a=c.algo,d=a.SHA512,a=a.SHA384=d.extend({_doReset:function(){this._hash=new e.init([new b.init(3418070365,3238371032),new b.init(1654270250,914150663),new b.init(2438529370,812702999),new b.init(355462360,4144912697),new b.init(1731405415,4290775857),new b.init(2394180231,1750603025),new b.init(3675008525,1694076839),new b.init(1203062813,3204075428)])},_doFinalize:function(){var a=d._doFinalize.call(this);a.sigBytes-=16;return a}});c.SHA384= +d._createHelper(a);c.HmacSHA384=d._createHmacHelper(a)})(); + +/* +CryptoJS v3.1.2 ripemd160-min.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +/* + +(c) 2012 by Cedric Mesnil. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +(function(){var q=CryptoJS,d=q.lib,n=d.WordArray,p=d.Hasher,d=q.algo,x=n.create([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]),y=n.create([5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]),z=n.create([11,14,15,12, +5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]),A=n.create([8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]),B=n.create([0,1518500249,1859775393,2400959708,2840853838]),C=n.create([1352829926,1548603684,1836072691, +2053994217,0]),d=d.RIPEMD160=p.extend({_doReset:function(){this._hash=n.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(e,v){for(var b=0;16>b;b++){var c=v+b,f=e[c];e[c]=(f<<8|f>>>24)&16711935|(f<<24|f>>>8)&4278255360}var c=this._hash.words,f=B.words,d=C.words,n=x.words,q=y.words,p=z.words,w=A.words,t,g,h,j,r,u,k,l,m,s;u=t=c[0];k=g=c[1];l=h=c[2];m=j=c[3];s=r=c[4];for(var a,b=0;80>b;b+=1)a=t+e[v+n[b]]|0,a=16>b?a+((g^h^j)+f[0]):32>b?a+((g&h|~g&j)+f[1]):48>b? +a+(((g|~h)^j)+f[2]):64>b?a+((g&j|h&~j)+f[3]):a+((g^(h|~j))+f[4]),a|=0,a=a<>>32-p[b],a=a+r|0,t=r,r=j,j=h<<10|h>>>22,h=g,g=a,a=u+e[v+q[b]]|0,a=16>b?a+((k^(l|~m))+d[0]):32>b?a+((k&m|l&~m)+d[1]):48>b?a+(((k|~l)^m)+d[2]):64>b?a+((k&l|~k&m)+d[3]):a+((k^l^m)+d[4]),a|=0,a=a<>>32-w[b],a=a+s|0,u=s,s=m,m=l<<10|l>>>22,l=k,k=a;a=c[1]+h+m|0;c[1]=c[2]+j+s|0;c[2]=c[3]+r+u|0;c[3]=c[4]+t+k|0;c[4]=c[0]+g+l|0;c[0]=a},_doFinalize:function(){var e=this._data,d=e.words,b=8*this._nDataBytes,c=8*e.sigBytes; +d[c>>>5]|=128<<24-c%32;d[(c+64>>>9<<4)+14]=(b<<8|b>>>24)&16711935|(b<<24|b>>>8)&4278255360;e.sigBytes=4*(d.length+1);this._process();e=this._hash;d=e.words;for(b=0;5>b;b++)c=d[b],d[b]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return e},clone:function(){var d=p.clone.call(this);d._hash=this._hash.clone();return d}});q.RIPEMD160=p._createHelper(d);q.HmacRIPEMD160=p._createHmacHelper(d)})(Math); + +/* +CryptoJS v3.1.2 hmac.js +code.google.com/p/crypto-js +(c) 2009-2013 by Jeff Mott. All rights reserved. +code.google.com/p/crypto-js/wiki/License +*/ +(function(){var c=CryptoJS,k=c.enc.Utf8;c.algo.HMAC=c.lib.Base.extend({init:function(a,b){a=this._hasher=new a.init;"string"==typeof b&&(b=k.parse(b));var c=a.blockSize,e=4*c;b.sigBytes>e&&(b=a.finalize(b));b.clamp();for(var f=this._oKey=b.clone(),g=this._iKey=b.clone(),h=f.words,j=g.words,d=0;d>6)+b64map.charAt(e&63)}if(b+1==d.length){e=parseInt(d.substring(b,b+1),16);a+=b64map.charAt(e<<2)}else{if(b+2==d.length){e=parseInt(d.substring(b,b+2),16);a+=b64map.charAt(e>>2)+b64map.charAt((e&3)<<4)}}if(b64pad){while((a.length&3)>0){a+=b64pad}}return a}function b64tohex(f){var d="";var e;var b=0;var c;var a;for(e=0;e>2);c=a&3;b=1}else{if(b==1){d+=int2char((c<<2)|(a>>4));c=a&15;b=2}else{if(b==2){d+=int2char(c);d+=int2char(a>>2);c=a&3;b=3}else{d+=int2char((c<<2)|(a>>4));d+=int2char(a&15);b=0}}}}if(b==1){d+=int2char(c<<2)}return d}function b64toBA(e){var d=b64tohex(e);var c;var b=new Array();for(c=0;2*c=0){var d=a*this[f++]+b[e]+h;h=Math.floor(d/67108864);b[e++]=d&67108863}return h}function am2(f,q,r,e,o,a){var k=q&32767,p=q>>15;while(--a>=0){var d=this[f]&32767;var g=this[f++]>>15;var b=p*d+g*k;d=k*d+((b&32767)<<15)+r[e]+(o&1073741823);o=(d>>>30)+(b>>>15)+p*g+(o>>>30);r[e++]=d&1073741823}return o}function am3(f,q,r,e,o,a){var k=q&16383,p=q>>14;while(--a>=0){var d=this[f]&16383;var g=this[f++]>>14;var b=p*d+g*k;d=k*d+((b&16383)<<14)+r[e]+o;o=(d>>28)+(b>>14)+p*g;r[e++]=d&268435455}return o}if(j_lm&&(navigator.appName=="Microsoft Internet Explorer")){BigInteger.prototype.am=am2;dbits=30}else{if(j_lm&&(navigator.appName!="Netscape")){BigInteger.prototype.am=am1;dbits=26}else{BigInteger.prototype.am=am3;dbits=28}}BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=((1<=0;--a){b[a]=this[a]}b.t=this.t;b.s=this.s}function bnpFromInt(a){this.t=1;this.s=(a<0)?-1:0;if(a>0){this[0]=a}else{if(a<-1){this[0]=a+this.DV}else{this.t=0}}}function nbv(a){var b=nbi();b.fromInt(a);return b}function bnpFromString(h,c){var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==256){e=8}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{this.fromRadix(h,c);return}}}}}}this.t=0;this.s=0;var g=h.length,d=false,f=0;while(--g>=0){var a=(e==8)?h[g]&255:intAt(h,g);if(a<0){if(h.charAt(g)=="-"){d=true}continue}d=false;if(f==0){this[this.t++]=a}else{if(f+e>this.DB){this[this.t-1]|=(a&((1<<(this.DB-f))-1))<>(this.DB-f))}else{this[this.t-1]|=a<=this.DB){f-=this.DB}}if(e==8&&(h[0]&128)!=0){this.s=-1;if(f>0){this[this.t-1]|=((1<<(this.DB-f))-1)<0&&this[this.t-1]==a){--this.t}}function bnToString(c){if(this.s<0){return"-"+this.negate().toString(c)}var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{return this.toRadix(c)}}}}}var g=(1<0){if(j>j)>0){a=true;h=int2char(l)}while(f>=0){if(j>(j+=this.DB-e)}else{l=(this[f]>>(j-=e))&g;if(j<=0){j+=this.DB;--f}}if(l>0){a=true}if(a){h+=int2char(l)}}}return a?h:"0"}function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a}function bnAbs(){return(this.s<0)?this.negate():this}function bnCompareTo(b){var d=this.s-b.s;if(d!=0){return d}var c=this.t;d=c-b.t;if(d!=0){return(this.s<0)?-d:d}while(--c>=0){if((d=this[c]-b[c])!=0){return d}}return 0}function nbits(a){var c=1,b;if((b=a>>>16)!=0){a=b;c+=16}if((b=a>>8)!=0){a=b;c+=8}if((b=a>>4)!=0){a=b;c+=4}if((b=a>>2)!=0){a=b;c+=2}if((b=a>>1)!=0){a=b;c+=1}return c}function bnBitLength(){if(this.t<=0){return 0}return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM))}function bnpDLShiftTo(c,b){var a;for(a=this.t-1;a>=0;--a){b[a+c]=this[a]}for(a=c-1;a>=0;--a){b[a]=0}b.t=this.t+c;b.s=this.s}function bnpDRShiftTo(c,b){for(var a=c;a=0;--d){e[d+f+1]=(this[d]>>a)|h;h=(this[d]&g)<=0;--d){e[d]=0}e[f]=h;e.t=this.t+f+1;e.s=this.s;e.clamp()}function bnpRShiftTo(g,d){d.s=this.s;var e=Math.floor(g/this.DB);if(e>=this.t){d.t=0;return}var b=g%this.DB;var a=this.DB-b;var f=(1<>b;for(var c=e+1;c>b}if(b>0){d[this.t-e-1]|=(this.s&f)<>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g-=d.s}f.s=(g<0)?-1:0;if(g<-1){f[e++]=this.DV+g}else{if(g>0){f[e++]=g}}f.t=e;f.clamp()}function bnpMultiplyTo(c,e){var b=this.abs(),f=c.abs();var d=b.t;e.t=d+f.t;while(--d>=0){e[d]=0}for(d=0;d=0){d[b]=0}for(b=0;b=a.DV){d[b+a.t]-=a.DV;d[b+a.t+1]=1}}if(d.t>0){d[d.t-1]+=a.am(b,a[b],d,2*b,0,1)}d.s=0;d.clamp()}function bnpDivRemTo(n,h,g){var w=n.abs();if(w.t<=0){return}var k=this.abs();if(k.t0){w.lShiftTo(v,d);k.lShiftTo(v,g)}else{w.copyTo(d);k.copyTo(g)}var p=d.t;var b=d[p-1];if(b==0){return}var o=b*(1<1)?d[p-2]>>this.F2:0);var A=this.FV/o,z=(1<=0){g[g.t++]=1;g.subTo(f,g)}BigInteger.ONE.dlShiftTo(p,f);f.subTo(d,d);while(d.t=0){var c=(g[--u]==b)?this.DM:Math.floor(g[u]*A+(g[u-1]+x)*z);if((g[u]+=d.am(0,c,g,s,0,p))0){g.rShiftTo(v,g)}if(a<0){BigInteger.ZERO.subTo(g,g)}}function bnMod(b){var c=nbi();this.abs().divRemTo(b,null,c);if(this.s<0&&c.compareTo(BigInteger.ZERO)>0){b.subTo(c,c)}return c}function Classic(a){this.m=a}function cConvert(a){if(a.s<0||a.compareTo(this.m)>=0){return a.mod(this.m)}else{return a}}function cRevert(a){return a}function cReduce(a){a.divRemTo(this.m,null,a)}function cMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}function cSqrTo(a,b){a.squareTo(b);this.reduce(b)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1){return 0}var a=this[0];if((a&1)==0){return 0}var b=a&3;b=(b*(2-(a&15)*b))&15;b=(b*(2-(a&255)*b))&255;b=(b*(2-(((a&65535)*b)&65535)))&65535;b=(b*(2-a*b%this.DV))%this.DV;return(b>0)?this.DV-b:-b}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<(a.DB-15))-1;this.mt2=2*a.t}function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);if(a.s<0&&b.compareTo(BigInteger.ZERO)>0){this.m.subTo(b,b)}return b}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b}function montReduce(a){while(a.t<=this.mt2){a[a.t++]=0}for(var c=0;c>15)*this.mpl)&this.um)<<15))&a.DM;b=c+this.m.t;a[b]+=this.m.am(0,d,a,c,0,this.m.t);while(a[b]>=a.DV){a[b]-=a.DV;a[++b]++}}a.clamp();a.drShiftTo(this.m.t,a);if(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function montSqrTo(a,b){a.squareTo(b);this.reduce(b)}function montMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return((this.t>0)?(this[0]&1):this.s)==0}function bnpExp(h,j){if(h>4294967295||h<1){return BigInteger.ONE}var f=nbi(),a=nbi(),d=j.convert(this),c=nbits(h)-1;d.copyTo(f);while(--c>=0){j.sqrTo(f,a);if((h&(1<0){j.mulTo(a,d,f)}else{var b=f;f=a;a=b}}return j.revert(f)}function bnModPowInt(b,a){var c;if(b<256||a.isEven()){c=new Classic(a)}else{c=new Montgomery(a)}return this.exp(b,c)}BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1); +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +function bnClone(){var a=nbi();this.copyTo(a);return a}function bnIntValue(){if(this.s<0){if(this.t==1){return this[0]-this.DV}else{if(this.t==0){return -1}}}else{if(this.t==1){return this[0]}else{if(this.t==0){return 0}}}return((this[1]&((1<<(32-this.DB))-1))<>24}function bnShortValue(){return(this.t==0)?this.s:(this[0]<<16)>>16}function bnpChunkSize(a){return Math.floor(Math.LN2*this.DB/Math.log(a))}function bnSigNum(){if(this.s<0){return -1}else{if(this.t<=0||(this.t==1&&this[0]<=0)){return 0}else{return 1}}}function bnpToRadix(c){if(c==null){c=10}if(this.signum()==0||c<2||c>36){return"0"}var f=this.chunkSize(c);var e=Math.pow(c,f);var i=nbv(e),j=nbi(),h=nbi(),g="";this.divRemTo(i,j,h);while(j.signum()>0){g=(e+h.intValue()).toString(c).substr(1)+g;j.divRemTo(i,j,h)}return h.intValue().toString(c)+g}function bnpFromRadix(m,h){this.fromInt(0);if(h==null){h=10}var f=this.chunkSize(h);var g=Math.pow(h,f),e=false,a=0,l=0;for(var c=0;c=f){this.dMultiply(g);this.dAddOffset(l,0);a=0;l=0}}if(a>0){this.dMultiply(Math.pow(h,a));this.dAddOffset(l,0)}if(e){BigInteger.ZERO.subTo(this,this)}}function bnpFromNumber(f,e,h){if("number"==typeof e){if(f<2){this.fromInt(1)}else{this.fromNumber(f,h);if(!this.testBit(f-1)){this.bitwiseTo(BigInteger.ONE.shiftLeft(f-1),op_or,this)}if(this.isEven()){this.dAddOffset(1,0)}while(!this.isProbablePrime(e)){this.dAddOffset(2,0);if(this.bitLength()>f){this.subTo(BigInteger.ONE.shiftLeft(f-1),this)}}}}else{var d=new Array(),g=f&7;d.length=(f>>3)+1;e.nextBytes(d);if(g>0){d[0]&=((1<0){if(e>e)!=(this.s&this.DM)>>e){c[a++]=f|(this.s<<(this.DB-e))}while(b>=0){if(e<8){f=(this[b]&((1<>(e+=this.DB-8)}else{f=(this[b]>>(e-=8))&255;if(e<=0){e+=this.DB;--b}}if((f&128)!=0){f|=-256}if(a==0&&(this.s&128)!=(f&128)){++a}if(a>0||f!=this.s){c[a++]=f}}}return c}function bnEquals(b){return(this.compareTo(b)==0)}function bnMin(b){return(this.compareTo(b)<0)?this:b}function bnMax(b){return(this.compareTo(b)>0)?this:b}function bnpBitwiseTo(c,h,e){var d,g,b=Math.min(c.t,this.t);for(d=0;d>=16;b+=16}if((a&255)==0){a>>=8;b+=8}if((a&15)==0){a>>=4;b+=4}if((a&3)==0){a>>=2;b+=2}if((a&1)==0){++b}return b}function bnGetLowestSetBit(){for(var a=0;a=this.t){return(this.s!=0)}return((this[a]&(1<<(b%this.DB)))!=0)}function bnpChangeBit(c,b){var a=BigInteger.ONE.shiftLeft(c);this.bitwiseTo(a,b,a);return a}function bnSetBit(a){return this.changeBit(a,op_or)}function bnClearBit(a){return this.changeBit(a,op_andnot)}function bnFlipBit(a){return this.changeBit(a,op_xor)}function bnpAddTo(d,f){var e=0,g=0,b=Math.min(d.t,this.t);while(e>=this.DB}if(d.t>=this.DB}g+=this.s}else{g+=this.s;while(e>=this.DB}g+=d.s}f.s=(g<0)?-1:0;if(g>0){f[e++]=g}else{if(g<-1){f[e++]=this.DV+g}}f.t=e;f.clamp()}function bnAdd(b){var c=nbi();this.addTo(b,c);return c}function bnSubtract(b){var c=nbi();this.subTo(b,c);return c}function bnMultiply(b){var c=nbi();this.multiplyTo(b,c);return c}function bnSquare(){var a=nbi();this.squareTo(a);return a}function bnDivide(b){var c=nbi();this.divRemTo(b,c,null);return c}function bnRemainder(b){var c=nbi();this.divRemTo(b,null,c);return c}function bnDivideAndRemainder(b){var d=nbi(),c=nbi();this.divRemTo(b,d,c);return new Array(d,c)}function bnpDMultiply(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()}function bnpDAddOffset(b,a){if(b==0){return}while(this.t<=a){this[this.t++]=0}this[a]+=b;while(this[a]>=this.DV){this[a]-=this.DV;if(++a>=this.t){this[this.t++]=0}++this[a]}}function NullExp(){}function nNop(a){return a}function nMulTo(a,c,b){a.multiplyTo(c,b)}function nSqrTo(a,b){a.squareTo(b)}NullExp.prototype.convert=nNop;NullExp.prototype.revert=nNop;NullExp.prototype.mulTo=nMulTo;NullExp.prototype.sqrTo=nSqrTo;function bnPow(a){return this.exp(a,new NullExp())}function bnpMultiplyLowerTo(b,f,e){var d=Math.min(this.t+b.t,f);e.s=0;e.t=d;while(d>0){e[--d]=0}var c;for(c=e.t-this.t;d=0){d[c]=0}for(c=Math.max(e-this.t,0);c2*this.m.t){return a.mod(this.m)}else{if(a.compareTo(this.m)<0){return a}else{var b=nbi();a.copyTo(b);this.reduce(b);return b}}}function barrettRevert(a){return a}function barrettReduce(a){a.drShiftTo(this.m.t-1,this.r2);if(a.t>this.m.t+1){a.t=this.m.t+1;a.clamp()}this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);while(a.compareTo(this.r2)<0){a.dAddOffset(1,this.m.t+1)}a.subTo(this.r2,a);while(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function barrettSqrTo(a,b){a.squareTo(b);this.reduce(b)}function barrettMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Barrett.prototype.convert=barrettConvert;Barrett.prototype.revert=barrettRevert;Barrett.prototype.reduce=barrettReduce;Barrett.prototype.mulTo=barrettMulTo;Barrett.prototype.sqrTo=barrettSqrTo;function bnModPow(q,f){var o=q.bitLength(),h,b=nbv(1),v;if(o<=0){return b}else{if(o<18){h=1}else{if(o<48){h=3}else{if(o<144){h=4}else{if(o<768){h=5}else{h=6}}}}}if(o<8){v=new Classic(f)}else{if(f.isEven()){v=new Barrett(f)}else{v=new Montgomery(f)}}var p=new Array(),d=3,s=h-1,a=(1<1){var A=nbi();v.sqrTo(p[1],A);while(d<=a){p[d]=nbi();v.mulTo(A,p[d-2],p[d]);d+=2}}var l=q.t-1,x,u=true,c=nbi(),y;o=nbits(q[l])-1;while(l>=0){if(o>=s){x=(q[l]>>(o-s))&a}else{x=(q[l]&((1<<(o+1))-1))<<(s-o);if(l>0){x|=q[l-1]>>(this.DB+o-s)}}d=h;while((x&1)==0){x>>=1;--d}if((o-=d)<0){o+=this.DB;--l}if(u){p[x].copyTo(b);u=false}else{while(d>1){v.sqrTo(b,c);v.sqrTo(c,b);d-=2}if(d>0){v.sqrTo(b,c)}else{y=b;b=c;c=y}v.mulTo(c,p[x],b)}while(l>=0&&(q[l]&(1<0){b.rShiftTo(f,b);h.rShiftTo(f,h)}while(b.signum()>0){if((d=b.getLowestSetBit())>0){b.rShiftTo(d,b)}if((d=h.getLowestSetBit())>0){h.rShiftTo(d,h)}if(b.compareTo(h)>=0){b.subTo(h,b);b.rShiftTo(1,b)}else{h.subTo(b,h);h.rShiftTo(1,h)}}if(f>0){h.lShiftTo(f,h)}return h}function bnpModInt(e){if(e<=0){return 0}var c=this.DV%e,b=(this.s<0)?e-1:0;if(this.t>0){if(c==0){b=this[0]%e}else{for(var a=this.t-1;a>=0;--a){b=(c*b+this[a])%e}}}return b}function bnModInverse(f){var j=f.isEven();if((this.isEven()&&j)||f.signum()==0){return BigInteger.ZERO}var i=f.clone(),h=this.clone();var g=nbv(1),e=nbv(0),l=nbv(0),k=nbv(1);while(i.signum()!=0){while(i.isEven()){i.rShiftTo(1,i);if(j){if(!g.isEven()||!e.isEven()){g.addTo(this,g);e.subTo(f,e)}g.rShiftTo(1,g)}else{if(!e.isEven()){e.subTo(f,e)}}e.rShiftTo(1,e)}while(h.isEven()){h.rShiftTo(1,h);if(j){if(!l.isEven()||!k.isEven()){l.addTo(this,l);k.subTo(f,k)}l.rShiftTo(1,l)}else{if(!k.isEven()){k.subTo(f,k)}}k.rShiftTo(1,k)}if(i.compareTo(h)>=0){i.subTo(h,i);if(j){g.subTo(l,g)}e.subTo(k,e)}else{h.subTo(i,h);if(j){l.subTo(g,l)}k.subTo(e,k)}}if(h.compareTo(BigInteger.ONE)!=0){return BigInteger.ZERO}if(k.compareTo(f)>=0){return k.subtract(f)}if(k.signum()<0){k.addTo(f,k)}else{return k}if(k.signum()<0){return k.add(f)}else{return k}}var lowprimes=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];var lplim=(1<<26)/lowprimes[lowprimes.length-1];function bnIsProbablePrime(e){var d,b=this.abs();if(b.t==1&&b[0]<=lowprimes[lowprimes.length-1]){for(d=0;d>1;if(f>lowprimes.length){f=lowprimes.length}var b=nbi();for(var e=0;e>8)&255;rng_pool[rng_pptr++]^=(a>>16)&255;rng_pool[rng_pptr++]^=(a>>24)&255;if(rng_pptr>=rng_psize){rng_pptr-=rng_psize}}function rng_seed_time(){rng_seed_int(new Date().getTime())}if(rng_pool==null){rng_pool=new Array();rng_pptr=0;var t;if(window!==undefined&&(window.crypto!==undefined||window.msCrypto!==undefined)){var crypto=window.crypto||window.msCrypto;if(crypto.getRandomValues){var ua=new Uint8Array(32);crypto.getRandomValues(ua);for(t=0;t<32;++t){rng_pool[rng_pptr++]=ua[t]}}else{if(navigator.appName=="Netscape"&&navigator.appVersion<"5"){var z=window.crypto.random(32);for(t=0;t>>8;rng_pool[rng_pptr++]=t&255}rng_pptr=0;rng_seed_time()}function rng_get_byte(){if(rng_state==null){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr=0&&h>0){var f=e.charCodeAt(d--);if(f<128){g[--h]=f}else{if((f>127)&&(f<2048)){g[--h]=(f&63)|128;g[--h]=(f>>6)|192}else{g[--h]=(f&63)|128;g[--h]=((f>>6)&63)|128;g[--h]=(f>>12)|224}}}g[--h]=0;var b=new SecureRandom();var a=new Array();while(h>2){a[0]=0;while(a[0]==0){b.nextBytes(a)}g[--h]=a[0]}g[--h]=2;g[--h]=0;return new BigInteger(g)}function oaep_mgf1_arr(c,a,e){var b="",d=0;while(b.length>24,(d&16711680)>>16,(d&65280)>>8,d&255])));d+=1}return b}function oaep_pad(q,a,f,l){var c=KJUR.crypto.MessageDigest;var o=KJUR.crypto.Util;var b=null;if(!f){f="sha1"}if(typeof f==="string"){b=c.getCanonicalAlgName(f);l=c.getHashLength(b);f=function(i){return hextorstr(o.hashHex(rstrtohex(i),b))}}if(q.length+2*l+2>a){throw"Message too long for RSA"}var k="",e;for(e=0;e0&&a.length>0){this.n=parseBigInt(b,16);this.e=parseInt(a,16)}else{throw"Invalid RSA public key"}}}function RSADoPublic(a){return a.modPowInt(this.e,this.n)}function RSAEncrypt(d){var a=pkcs1pad2(d,(this.n.bitLength()+7)>>3);if(a==null){return null}var e=this.doPublic(a);if(e==null){return null}var b=e.toString(16);if((b.length&1)==0){return b}else{return"0"+b}}function RSAEncryptOAEP(f,e,b){var a=oaep_pad(f,(this.n.bitLength()+7)>>3,e,b);if(a==null){return null}var g=this.doPublic(a);if(g==null){return null}var d=g.toString(16);if((d.length&1)==0){return d}else{return"0"+d}}RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;RSAKey.prototype.encryptOAEP=RSAEncryptOAEP;RSAKey.prototype.type="RSA"; +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +function pkcs1unpad2(g,j){var a=g.toByteArray();var f=0;while(f=a.length){return null}}var e="";while(++f191)&&(h<224)){e+=String.fromCharCode(((h&31)<<6)|(a[f+1]&63));++f}else{e+=String.fromCharCode(((h&15)<<12)|((a[f+1]&63)<<6)|(a[f+2]&63));f+=2}}}return e}function oaep_mgf1_str(c,a,e){var b="",d=0;while(b.length>24,(d&16711680)>>16,(d&65280)>>8,d&255]));d+=1}return b}function oaep_unpad(o,b,g,p){var e=KJUR.crypto.MessageDigest;var r=KJUR.crypto.Util;var c=null;if(!g){g="sha1"}if(typeof g==="string"){c=e.getCanonicalAlgName(g);p=e.getHashLength(c);g=function(d){return hextorstr(r.hashHex(rstrtohex(d),c))}}o=o.toByteArray();var h;for(h=0;h0&&a.length>0){this.n=parseBigInt(c,16);this.e=parseInt(a,16);this.d=parseBigInt(b,16)}else{throw"Invalid RSA private key"}}}function RSASetPrivateEx(g,d,e,c,b,a,h,f){this.isPrivate=true;this.isPublic=false;if(g==null){throw"RSASetPrivateEx N == null"}if(d==null){throw"RSASetPrivateEx E == null"}if(g.length==0){throw"RSASetPrivateEx N.length == 0"}if(d.length==0){throw"RSASetPrivateEx E.length == 0"}if(g!=null&&d!=null&&g.length>0&&d.length>0){this.n=parseBigInt(g,16);this.e=parseInt(d,16);this.d=parseBigInt(e,16);this.p=parseBigInt(c,16);this.q=parseBigInt(b,16);this.dmp1=parseBigInt(a,16);this.dmq1=parseBigInt(h,16);this.coeff=parseBigInt(f,16)}else{throw"Invalid RSA private key in RSASetPrivateEx"}}function RSAGenerate(b,i){var a=new SecureRandom();var f=b>>1;this.e=parseInt(i,16);var c=new BigInteger(i,16);for(;;){for(;;){this.p=new BigInteger(b-f,1,a);if(this.p.subtract(BigInteger.ONE).gcd(c).compareTo(BigInteger.ONE)==0&&this.p.isProbablePrime(10)){break}}for(;;){this.q=new BigInteger(f,1,a);if(this.q.subtract(BigInteger.ONE).gcd(c).compareTo(BigInteger.ONE)==0&&this.q.isProbablePrime(10)){break}}if(this.p.compareTo(this.q)<=0){var h=this.p;this.p=this.q;this.q=h}var g=this.p.subtract(BigInteger.ONE);var d=this.q.subtract(BigInteger.ONE);var e=g.multiply(d);if(e.gcd(c).compareTo(BigInteger.ONE)==0){this.n=this.p.multiply(this.q);this.d=c.modInverse(e);this.dmp1=this.d.mod(g);this.dmq1=this.d.mod(d);this.coeff=this.q.modInverse(this.p);break}}this.isPrivate=true}function RSADoPrivate(a){if(this.p==null||this.q==null){return a.modPow(this.d,this.n)}var c=a.mod(this.p).modPow(this.dmp1,this.p);var b=a.mod(this.q).modPow(this.dmq1,this.q);while(c.compareTo(b)<0){c=c.add(this.p)}return c.subtract(b).multiply(this.coeff).mod(this.p).multiply(this.q).add(b)}function RSADecrypt(b){var d=parseBigInt(b,16);var a=this.doPrivate(d);if(a==null){return null}return pkcs1unpad2(a,(this.n.bitLength()+7)>>3)}function RSADecryptOAEP(e,d,b){var f=parseBigInt(e,16);var a=this.doPrivate(f);if(a==null){return null}return oaep_unpad(a,(this.n.bitLength()+7)>>3,d,b)}RSAKey.prototype.doPrivate=RSADoPrivate;RSAKey.prototype.setPrivate=RSASetPrivate;RSAKey.prototype.setPrivateEx=RSASetPrivateEx;RSAKey.prototype.generate=RSAGenerate;RSAKey.prototype.decrypt=RSADecrypt;RSAKey.prototype.decryptOAEP=RSADecryptOAEP; +/*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/ + */ +function ECFieldElementFp(b,a){this.x=a;this.q=b}function feFpEquals(a){if(a==this){return true}return(this.q.equals(a.q)&&this.x.equals(a.x))}function feFpToBigInteger(){return this.x}function feFpNegate(){return new ECFieldElementFp(this.q,this.x.negate().mod(this.q))}function feFpAdd(a){return new ECFieldElementFp(this.q,this.x.add(a.toBigInteger()).mod(this.q))}function feFpSubtract(a){return new ECFieldElementFp(this.q,this.x.subtract(a.toBigInteger()).mod(this.q))}function feFpMultiply(a){return new ECFieldElementFp(this.q,this.x.multiply(a.toBigInteger()).mod(this.q))}function feFpSquare(){return new ECFieldElementFp(this.q,this.x.square().mod(this.q))}function feFpDivide(a){return new ECFieldElementFp(this.q,this.x.multiply(a.toBigInteger().modInverse(this.q)).mod(this.q))}ECFieldElementFp.prototype.equals=feFpEquals;ECFieldElementFp.prototype.toBigInteger=feFpToBigInteger;ECFieldElementFp.prototype.negate=feFpNegate;ECFieldElementFp.prototype.add=feFpAdd;ECFieldElementFp.prototype.subtract=feFpSubtract;ECFieldElementFp.prototype.multiply=feFpMultiply;ECFieldElementFp.prototype.square=feFpSquare;ECFieldElementFp.prototype.divide=feFpDivide;function ECPointFp(c,a,d,b){this.curve=c;this.x=a;this.y=d;if(b==null){this.z=BigInteger.ONE}else{this.z=b}this.zinv=null}function pointFpGetX(){if(this.zinv==null){this.zinv=this.z.modInverse(this.curve.q)}return this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q))}function pointFpGetY(){if(this.zinv==null){this.zinv=this.z.modInverse(this.curve.q)}return this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q))}function pointFpEquals(a){if(a==this){return true}if(this.isInfinity()){return a.isInfinity()}if(a.isInfinity()){return this.isInfinity()}var c,b;c=a.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(a.z)).mod(this.curve.q);if(!c.equals(BigInteger.ZERO)){return false}b=a.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(a.z)).mod(this.curve.q);return b.equals(BigInteger.ZERO)}function pointFpIsInfinity(){if((this.x==null)&&(this.y==null)){return true}return this.z.equals(BigInteger.ZERO)&&!this.y.toBigInteger().equals(BigInteger.ZERO)}function pointFpNegate(){return new ECPointFp(this.curve,this.x,this.y.negate(),this.z)}function pointFpAdd(l){if(this.isInfinity()){return l}if(l.isInfinity()){return this}var p=l.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(l.z)).mod(this.curve.q);var o=l.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(l.z)).mod(this.curve.q);if(BigInteger.ZERO.equals(o)){if(BigInteger.ZERO.equals(p)){return this.twice()}return this.curve.getInfinity()}var j=new BigInteger("3");var e=this.x.toBigInteger();var n=this.y.toBigInteger();var c=l.x.toBigInteger();var k=l.y.toBigInteger();var m=o.square();var i=m.multiply(o);var d=e.multiply(m);var g=p.square().multiply(this.z);var a=g.subtract(d.shiftLeft(1)).multiply(l.z).subtract(i).multiply(o).mod(this.curve.q);var h=d.multiply(j).multiply(p).subtract(n.multiply(i)).subtract(g.multiply(p)).multiply(l.z).add(p.multiply(i)).mod(this.curve.q);var f=i.multiply(this.z).multiply(l.z).mod(this.curve.q);return new ECPointFp(this.curve,this.curve.fromBigInteger(a),this.curve.fromBigInteger(h),f)}function pointFpTwice(){if(this.isInfinity()){return this}if(this.y.toBigInteger().signum()==0){return this.curve.getInfinity()}var g=new BigInteger("3");var c=this.x.toBigInteger();var h=this.y.toBigInteger();var e=h.multiply(this.z);var j=e.multiply(h).mod(this.curve.q);var i=this.curve.a.toBigInteger();var k=c.square().multiply(g);if(!BigInteger.ZERO.equals(i)){k=k.add(this.z.square().multiply(i))}k=k.mod(this.curve.q);var b=k.square().subtract(c.shiftLeft(3).multiply(j)).shiftLeft(1).multiply(e).mod(this.curve.q);var f=k.multiply(g).multiply(c).subtract(j.shiftLeft(1)).shiftLeft(2).multiply(j).subtract(k.square().multiply(k)).mod(this.curve.q);var d=e.square().multiply(e).shiftLeft(3).mod(this.curve.q);return new ECPointFp(this.curve,this.curve.fromBigInteger(b),this.curve.fromBigInteger(f),d)}function pointFpMultiply(b){if(this.isInfinity()){return this}if(b.signum()==0){return this.curve.getInfinity()}var g=b;var f=g.multiply(new BigInteger("3"));var l=this.negate();var d=this;var c;for(c=f.bitLength()-2;c>0;--c){d=d.twice();var a=f.testBit(c);var j=g.testBit(c);if(a!=j){d=d.add(a?this:l)}}return d}function pointFpMultiplyTwo(c,a,b){var d;if(c.bitLength()>b.bitLength()){d=c.bitLength()-1}else{d=b.bitLength()-1}var f=this.curve.getInfinity();var e=this.add(a);while(d>=0){f=f.twice();if(c.testBit(d)){if(b.testBit(d)){f=f.add(e)}else{f=f.add(this)}}else{if(b.testBit(d)){f=f.add(a)}}--d}return f}ECPointFp.prototype.getX=pointFpGetX;ECPointFp.prototype.getY=pointFpGetY;ECPointFp.prototype.equals=pointFpEquals;ECPointFp.prototype.isInfinity=pointFpIsInfinity;ECPointFp.prototype.negate=pointFpNegate;ECPointFp.prototype.add=pointFpAdd;ECPointFp.prototype.twice=pointFpTwice;ECPointFp.prototype.multiply=pointFpMultiply;ECPointFp.prototype.multiplyTwo=pointFpMultiplyTwo;function ECCurveFp(e,d,c){this.q=e;this.a=this.fromBigInteger(d);this.b=this.fromBigInteger(c);this.infinity=new ECPointFp(this,null,null)}function curveFpGetQ(){return this.q}function curveFpGetA(){return this.a}function curveFpGetB(){return this.b}function curveFpEquals(a){if(a==this){return true}return(this.q.equals(a.q)&&this.a.equals(a.a)&&this.b.equals(a.b))}function curveFpGetInfinity(){return this.infinity}function curveFpFromBigInteger(a){return new ECFieldElementFp(this.q,a)}function curveFpDecodePointHex(d){switch(parseInt(d.substr(0,2),16)){case 0:return this.infinity;case 2:case 3:return null;case 4:case 6:case 7:var a=(d.length-2)/2;var c=d.substr(2,a);var b=d.substr(a+2,a);return new ECPointFp(this,this.fromBigInteger(new BigInteger(c,16)),this.fromBigInteger(new BigInteger(b,16)));default:return null}}ECCurveFp.prototype.getQ=curveFpGetQ;ECCurveFp.prototype.getA=curveFpGetA;ECCurveFp.prototype.getB=curveFpGetB;ECCurveFp.prototype.equals=curveFpEquals;ECCurveFp.prototype.getInfinity=curveFpGetInfinity;ECCurveFp.prototype.fromBigInteger=curveFpFromBigInteger;ECCurveFp.prototype.decodePointHex=curveFpDecodePointHex; +/*! (c) Stefan Thomas | https://github.com/bitcoinjs/bitcoinjs-lib + */ +ECFieldElementFp.prototype.getByteLength=function(){return Math.floor((this.toBigInteger().bitLength()+7)/8)};ECPointFp.prototype.getEncoded=function(c){var d=function(h,f){var g=h.toByteArrayUnsigned();if(fg.length){g.unshift(0)}}return g};var a=this.getX().toBigInteger();var e=this.getY().toBigInteger();var b=d(a,32);if(c){if(e.isEven()){b.unshift(2)}else{b.unshift(3)}}else{b.unshift(4);b=b.concat(d(e,32))}return b};ECPointFp.decodeFrom=function(g,c){var f=c[0];var e=c.length-1;var d=c.slice(1,1+e/2);var b=c.slice(1+e/2,1+e);d.unshift(0);b.unshift(0);var a=new BigInteger(d);var h=new BigInteger(b);return new ECPointFp(g,g.fromBigInteger(a),g.fromBigInteger(h))};ECPointFp.decodeFromHex=function(g,c){var f=c.substr(0,2);var e=c.length-2;var d=c.substr(2,e/2);var b=c.substr(2+e/2,e/2);var a=new BigInteger(d,16);var h=new BigInteger(b,16);return new ECPointFp(g,g.fromBigInteger(a),g.fromBigInteger(h))};ECPointFp.prototype.add2D=function(c){if(this.isInfinity()){return c}if(c.isInfinity()){return this}if(this.x.equals(c.x)){if(this.y.equals(c.y)){return this.twice()}return this.curve.getInfinity()}var g=c.x.subtract(this.x);var e=c.y.subtract(this.y);var a=e.divide(g);var d=a.square().subtract(this.x).subtract(c.x);var f=a.multiply(this.x.subtract(d)).subtract(this.y);return new ECPointFp(this.curve,d,f)};ECPointFp.prototype.twice2D=function(){if(this.isInfinity()){return this}if(this.y.toBigInteger().signum()==0){return this.curve.getInfinity()}var b=this.curve.fromBigInteger(BigInteger.valueOf(2));var e=this.curve.fromBigInteger(BigInteger.valueOf(3));var a=this.x.square().multiply(e).add(this.curve.a).divide(this.y.multiply(b));var c=a.square().subtract(this.x.multiply(b));var d=a.multiply(this.x.subtract(c)).subtract(this.y);return new ECPointFp(this.curve,c,d)};ECPointFp.prototype.multiply2D=function(b){if(this.isInfinity()){return this}if(b.signum()==0){return this.curve.getInfinity()}var g=b;var f=g.multiply(new BigInteger("3"));var l=this.negate();var d=this;var c;for(c=f.bitLength()-2;c>0;--c){d=d.twice();var a=f.testBit(c);var j=g.testBit(c);if(a!=j){d=d.add2D(a?this:l)}}return d};ECPointFp.prototype.isOnCurve=function(){var d=this.getX().toBigInteger();var i=this.getY().toBigInteger();var f=this.curve.getA().toBigInteger();var c=this.curve.getB().toBigInteger();var h=this.curve.getQ();var e=i.multiply(i).mod(h);var g=d.multiply(d).multiply(d).add(f.multiply(d)).add(c).mod(h);return e.equals(g)};ECPointFp.prototype.toString=function(){return"("+this.getX().toBigInteger().toString()+","+this.getY().toBigInteger().toString()+")"};ECPointFp.prototype.validate=function(){var c=this.curve.getQ();if(this.isInfinity()){throw new Error("Point is at infinity.")}var a=this.getX().toBigInteger();var b=this.getY().toBigInteger();if(a.compareTo(BigInteger.ONE)<0||a.compareTo(c.subtract(BigInteger.ONE))>0){throw new Error("x coordinate out of bounds")}if(b.compareTo(BigInteger.ONE)<0||b.compareTo(c.subtract(BigInteger.ONE))>0){throw new Error("y coordinate out of bounds")}if(!this.isOnCurve()){throw new Error("Point is not on the curve.")}if(this.multiply(c).isInfinity()){throw new Error("Point is not a scalar multiple of G.")}return true}; +/*! Mike Samuel (c) 2009 | code.google.com/p/json-sans-eval + */ +var jsonParse=(function(){var e="(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)";var j='(?:[^\\0-\\x08\\x0a-\\x1f"\\\\]|\\\\(?:["/\\\\bfnrt]|u[0-9A-Fa-f]{4}))';var i='(?:"'+j+'*")';var d=new RegExp("(?:false|true|null|[\\{\\}\\[\\]]|"+e+"|"+i+")","g");var k=new RegExp("\\\\(?:([^u])|u(.{4}))","g");var g={'"':'"',"/":"/","\\":"\\",b:"\b",f:"\f",n:"\n",r:"\r",t:"\t"};function h(l,m,n){return m?g[m]:String.fromCharCode(parseInt(n,16))}var c=new String("");var a="\\";var f={"{":Object,"[":Array};var b=Object.hasOwnProperty;return function(u,q){var p=u.match(d);var x;var v=p[0];var l=false;if("{"===v){x={}}else{if("["===v){x=[]}else{x=[];l=true}}var t;var r=[x];for(var o=1-l,m=p.length;o=0;){delete D[n[A]]}}}return q.call(C,B,D)};x=s({"":x},"")}return x}})(); +if(typeof KJUR=="undefined"||!KJUR){KJUR={}}if(typeof KJUR.asn1=="undefined"||!KJUR.asn1){KJUR.asn1={}}KJUR.asn1.ASN1Util=new function(){this.integerToByteHex=function(a){var b=a.toString(16);if((b.length%2)==1){b="0"+b}return b};this.bigIntToMinTwosComplementsHex=function(j){var f=j.toString(16);if(f.substr(0,1)!="-"){if(f.length%2==1){f="0"+f}else{if(!f.match(/^[0-7]/)){f="00"+f}}}else{var a=f.substr(1);var e=a.length;if(e%2==1){e+=1}else{if(!f.match(/^[0-7]/)){e+=2}}var g="";for(var d=0;d15){throw"ASN.1 length too long to represent by 8x: n = "+i.toString(16)}var f=128+g;return f.toString(16)+h}};this.getEncodedHex=function(){if(this.hTLV==null||this.isModified){this.hV=this.getFreshValueHex();this.hL=this.getLengthHexFromValue();this.hTLV=this.hT+this.hL+this.hV;this.isModified=false}return this.hTLV};this.getValueHex=function(){this.getEncodedHex();return this.hV};this.getFreshValueHex=function(){return""}};KJUR.asn1.DERAbstractString=function(c){KJUR.asn1.DERAbstractString.superclass.constructor.call(this);var b=null;var a=null;this.getString=function(){return this.s};this.setString=function(d){this.hTLV=null;this.isModified=true;this.s=d;this.hV=utf8tohex(this.s).toLowerCase()};this.setStringHex=function(d){this.hTLV=null;this.isModified=true;this.s=null;this.hV=d};this.getFreshValueHex=function(){return this.hV};if(typeof c!="undefined"){if(typeof c=="string"){this.setString(c)}else{if(typeof c.str!="undefined"){this.setString(c.str)}else{if(typeof c.hex!="undefined"){this.setStringHex(c.hex)}}}}};YAHOO.lang.extend(KJUR.asn1.DERAbstractString,KJUR.asn1.ASN1Object);KJUR.asn1.DERAbstractTime=function(c){KJUR.asn1.DERAbstractTime.superclass.constructor.call(this);var b=null;var a=null;this.localDateToUTC=function(f){utc=f.getTime()+(f.getTimezoneOffset()*60000);var e=new Date(utc);return e};this.formatDate=function(m,o,e){var g=this.zeroPadding;var n=this.localDateToUTC(m);var p=String(n.getFullYear());if(o=="utc"){p=p.substr(2,2)}var l=g(String(n.getMonth()+1),2);var q=g(String(n.getDate()),2);var h=g(String(n.getHours()),2);var i=g(String(n.getMinutes()),2);var j=g(String(n.getSeconds()),2);var r=p+l+q+h+i+j;if(e===true){var f=n.getMilliseconds();if(f!=0){var k=g(String(f),3);k=k.replace(/[0]+$/,"");r=r+"."+k}}return r+"Z"};this.zeroPadding=function(e,d){if(e.length>=d){return e}return new Array(d-e.length+1).join("0")+e};this.getString=function(){return this.s};this.setString=function(d){this.hTLV=null;this.isModified=true;this.s=d;this.hV=stohex(d)};this.setByDateValue=function(h,j,e,d,f,g){var i=new Date(Date.UTC(h,j-1,e,d,f,g,0));this.setByDate(i)};this.getFreshValueHex=function(){return this.hV}};YAHOO.lang.extend(KJUR.asn1.DERAbstractTime,KJUR.asn1.ASN1Object);KJUR.asn1.DERAbstractStructured=function(b){KJUR.asn1.DERAbstractString.superclass.constructor.call(this);var a=null;this.setByASN1ObjectArray=function(c){this.hTLV=null;this.isModified=true;this.asn1Array=c};this.appendASN1Object=function(c){this.hTLV=null;this.isModified=true;this.asn1Array.push(c)};this.asn1Array=new Array();if(typeof b!="undefined"){if(typeof b.array!="undefined"){this.asn1Array=b.array}}};YAHOO.lang.extend(KJUR.asn1.DERAbstractStructured,KJUR.asn1.ASN1Object);KJUR.asn1.DERBoolean=function(){KJUR.asn1.DERBoolean.superclass.constructor.call(this);this.hT="01";this.hTLV="0101ff"};YAHOO.lang.extend(KJUR.asn1.DERBoolean,KJUR.asn1.ASN1Object);KJUR.asn1.DERInteger=function(a){KJUR.asn1.DERInteger.superclass.constructor.call(this);this.hT="02";this.setByBigInteger=function(b){this.hTLV=null;this.isModified=true;this.hV=KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex(b)};this.setByInteger=function(c){var b=new BigInteger(String(c),10);this.setByBigInteger(b)};this.setValueHex=function(b){this.hV=b};this.getFreshValueHex=function(){return this.hV};if(typeof a!="undefined"){if(typeof a.bigint!="undefined"){this.setByBigInteger(a.bigint)}else{if(typeof a["int"]!="undefined"){this.setByInteger(a["int"])}else{if(typeof a=="number"){this.setByInteger(a)}else{if(typeof a.hex!="undefined"){this.setValueHex(a.hex)}}}}}};YAHOO.lang.extend(KJUR.asn1.DERInteger,KJUR.asn1.ASN1Object);KJUR.asn1.DERBitString=function(b){if(b!==undefined&&typeof b.obj!=="undefined"){var a=KJUR.asn1.ASN1Util.newObject(b.obj);b.hex="00"+a.getEncodedHex()}KJUR.asn1.DERBitString.superclass.constructor.call(this);this.hT="03";this.setHexValueIncludingUnusedBits=function(c){this.hTLV=null;this.isModified=true;this.hV=c};this.setUnusedBitsAndHexValue=function(c,e){if(c<0||7=(l*2))){break}if(d>=200){break}g.push(b);c=b;d++}return g};ASN1HEX.getNthChildIdx=function(d,b,e){var c=ASN1HEX.getChildIdx(d,b);return c[e]};ASN1HEX.getIdxbyList=function(e,d,c,i){var g=ASN1HEX;var f,b;if(c.length==0){if(i!==undefined){if(e.substr(d,2)!==i){throw"checking tag doesn't match: "+e.substr(d,2)+"!="+i}}return d}f=c.shift();b=g.getChildIdx(e,d);return g.getIdxbyList(e,b[f],c,i)};ASN1HEX.getTLVbyList=function(d,c,b,f){var e=ASN1HEX;var a=e.getIdxbyList(d,c,b);if(a===undefined){throw"can't find nthList object"}if(f!==undefined){if(d.substr(a,2)!=f){throw"checking tag doesn't match: "+d.substr(a,2)+"!="+f}}return e.getTLV(d,a)};ASN1HEX.getVbyList=function(e,c,b,g,i){var f=ASN1HEX;var a,d;a=f.getIdxbyList(e,c,b,g);if(a===undefined){throw"can't find nthList object"}d=f.getV(e,a);if(i===true){d=d.substr(2)}return d};ASN1HEX.hextooidstr=function(e){var h=function(b,a){if(b.length>=a){return b}return new Array(a-b.length+1).join("0")+b};var l=[];var o=e.substr(0,2);var f=parseInt(o,16);l[0]=new String(Math.floor(f/40));l[1]=new String(f%40);var m=e.substr(2);var k=[];for(var g=0;g0){n=n+"."+j.join(".")}return n};ASN1HEX.dump=function(t,c,l,g){var p=ASN1HEX;var j=p.getV;var y=p.dump;var w=p.getChildIdx;var e=t;if(t instanceof KJUR.asn1.ASN1Object){e=t.getEncodedHex()}var q=function(A,i){if(A.length<=i*2){return A}else{var v=A.substr(0,i)+"..(total "+A.length/2+"bytes).."+A.substr(A.length-i,i);return v}};if(c===undefined){c={ommit_long_octet:32}}if(l===undefined){l=0}if(g===undefined){g=""}var x=c.ommit_long_octet;if(e.substr(l,2)=="01"){var h=j(e,l);if(h=="00"){return g+"BOOLEAN FALSE\n"}else{return g+"BOOLEAN TRUE\n"}}if(e.substr(l,2)=="02"){var h=j(e,l);return g+"INTEGER "+q(h,x)+"\n"}if(e.substr(l,2)=="03"){var h=j(e,l);return g+"BITSTRING "+q(h,x)+"\n"}if(e.substr(l,2)=="04"){var h=j(e,l);if(p.isASN1HEX(h)){var k=g+"OCTETSTRING, encapsulates\n";k=k+y(h,c,0,g+" ");return k}else{return g+"OCTETSTRING "+q(h,x)+"\n"}}if(e.substr(l,2)=="05"){return g+"NULL\n"}if(e.substr(l,2)=="06"){var m=j(e,l);var a=KJUR.asn1.ASN1Util.oidHexToInt(m);var o=KJUR.asn1.x509.OID.oid2name(a);var b=a.replace(/\./g," ");if(o!=""){return g+"ObjectIdentifier "+o+" ("+b+")\n"}else{return g+"ObjectIdentifier ("+b+")\n"}}if(e.substr(l,2)=="0c"){return g+"UTF8String '"+hextoutf8(j(e,l))+"'\n"}if(e.substr(l,2)=="13"){return g+"PrintableString '"+hextoutf8(j(e,l))+"'\n"}if(e.substr(l,2)=="14"){return g+"TeletexString '"+hextoutf8(j(e,l))+"'\n"}if(e.substr(l,2)=="16"){return g+"IA5String '"+hextoutf8(j(e,l))+"'\n"}if(e.substr(l,2)=="17"){return g+"UTCTime "+hextoutf8(j(e,l))+"\n"}if(e.substr(l,2)=="18"){return g+"GeneralizedTime "+hextoutf8(j(e,l))+"\n"}if(e.substr(l,2)=="30"){if(e.substr(l,4)=="3000"){return g+"SEQUENCE {}\n"}var k=g+"SEQUENCE\n";var d=w(e,l);var f=c;if((d.length==2||d.length==3)&&e.substr(d[0],2)=="06"&&e.substr(d[d.length-1],2)=="04"){var o=p.oidname(j(e,d[0]));var r=JSON.parse(JSON.stringify(c));r.x509ExtName=o;f=r}for(var u=0;u0){var m=new f({array:this.extensionsArray});var k=new c({explicit:true,tag:"a3",obj:m});this.asn1Array.push(k)}var n=new f({array:this.asn1Array});this.hTLV=n.getEncodedHex();this.isModified=false;return this.hTLV};this._initialize()};YAHOO.lang.extend(KJUR.asn1.x509.TBSCertificate,KJUR.asn1.ASN1Object);KJUR.asn1.x509.Extension=function(d){KJUR.asn1.x509.Extension.superclass.constructor.call(this);var f=null,a=KJUR,e=a.asn1,h=e.DERObjectIdentifier,i=e.DEROctetString,b=e.DERBitString,g=e.DERBoolean,c=e.DERSequence;this.getEncodedHex=function(){var m=new h({oid:this.oid});var l=new i({hex:this.getExtnValueHex()});var k=new Array();k.push(m);if(this.critical){k.push(new g())}k.push(l);var j=new c({array:k});return j.getEncodedHex()};this.critical=false;if(d!==undefined){if(d.critical!==undefined){this.critical=d.critical}}};YAHOO.lang.extend(KJUR.asn1.x509.Extension,KJUR.asn1.ASN1Object);KJUR.asn1.x509.Extension.appendByNameToArray=function(e,c,b){var g=e.toLowerCase(),f=KJUR.asn1.x509;if(g=="basicconstraints"){var d=new f.BasicConstraints(c);b.push(d)}else{if(g=="keyusage"){var d=new f.KeyUsage(c);b.push(d)}else{if(g=="crldistributionpoints"){var d=new f.CRLDistributionPoints(c);b.push(d)}else{if(g=="extkeyusage"){var d=new f.ExtKeyUsage(c);b.push(d)}else{if(g=="authoritykeyidentifier"){var d=new f.AuthorityKeyIdentifier(c);b.push(d)}else{if(g=="authorityinfoaccess"){var d=new f.AuthorityInfoAccess(c);b.push(d)}else{if(g=="subjectaltname"){var d=new f.SubjectAltName(c);b.push(d)}else{if(g=="issueraltname"){var d=new f.IssuerAltName(c);b.push(d)}else{throw"unsupported extension name: "+e}}}}}}}}};KJUR.asn1.x509.KeyUsage=function(f){KJUR.asn1.x509.KeyUsage.superclass.constructor.call(this,f);var a=X509.KEYUSAGE_NAME;this.getExtnValueHex=function(){return this.asn1ExtnValue.getEncodedHex()};this.oid="2.5.29.15";if(f!==undefined){if(f.bin!==undefined){this.asn1ExtnValue=new KJUR.asn1.DERBitString(f)}if(f.names!==undefined&&f.names.length!==undefined){var e=f.names;var d="000000000";for(var c=0;c-1){e.push(new KJUR.asn1.DERInteger({"int":this.pathLen}))}var d=new KJUR.asn1.DERSequence({array:e});this.asn1ExtnValue=d;return this.asn1ExtnValue.getEncodedHex()};this.oid="2.5.29.19";this.cA=false;this.pathLen=-1;if(c!==undefined){if(c.cA!==undefined){this.cA=c.cA}if(c.pathLen!==undefined){this.pathLen=c.pathLen}}};YAHOO.lang.extend(KJUR.asn1.x509.BasicConstraints,KJUR.asn1.x509.Extension);KJUR.asn1.x509.CRLDistributionPoints=function(d){KJUR.asn1.x509.CRLDistributionPoints.superclass.constructor.call(this,d);var b=KJUR,a=b.asn1,c=a.x509;this.getExtnValueHex=function(){return this.asn1ExtnValue.getEncodedHex()};this.setByDPArray=function(e){this.asn1ExtnValue=new a.DERSequence({array:e})};this.setByOneURI=function(h){var e=new c.GeneralNames([{uri:h}]);var g=new c.DistributionPointName(e);var f=new c.DistributionPoint({dpobj:g});this.setByDPArray([f])};this.oid="2.5.29.31";if(d!==undefined){if(d.array!==undefined){this.setByDPArray(d.array)}else{if(d.uri!==undefined){this.setByOneURI(d.uri)}}}};YAHOO.lang.extend(KJUR.asn1.x509.CRLDistributionPoints,KJUR.asn1.x509.Extension);KJUR.asn1.x509.ExtKeyUsage=function(c){KJUR.asn1.x509.ExtKeyUsage.superclass.constructor.call(this,c);var b=KJUR,a=b.asn1;this.setPurposeArray=function(d){this.asn1ExtnValue=new a.DERSequence();for(var e=0;e0){var h=new b({array:this.aRevokedCert});this.asn1Array.push(h)}var i=new b({array:this.asn1Array});this.hTLV=i.getEncodedHex();this.isModified=false;return this.hTLV};this._initialize=function(){this.asn1Version=null;this.asn1SignatureAlg=null;this.asn1Issuer=null;this.asn1ThisUpdate=null;this.asn1NextUpdate=null;this.aRevokedCert=new Array()};this._initialize()};YAHOO.lang.extend(KJUR.asn1.x509.TBSCertList,KJUR.asn1.ASN1Object);KJUR.asn1.x509.CRLEntry=function(e){KJUR.asn1.x509.CRLEntry.superclass.constructor.call(this);var d=null,c=null,b=KJUR,a=b.asn1;this.setCertSerial=function(f){this.sn=new a.DERInteger(f)};this.setRevocationDate=function(f){this.time=new a.x509.Time(f)};this.getEncodedHex=function(){var f=new a.DERSequence({array:[this.sn,this.time]});this.TLV=f.getEncodedHex();return this.TLV};if(e!==undefined){if(e.time!==undefined){this.setRevocationDate(e.time)}if(e.sn!==undefined){this.setCertSerial(e.sn)}}};YAHOO.lang.extend(KJUR.asn1.x509.CRLEntry,KJUR.asn1.ASN1Object);KJUR.asn1.x509.X500Name=function(f){KJUR.asn1.x509.X500Name.superclass.constructor.call(this);this.asn1Array=new Array();var d=KJUR,c=d.asn1,e=c.x509,b=pemtohex;this.setByString=function(g){var k=g.split("/");k.shift();var j=[];for(var l=0;l0;f++){var h=c.shift();if(e===true){var d=b.pop();var j=(d+","+h).replace(/\\,/g,",");b.push(j);e=false}else{b.push(h)}if(h.substr(-1,1)==="\\"){e=true}}b=b.map(function(a){return a.replace("/","\\/")});b.reverse();return"/"+b.join("/")};KJUR.asn1.x509.RDN=function(a){KJUR.asn1.x509.RDN.superclass.constructor.call(this);this.asn1Array=new Array();this.addByString=function(b){this.asn1Array.push(new KJUR.asn1.x509.AttributeTypeAndValue({str:b}))};this.addByMultiValuedString=function(d){var b=KJUR.asn1.x509.RDN.parseString(d);for(var c=0;c0;g++){var k=j.shift();if(h===true){var f=c.pop();var d=(f+"+"+k).replace(/\\\+/g,"+");c.push(d);h=false}else{c.push(k)}if(k.substr(-1,1)==="\\"){h=true}}var l=false;var b=[];for(var g=0;c.length>0;g++){var k=c.shift();if(l===true){var e=b.pop();if(k.match(/"$/)){var d=(e+"+"+k).replace(/^([^=]+)="(.*)"$/,"$1=$2");b.push(d);l=false}else{b.push(e+"+"+k)}}else{b.push(k)}if(k.match(/^[^=]+="/)){l=true}}return b};KJUR.asn1.x509.AttributeTypeAndValue=function(d){KJUR.asn1.x509.AttributeTypeAndValue.superclass.constructor.call(this);var f=null,e=null,a="utf8",c=KJUR,b=c.asn1;this.setByString=function(h){var g=h.match(/^([^=]+)=(.+)$/);if(g){this.setByAttrTypeAndValueStr(g[1],g[2])}else{throw"malformed attrTypeAndValueStr: "+h}};this.setByAttrTypeAndValueStr=function(i,h){this.typeObj=KJUR.asn1.x509.OID.atype2obj(i);var g=a;if(i=="C"){g="prn"}this.valueObj=this.getValueObj(g,h)};this.getValueObj=function(h,g){if(h=="utf8"){return new b.DERUTF8String({str:g})}if(h=="prn"){return new b.DERPrintableString({str:g})}if(h=="tel"){return new b.DERTeletexString({str:g})}if(h=="ia5"){return new b.DERIA5String({str:g})}throw"unsupported directory string type: type="+h+" value="+g};this.getEncodedHex=function(){var g=new b.DERSequence({array:[this.typeObj,this.valueObj]});this.TLV=g.getEncodedHex();return this.TLV};if(d!==undefined){if(d.str!==undefined){this.setByString(d.str)}}};YAHOO.lang.extend(KJUR.asn1.x509.AttributeTypeAndValue,KJUR.asn1.ASN1Object);KJUR.asn1.x509.SubjectPublicKeyInfo=function(f){KJUR.asn1.x509.SubjectPublicKeyInfo.superclass.constructor.call(this);var l=null,k=null,a=KJUR,j=a.asn1,i=j.DERInteger,b=j.DERBitString,m=j.DERObjectIdentifier,e=j.DERSequence,h=j.ASN1Util.newObject,d=j.x509,o=d.AlgorithmIdentifier,g=a.crypto,n=g.ECDSA,c=g.DSA;this.getASN1Object=function(){if(this.asn1AlgId==null||this.asn1SubjPKey==null){throw"algId and/or subjPubKey not set"}var p=new e({array:[this.asn1AlgId,this.asn1SubjPKey]});return p};this.getEncodedHex=function(){var p=this.getASN1Object();this.hTLV=p.getEncodedHex();return this.hTLV};this.setPubKey=function(q){try{if(q instanceof RSAKey){var u=h({seq:[{"int":{bigint:q.n}},{"int":{"int":q.e}}]});var s=u.getEncodedHex();this.asn1AlgId=new o({name:"rsaEncryption"});this.asn1SubjPKey=new b({hex:"00"+s})}}catch(p){}try{if(q instanceof KJUR.crypto.ECDSA){var r=new m({name:q.curveName});this.asn1AlgId=new o({name:"ecPublicKey",asn1params:r});this.asn1SubjPKey=new b({hex:"00"+q.pubKeyHex})}}catch(p){}try{if(q instanceof KJUR.crypto.DSA){var r=new h({seq:[{"int":{bigint:q.p}},{"int":{bigint:q.q}},{"int":{bigint:q.g}}]});this.asn1AlgId=new o({name:"dsa",asn1params:r});var t=new i({bigint:q.y});this.asn1SubjPKey=new b({hex:"00"+t.getEncodedHex()})}}catch(p){}};if(f!==undefined){this.setPubKey(f)}};YAHOO.lang.extend(KJUR.asn1.x509.SubjectPublicKeyInfo,KJUR.asn1.ASN1Object);KJUR.asn1.x509.Time=function(f){KJUR.asn1.x509.Time.superclass.constructor.call(this);var e=null,a=null,d=KJUR,c=d.asn1,b=c.DERUTCTime,g=c.DERGeneralizedTime;this.setTimeParams=function(h){this.timeParams=h};this.getEncodedHex=function(){var h=null;if(this.timeParams!=null){if(this.type=="utc"){h=new b(this.timeParams)}else{h=new g(this.timeParams)}}else{if(this.type=="utc"){h=new b()}else{h=new g()}}this.TLV=h.getEncodedHex();return this.TLV};this.type="utc";if(f!==undefined){if(f.type!==undefined){this.type=f.type}else{if(f.str!==undefined){if(f.str.match(/^[0-9]{12}Z$/)){this.type="utc"}if(f.str.match(/^[0-9]{14}Z$/)){this.type="gen"}}}this.timeParams=f}};YAHOO.lang.extend(KJUR.asn1.x509.Time,KJUR.asn1.ASN1Object);KJUR.asn1.x509.AlgorithmIdentifier=function(d){KJUR.asn1.x509.AlgorithmIdentifier.superclass.constructor.call(this);this.nameAlg=null;this.asn1Alg=null;this.asn1Params=null;this.paramEmpty=false;var b=KJUR,a=b.asn1;this.getEncodedHex=function(){if(this.nameAlg===null&&this.asn1Alg===null){throw"algorithm not specified"}if(this.nameAlg!==null&&this.asn1Alg===null){this.asn1Alg=a.x509.OID.name2obj(this.nameAlg)}var e=[this.asn1Alg];if(this.asn1Params!==null){e.push(this.asn1Params)}var f=new a.DERSequence({array:e});this.hTLV=f.getEncodedHex();return this.hTLV};if(d!==undefined){if(d.name!==undefined){this.nameAlg=d.name}if(d.asn1params!==undefined){this.asn1Params=d.asn1params}if(d.paramempty!==undefined){this.paramEmpty=d.paramempty}}if(this.asn1Params===null&&this.paramEmpty===false&&this.nameAlg!==null){var c=this.nameAlg.toLowerCase();if(c.substr(-7,7)!=="withdsa"&&c.substr(-9,9)!=="withecdsa"){this.asn1Params=new a.DERNull()}}};YAHOO.lang.extend(KJUR.asn1.x509.AlgorithmIdentifier,KJUR.asn1.ASN1Object);KJUR.asn1.x509.GeneralName=function(e){KJUR.asn1.x509.GeneralName.superclass.constructor.call(this);var m=null,i=null,k={rfc822:"81",dns:"82",dn:"a4",uri:"86",ip:"87"},b=KJUR,g=b.asn1,f=g.DERSequence,j=g.DEROctetString,d=g.DERIA5String,c=g.DERTaggedObject,l=g.ASN1Object,a=g.x509.X500Name,h=pemtohex;this.explicit=false;this.setByParam=function(p){var r=null;var u=null;if(p===undefined){return}if(p.rfc822!==undefined){this.type="rfc822";u=new d({str:p[this.type]})}if(p.dns!==undefined){this.type="dns";u=new d({str:p[this.type]})}if(p.uri!==undefined){this.type="uri";u=new d({str:p[this.type]})}if(p.dn!==undefined){this.type="dn";this.explicit=true;u=new a({str:p.dn})}if(p.ldapdn!==undefined){this.type="dn";this.explicit=true;u=new a({ldapstr:p.ldapdn})}if(p.certissuer!==undefined){this.type="dn";this.explicit=true;var o=p.certissuer;var w=null;if(o.match(/^[0-9A-Fa-f]+$/)){w==o}if(o.indexOf("-----BEGIN ")!=-1){w=h(o)}if(w==null){throw"certissuer param not cert"}var t=new X509();t.hex=w;var y=t.getIssuerHex();u=new l();u.hTLV=y}if(p.certsubj!==undefined){this.type="dn";this.explicit=true;var o=p.certsubj;var w=null;if(o.match(/^[0-9A-Fa-f]+$/)){w==o}if(o.indexOf("-----BEGIN ")!=-1){w=h(o)}if(w==null){throw"certsubj param not cert"}var t=new X509();t.hex=w;var y=t.getSubjectHex();u=new l();u.hTLV=y}if(p.ip!==undefined){this.type="ip";this.explicit=false;var q=p.ip;var s;var n="malformed IP address";if(q.match(/^[0-9.]+[.][0-9.]+$/)){s=intarystrtohex("["+q.split(".").join(",")+"]");if(s.length!==8){throw n}}else{if(q.match(/^[0-9A-Fa-f:]+:[0-9A-Fa-f:]+$/)){s=ipv6tohex(q)}else{if(q.match(/^([0-9A-Fa-f][0-9A-Fa-f]){1,}$/)){s=q}else{throw n}}}u=new j({hex:s})}if(this.type==null){throw"unsupported type in params="+p}this.asn1Obj=new c({explicit:this.explicit,tag:k[this.type],obj:u})};this.getEncodedHex=function(){return this.asn1Obj.getEncodedHex()};if(e!==undefined){this.setByParam(e)}};YAHOO.lang.extend(KJUR.asn1.x509.GeneralName,KJUR.asn1.ASN1Object);KJUR.asn1.x509.GeneralNames=function(d){KJUR.asn1.x509.GeneralNames.superclass.constructor.call(this);var a=null,c=KJUR,b=c.asn1;this.setByParamArray=function(g){for(var e=0;e0){r=new b({obj:this.dUnsignedAttrs,tag:"a1",explicit:false})}var q=[this.dCMSVersion,this.dSignerIdentifier,this.dDigestAlgorithm,o,this.dSigAlg,this.dSig,];if(r!=null){q.push(r)}var p=new h.DERSequence({array:q});this.hTLV=p.getEncodedHex();return this.hTLV}};YAHOO.lang.extend(KJUR.asn1.cms.SignerInfo,KJUR.asn1.ASN1Object);KJUR.asn1.cms.EncapsulatedContentInfo=function(g){var c=KJUR,b=c.asn1,e=b.DERTaggedObject,a=b.DERSequence,h=b.DERObjectIdentifier,d=b.DEROctetString,f=b.cms;f.EncapsulatedContentInfo.superclass.constructor.call(this);this.dEContentType=new h({name:"data"});this.dEContent=null;this.isDetached=false;this.eContentValueHex=null;this.setContentType=function(i){if(i.match(/^[0-2][.][0-9.]+$/)){this.dEContentType=new h({oid:i})}else{this.dEContentType=new h({name:i})}};this.setContentValue=function(i){if(i!==undefined){if(typeof i.hex=="string"){this.eContentValueHex=i.hex}else{if(typeof i.str=="string"){this.eContentValueHex=utf8tohex(i.str)}}}};this.setContentValueHex=function(i){this.eContentValueHex=i};this.setContentValueStr=function(i){this.eContentValueHex=utf8tohex(i)};this.getEncodedHex=function(){if(typeof this.eContentValueHex!="string"){throw"eContentValue not yet set"}var k=new d({hex:this.eContentValueHex});this.dEContent=new e({obj:k,tag:"a0",explicit:true});var i=[this.dEContentType];if(!this.isDetached){i.push(this.dEContent)}var j=new a({array:i});this.hTLV=j.getEncodedHex();return this.hTLV}};YAHOO.lang.extend(KJUR.asn1.cms.EncapsulatedContentInfo,KJUR.asn1.ASN1Object);KJUR.asn1.cms.ContentInfo=function(f){var c=KJUR,b=c.asn1,d=b.DERTaggedObject,a=b.DERSequence,e=b.x509;KJUR.asn1.cms.ContentInfo.superclass.constructor.call(this);this.dContentType=null;this.dContent=null;this.setContentType=function(g){if(typeof g=="string"){this.dContentType=e.OID.name2obj(g)}};this.getEncodedHex=function(){var h=new d({obj:this.dContent,tag:"a0",explicit:true});var g=new a({array:[this.dContentType,h]});this.hTLV=g.getEncodedHex();return this.hTLV};if(f!==undefined){if(f.type){this.setContentType(f.type)}if(f.obj&&f.obj instanceof b.ASN1Object){this.dContent=f.obj}}};YAHOO.lang.extend(KJUR.asn1.cms.ContentInfo,KJUR.asn1.ASN1Object);KJUR.asn1.cms.SignedData=function(e){var a=KJUR,h=a.asn1,j=h.ASN1Object,g=h.DERInteger,m=h.DERSet,f=h.DERSequence,b=h.DERTaggedObject,l=h.cms,i=l.EncapsulatedContentInfo,d=l.SignerInfo,n=l.ContentInfo,c=h.x509,k=c.AlgorithmIdentifier;KJUR.asn1.cms.SignedData.superclass.constructor.call(this);this.dCMSVersion=new g({"int":1});this.dDigestAlgs=null;this.digestAlgNameList=[];this.dEncapContentInfo=new i();this.dCerts=null;this.certificateList=[];this.crlList=[];this.signerInfoList=[new d()];this.addCertificatesByPEM=function(p){var q=pemtohex(p);var r=new j();r.hTLV=q;this.certificateList.push(r)};this.getEncodedHex=function(){if(typeof this.hTLV=="string"){return this.hTLV}if(this.dDigestAlgs==null){var u=[];for(var t=0;t0){var v=new m({array:this.certificateList});this.dCerts=new b({obj:v,tag:"a0",explicit:false})}}if(this.dCerts!=null){p.push(this.dCerts)}var r=new m({array:this.signerInfoList});p.push(r);var q=new f({array:p});this.hTLV=q.getEncodedHex();return this.hTLV};this.getContentInfo=function(){this.getEncodedHex();var o=new n({type:"signed-data",obj:this});return o};this.getContentInfoEncodedHex=function(){var o=this.getContentInfo();var p=o.getEncodedHex();return p};this.getPEM=function(){return hextopem(this.getContentInfoEncodedHex(),"CMS")}};YAHOO.lang.extend(KJUR.asn1.cms.SignedData,KJUR.asn1.ASN1Object);KJUR.asn1.cms.CMSUtil=new function(){};KJUR.asn1.cms.CMSUtil.newSignedData=function(d){var b=KJUR,j=b.asn1,q=j.cms,f=q.SignerInfo,n=q.SignedData,o=q.SigningTime,a=q.SigningCertificate,p=q.SigningCertificateV2,c=j.cades,e=c.SignaturePolicyIdentifier;var m=new n();m.dEncapContentInfo.setContentValue(d.content);if(typeof d.certs=="object"){for(var h=0;h0){var s=new f({array:this.extensionsArray});var r=new m({array:[s]});var q=new f({array:[new k({oid:"1.2.840.113549.1.9.14"}),r]});var p=new c({explicit:true,tag:"a0",obj:q});this.asn1Array.push(p)}else{var p=new c({explicit:false,tag:"a0",obj:new j()});this.asn1Array.push(p)}var t=new f({array:this.asn1Array});this.hTLV=t.getEncodedHex();this.isModified=false;return this.hTLV};this._initialize()};YAHOO.lang.extend(KJUR.asn1.csr.CertificationRequestInfo,KJUR.asn1.ASN1Object);KJUR.asn1.csr.CSRUtil=new function(){};KJUR.asn1.csr.CSRUtil.newCSRPEM=function(h){var c=KEYUTIL,b=KJUR.asn1.csr;if(h.subject===undefined){throw"parameter subject undefined"}if(h.sbjpubkey===undefined){throw"parameter sbjpubkey undefined"}if(h.sigalg===undefined){throw"parameter sigalg undefined"}if(h.sbjprvkey===undefined){throw"parameter sbjpubkey undefined"}var d=new b.CertificationRequestInfo();d.setSubjectByParam(h.subject);d.setSubjectPublicKeyByGetKey(h.sbjpubkey);if(h.ext!==undefined&&h.ext.length!==undefined){for(var e=0;ef.length){f=c[d]}}e=e.replace(f,"::");return e.slice(1,-1)}function hextoip(b){var d="malformed hex value";if(!b.match(/^([0-9A-Fa-f][0-9A-Fa-f]){1,}$/)){throw d}if(b.length==8){var c;try{c=parseInt(b.substr(0,2),16)+"."+parseInt(b.substr(2,2),16)+"."+parseInt(b.substr(4,2),16)+"."+parseInt(b.substr(6,2),16);return c}catch(a){throw d}}else{if(b.length==32){return hextoipv6(b)}else{return b}}}function iptohex(f){var j="malformed IP address";f=f.toLowerCase(f);if(f.match(/^[0-9.]+$/)){var b=f.split(".");if(b.length!==4){throw j}var g="";try{for(var e=0;e<4;e++){var h=parseInt(b[e]);g+=("0"+h.toString(16)).slice(-2)}return g}catch(c){throw j}}else{if(f.match(/^[0-9a-f:]+$/)&&f.indexOf(":")!==-1){return ipv6tohex(f)}else{throw j}}}function encodeURIComponentAll(a){var d=encodeURIComponent(a);var b="";for(var c=0;c"7"){return"00"+a}return a}function intarystrtohex(b){b=b.replace(/^\s*\[\s*/,"");b=b.replace(/\s*\]\s*$/,"");b=b.replace(/\s*/g,"");try{var c=b.split(/,/).map(function(g,e,h){var f=parseInt(g);if(f<0||255a.length){d=a.length}for(var b=0;bd){throw"key is too short for SigAlg: keylen="+j+","+a}var b="0001";var k="00"+c;var g="";var l=d-b.length-k.length;for(var f=0;f=0;--p){q=q.twice2D();q.z=BigInteger.ONE;if(o.testBit(p)){if(n.testBit(p)){q=q.add2D(t)}else{q=q.add2D(s)}}else{if(n.testBit(p)){q=q.add2D(r)}}}return q}this.getBigRandom=function(i){return new BigInteger(i.bitLength(),a).mod(i.subtract(BigInteger.ONE)).add(BigInteger.ONE)};this.setNamedCurve=function(i){this.ecparams=KJUR.crypto.ECParameterDB.getByName(i);this.prvKeyHex=null;this.pubKeyHex=null;this.curveName=i};this.setPrivateKeyHex=function(i){this.isPrivate=true;this.prvKeyHex=i};this.setPublicKeyHex=function(i){this.isPublic=true;this.pubKeyHex=i};this.getPublicKeyXYHex=function(){var k=this.pubKeyHex;if(k.substr(0,2)!=="04"){throw"this method supports uncompressed format(04) only"}var j=this.ecparams.keylen/4;if(k.length!==2+j*2){throw"malformed public key hex length"}var i={};i.x=k.substr(2,j);i.y=k.substr(2+j);return i};this.getShortNISTPCurveName=function(){var i=this.curveName;if(i==="secp256r1"||i==="NIST P-256"||i==="P-256"||i==="prime256v1"){return"P-256"}if(i==="secp384r1"||i==="NIST P-384"||i==="P-384"){return"P-384"}return null};this.generateKeyPairHex=function(){var k=this.ecparams.n;var n=this.getBigRandom(k);var l=this.ecparams.G.multiply(n);var q=l.getX().toBigInteger();var o=l.getY().toBigInteger();var i=this.ecparams.keylen/4;var m=("0000000000"+n.toString(16)).slice(-i);var r=("0000000000"+q.toString(16)).slice(-i);var p=("0000000000"+o.toString(16)).slice(-i);var j="04"+r+p;this.setPrivateKeyHex(m);this.setPublicKeyHex(j);return{ecprvhex:m,ecpubhex:j}};this.signWithMessageHash=function(i){return this.signHex(i,this.prvKeyHex)};this.signHex=function(o,j){var t=new BigInteger(j,16);var l=this.ecparams.n;var q=new BigInteger(o,16);do{var m=this.getBigRandom(l);var u=this.ecparams.G;var p=u.multiply(m);var i=p.getX().toBigInteger().mod(l)}while(i.compareTo(BigInteger.ZERO)<=0);var v=m.modInverse(l).multiply(q.add(t.multiply(i))).mod(l);return KJUR.crypto.ECDSA.biRSSigToASN1Sig(i,v)};this.sign=function(m,u){var q=u;var j=this.ecparams.n;var p=BigInteger.fromByteArrayUnsigned(m);do{var l=this.getBigRandom(j);var t=this.ecparams.G;var o=t.multiply(l);var i=o.getX().toBigInteger().mod(j)}while(i.compareTo(BigInteger.ZERO)<=0);var v=l.modInverse(j).multiply(p.add(q.multiply(i))).mod(j);return this.serializeSig(i,v)};this.verifyWithMessageHash=function(j,i){return this.verifyHex(j,i,this.pubKeyHex)};this.verifyHex=function(m,i,p){var l,j;var o=KJUR.crypto.ECDSA.parseSigHex(i);l=o.r;j=o.s;var k;k=ECPointFp.decodeFromHex(this.ecparams.curve,p);var n=new BigInteger(m,16);return this.verifyRaw(n,l,j,k)};this.verify=function(o,p,j){var l,i;if(Bitcoin.Util.isArray(p)){var n=this.parseSig(p);l=n.r;i=n.s}else{if("object"===typeof p&&p.r&&p.s){l=p.r;i=p.s}else{throw"Invalid value for signature"}}var k;if(j instanceof ECPointFp){k=j}else{if(Bitcoin.Util.isArray(j)){k=ECPointFp.decodeFrom(this.ecparams.curve,j)}else{throw"Invalid format for pubkey value, must be byte array or ECPointFp"}}var m=BigInteger.fromByteArrayUnsigned(o);return this.verifyRaw(m,l,i,k)};this.verifyRaw=function(o,i,w,m){var l=this.ecparams.n;var u=this.ecparams.G;if(i.compareTo(BigInteger.ONE)<0||i.compareTo(l)>=0){return false}if(w.compareTo(BigInteger.ONE)<0||w.compareTo(l)>=0){return false}var p=w.modInverse(l);var k=o.multiply(p).mod(l);var j=i.multiply(p).mod(l);var q=u.multiply(k).add(m.multiply(j));var t=q.getX().toBigInteger().mod(l);return t.equals(i)};this.serializeSig=function(k,j){var l=k.toByteArraySigned();var i=j.toByteArraySigned();var m=[];m.push(2);m.push(l.length);m=m.concat(l);m.push(2);m.push(i.length);m=m.concat(i);m.unshift(m.length);m.unshift(48);return m};this.parseSig=function(n){var m;if(n[0]!=48){throw new Error("Signature not a valid DERSequence")}m=2;if(n[m]!=2){throw new Error("First element in signature must be a DERInteger")}var l=n.slice(m+2,m+2+n[m+1]);m+=2+n[m+1];if(n[m]!=2){throw new Error("Second element in signature must be a DERInteger")}var i=n.slice(m+2,m+2+n[m+1]);m+=2+n[m+1];var k=BigInteger.fromByteArrayUnsigned(l);var j=BigInteger.fromByteArrayUnsigned(i);return{r:k,s:j}};this.parseSigCompact=function(m){if(m.length!==65){throw"Signature has the wrong length"}var j=m[0]-27;if(j<0||j>7){throw"Invalid signature type"}var o=this.ecparams.n;var l=BigInteger.fromByteArrayUnsigned(m.slice(1,33)).mod(o);var k=BigInteger.fromByteArrayUnsigned(m.slice(33,65)).mod(o);return{r:l,s:k,i:j}};this.readPKCS5PrvKeyHex=function(l){var n=ASN1HEX;var m=KJUR.crypto.ECDSA.getName;var p=n.getVbyList;if(n.isASN1HEX(l)===false){throw"not ASN.1 hex string"}var i,k,o;try{i=p(l,0,[2,0],"06");k=p(l,0,[1],"04");try{o=p(l,0,[3,0],"03").substr(2)}catch(j){}}catch(j){throw"malformed PKCS#1/5 plain ECC private key"}this.curveName=m(i);if(this.curveName===undefined){throw"unsupported curve name"}this.setNamedCurve(this.curveName);this.setPublicKeyHex(o);this.setPrivateKeyHex(k);this.isPublic=false};this.readPKCS8PrvKeyHex=function(l){var q=ASN1HEX;var i=KJUR.crypto.ECDSA.getName;var n=q.getVbyList;if(q.isASN1HEX(l)===false){throw"not ASN.1 hex string"}var j,p,m,k;try{j=n(l,0,[1,0],"06");p=n(l,0,[1,1],"06");m=n(l,0,[2,0,1],"04");try{k=n(l,0,[2,0,2,0],"03").substr(2)}catch(o){}}catch(o){throw"malformed PKCS#8 plain ECC private key"}this.curveName=i(p);if(this.curveName===undefined){throw"unsupported curve name"}this.setNamedCurve(this.curveName);this.setPublicKeyHex(k);this.setPrivateKeyHex(m);this.isPublic=false};this.readPKCS8PubKeyHex=function(l){var n=ASN1HEX;var m=KJUR.crypto.ECDSA.getName;var p=n.getVbyList;if(n.isASN1HEX(l)===false){throw"not ASN.1 hex string"}var k,i,o;try{k=p(l,0,[0,0],"06");i=p(l,0,[0,1],"06");o=p(l,0,[1],"03").substr(2)}catch(j){throw"malformed PKCS#8 ECC public key"}this.curveName=m(i);if(this.curveName===null){throw"unsupported curve name"}this.setNamedCurve(this.curveName);this.setPublicKeyHex(o)};this.readCertPubKeyHex=function(k,p){if(p!==5){p=6}var m=ASN1HEX;var l=KJUR.crypto.ECDSA.getName;var o=m.getVbyList;if(m.isASN1HEX(k)===false){throw"not ASN.1 hex string"}var i,n;try{i=o(k,0,[0,p,0,1],"06");n=o(k,0,[0,p,1],"03").substr(2)}catch(j){throw"malformed X.509 certificate ECC public key"}this.curveName=l(i);if(this.curveName===null){throw"unsupported curve name"}this.setNamedCurve(this.curveName);this.setPublicKeyHex(n)};if(h!==undefined){if(h.curve!==undefined){this.curveName=h.curve}}if(this.curveName===undefined){this.curveName=e}this.setNamedCurve(this.curveName);if(h!==undefined){if(h.prv!==undefined){this.setPrivateKeyHex(h.prv)}if(h.pub!==undefined){this.setPublicKeyHex(h.pub)}}};KJUR.crypto.ECDSA.parseSigHex=function(a){var b=KJUR.crypto.ECDSA.parseSigHexInHexRS(a);var d=new BigInteger(b.r,16);var c=new BigInteger(b.s,16);return{r:d,s:c}};KJUR.crypto.ECDSA.parseSigHexInHexRS=function(f){var j=ASN1HEX;var i=j.getChildIdx;var g=j.getV;if(f.substr(0,2)!="30"){throw"signature is not a ASN.1 sequence"}var h=i(f,0);if(h.length!=2){throw"number of signature ASN.1 sequence elements seem wrong"}var e=h[0];var d=h[1];if(f.substr(e,2)!="02"){throw"1st item of sequene of signature is not ASN.1 integer"}if(f.substr(d,2)!="02"){throw"2nd item of sequene of signature is not ASN.1 integer"}var c=g(f,e);var b=g(f,d);return{r:c,s:b}};KJUR.crypto.ECDSA.asn1SigToConcatSig=function(c){var d=KJUR.crypto.ECDSA.parseSigHexInHexRS(c);var b=d.r;var a=d.s;if(b.substr(0,2)=="00"&&(b.length%32)==2){b=b.substr(2)}if(a.substr(0,2)=="00"&&(a.length%32)==2){a=a.substr(2)}if((b.length%32)==30){b="00"+b}if((a.length%32)==30){a="00"+a}if(b.length%32!=0){throw"unknown ECDSA sig r length error"}if(a.length%32!=0){throw"unknown ECDSA sig s length error"}return b+a};KJUR.crypto.ECDSA.concatSigToASN1Sig=function(a){if((((a.length/2)*8)%(16*8))!=0){throw"unknown ECDSA concatinated r-s sig length error"}var c=a.substr(0,a.length/2);var b=a.substr(a.length/2);return KJUR.crypto.ECDSA.hexRSSigToASN1Sig(c,b)};KJUR.crypto.ECDSA.hexRSSigToASN1Sig=function(b,a){var d=new BigInteger(b,16);var c=new BigInteger(a,16);return KJUR.crypto.ECDSA.biRSSigToASN1Sig(d,c)};KJUR.crypto.ECDSA.biRSSigToASN1Sig=function(f,d){var c=KJUR.asn1;var b=new c.DERInteger({bigint:f});var a=new c.DERInteger({bigint:d});var e=new c.DERSequence({array:[b,a]});return e.getEncodedHex()};KJUR.crypto.ECDSA.getName=function(a){if(a==="2a8648ce3d030107"){return"secp256r1"}if(a==="2b8104000a"){return"secp256k1"}if(a==="2b81040022"){return"secp384r1"}if("|secp256r1|NIST P-256|P-256|prime256v1|".indexOf(a)!==-1){return"secp256r1"}if("|secp256k1|".indexOf(a)!==-1){return"secp256k1"}if("|secp384r1|NIST P-384|P-384|".indexOf(a)!==-1){return"secp384r1"}return null}; +if(typeof KJUR=="undefined"||!KJUR){KJUR={}}if(typeof KJUR.crypto=="undefined"||!KJUR.crypto){KJUR.crypto={}}KJUR.crypto.ECParameterDB=new function(){var b={};var c={};function a(d){return new BigInteger(d,16)}this.getByName=function(e){var d=e;if(typeof c[d]!="undefined"){d=c[e]}if(typeof b[d]!="undefined"){return b[d]}throw"unregistered EC curve name: "+d};this.regist=function(A,l,o,g,m,e,j,f,k,u,d,x){b[A]={};var s=a(o);var z=a(g);var y=a(m);var t=a(e);var w=a(j);var r=new ECCurveFp(s,z,y);var q=r.decodePointHex("04"+f+k);b[A]["name"]=A;b[A]["keylen"]=l;b[A]["curve"]=r;b[A]["G"]=q;b[A]["n"]=t;b[A]["h"]=w;b[A]["oid"]=d;b[A]["info"]=x;for(var v=0;v1){g=new BigInteger(i,16)}else{g=null}h=new BigInteger(j,16);this.setPrivate(c,a,e,g,h)};this.setPublic=function(c,b,a,d){this.isPublic=true;this.p=c;this.q=b;this.g=a;this.y=d;this.x=null};this.setPublicHex=function(f,e,d,g){var b,a,h,c;b=new BigInteger(f,16);a=new BigInteger(e,16);h=new BigInteger(d,16);c=new BigInteger(g,16);this.setPublic(b,a,h,c)};this.signWithMessageHash=function(d){var c=this.p;var b=this.q;var f=this.g;var i=this.y;var j=this.x;var e=KJUR.crypto.Util.getRandomBigIntegerMinToMax(BigInteger.ONE.add(BigInteger.ONE),b.subtract(BigInteger.ONE));var l=d.substr(0,b.bitLength()/4);var h=new BigInteger(l,16);var a=(f.modPow(e,c)).mod(b);var n=(e.modInverse(b).multiply(h.add(j.multiply(a)))).mod(b);var m=KJUR.asn1.ASN1Util.jsonToASN1HEX({seq:[{"int":{bigint:a}},{"int":{bigint:n}}]});return m};this.verifyWithMessageHash=function(h,f){var d=this.p;var b=this.q;var j=this.g;var l=this.y;var i=this.parseASN1Signature(f);var a=i[0];var t=i[1];var o=h.substr(0,b.bitLength()/4);var k=new BigInteger(o,16);if(BigInteger.ZERO.compareTo(a)>0||a.compareTo(b)>0){throw"invalid DSA signature"}if(BigInteger.ZERO.compareTo(t)>=0||t.compareTo(b)>0){throw"invalid DSA signature"}var m=t.modInverse(b);var e=k.multiply(m).mod(b);var c=a.multiply(m).mod(b);var n=j.modPow(e,d).multiply(l.modPow(c,d)).mod(d).mod(b);return n.compareTo(a)==0};this.parseASN1Signature=function(a){try{var d=new BigInteger(ASN1HEX.getVbyList(a,0,[0],"02"),16);var c=new BigInteger(ASN1HEX.getVbyList(a,0,[1],"02"),16);return[d,c]}catch(b){throw"malformed ASN.1 DSA signature"}};this.readPKCS5PrvKeyHex=function(c){var b,a,f,g,i;var j=ASN1HEX;var d=j.getVbyList;if(j.isASN1HEX(c)===false){throw"not ASN.1 hex string"}try{b=d(c,0,[1],"02");a=d(c,0,[2],"02");f=d(c,0,[3],"02");g=d(c,0,[4],"02");i=d(c,0,[5],"02")}catch(e){console.log("EXCEPTION:"+e);throw"malformed PKCS#1/5 plain DSA private key"}this.setPrivateHex(b,a,f,g,i)};this.readPKCS8PrvKeyHex=function(d){var f,c,b,g;var e=ASN1HEX;var i=e.getVbyList;if(e.isASN1HEX(d)===false){throw"not ASN.1 hex string"}try{f=i(d,0,[1,1,0],"02");c=i(d,0,[1,1,1],"02");b=i(d,0,[1,1,2],"02");g=i(d,0,[2,0],"02")}catch(a){console.log("EXCEPTION:"+a);throw"malformed PKCS#8 plain DSA private key"}this.setPrivateHex(f,c,b,null,g)};this.readPKCS8PubKeyHex=function(d){var f,c,b,g;var e=ASN1HEX;var i=e.getVbyList;if(e.isASN1HEX(d)===false){throw"not ASN.1 hex string"}try{f=i(d,0,[0,1,0],"02");c=i(d,0,[0,1,1],"02");b=i(d,0,[0,1,2],"02");g=i(d,0,[1,0],"02")}catch(a){console.log("EXCEPTION:"+a);throw"malformed PKCS#8 DSA public key"}this.setPublicHex(f,c,b,g)};this.readCertPubKeyHex=function(c,f){if(f!==5){f=6}var b,a,g,i;var j=ASN1HEX;var d=j.getVbyList;if(j.isASN1HEX(c)===false){throw"not ASN.1 hex string"}try{b=d(c,0,[0,f,0,1,0],"02");a=d(c,0,[0,f,0,1,1],"02");g=d(c,0,[0,f,0,1,2],"02");i=d(c,0,[0,f,1,0],"02")}catch(e){console.log("EXCEPTION:"+e);throw"malformed X.509 certificate DSA public key"}this.setPublicHex(b,a,g,i)}}; +var KEYUTIL=function(){var d=function(p,r,q){return k(CryptoJS.AES,p,r,q)};var e=function(p,r,q){return k(CryptoJS.TripleDES,p,r,q)};var a=function(p,r,q){return k(CryptoJS.DES,p,r,q)};var k=function(s,x,u,q){var r=CryptoJS.enc.Hex.parse(x);var w=CryptoJS.enc.Hex.parse(u);var p=CryptoJS.enc.Hex.parse(q);var t={};t.key=w;t.iv=p;t.ciphertext=r;var v=s.decrypt(t,w,{iv:p});return CryptoJS.enc.Hex.stringify(v)};var l=function(p,r,q){return g(CryptoJS.AES,p,r,q)};var o=function(p,r,q){return g(CryptoJS.TripleDES,p,r,q)};var f=function(p,r,q){return g(CryptoJS.DES,p,r,q)};var g=function(t,y,v,q){var s=CryptoJS.enc.Hex.parse(y);var x=CryptoJS.enc.Hex.parse(v);var p=CryptoJS.enc.Hex.parse(q);var w=t.encrypt(s,x,{iv:p});var r=CryptoJS.enc.Hex.parse(w.toString());var u=CryptoJS.enc.Base64.stringify(r);return u};var i={"AES-256-CBC":{proc:d,eproc:l,keylen:32,ivlen:16},"AES-192-CBC":{proc:d,eproc:l,keylen:24,ivlen:16},"AES-128-CBC":{proc:d,eproc:l,keylen:16,ivlen:16},"DES-EDE3-CBC":{proc:e,eproc:o,keylen:24,ivlen:8},"DES-CBC":{proc:a,eproc:f,keylen:8,ivlen:8}};var c=function(p){return i[p]["proc"]};var m=function(p){var r=CryptoJS.lib.WordArray.random(p);var q=CryptoJS.enc.Hex.stringify(r);return q};var n=function(v){var w={};var q=v.match(new RegExp("DEK-Info: ([^,]+),([0-9A-Fa-f]+)","m"));if(q){w.cipher=q[1];w.ivsalt=q[2]}var p=v.match(new RegExp("-----BEGIN ([A-Z]+) PRIVATE KEY-----"));if(p){w.type=p[1]}var u=-1;var x=0;if(v.indexOf("\r\n\r\n")!=-1){u=v.indexOf("\r\n\r\n");x=2}if(v.indexOf("\n\n")!=-1){u=v.indexOf("\n\n");x=1}var t=v.indexOf("-----END");if(u!=-1&&t!=-1){var r=v.substring(u+x*2,t-x);r=r.replace(/\s+/g,"");w.data=r}return w};var j=function(q,y,p){var v=p.substring(0,16);var t=CryptoJS.enc.Hex.parse(v);var r=CryptoJS.enc.Utf8.parse(y);var u=i[q]["keylen"]+i[q]["ivlen"];var x="";var w=null;for(;;){var s=CryptoJS.algo.MD5.create();if(w!=null){s.update(w)}s.update(r);s.update(t);w=s.finalize();x=x+CryptoJS.enc.Hex.stringify(w);if(x.length>=u*2){break}}var z={};z.keyhex=x.substr(0,i[q]["keylen"]*2);z.ivhex=x.substr(i[q]["keylen"]*2,i[q]["ivlen"]*2);return z};var b=function(p,v,r,w){var s=CryptoJS.enc.Base64.parse(p);var q=CryptoJS.enc.Hex.stringify(s);var u=i[v]["proc"];var t=u(q,r,w);return t};var h=function(p,s,q,u){var r=i[s]["eproc"];var t=r(p,q,u);return t};return{version:"1.0.0",parsePKCS5PEM:function(p){return n(p)},getKeyAndUnusedIvByPasscodeAndIvsalt:function(q,p,r){return j(q,p,r)},decryptKeyB64:function(p,r,q,s){return b(p,r,q,s)},getDecryptedKeyHex:function(y,x){var q=n(y);var t=q.type;var r=q.cipher;var p=q.ivsalt;var s=q.data;var w=j(r,x,p);var v=w.keyhex;var u=b(s,r,v,p);return u},getEncryptedPKCS5PEMFromPrvKeyHex:function(x,s,A,t,r){var p="";if(typeof t=="undefined"||t==null){t="AES-256-CBC"}if(typeof i[t]=="undefined"){throw"KEYUTIL unsupported algorithm: "+t}if(typeof r=="undefined"||r==null){var v=i[t]["ivlen"];var u=m(v);r=u.toUpperCase()}var z=j(t,A,r);var y=z.keyhex;var w=h(s,t,y,r);var q=w.replace(/(.{64})/g,"$1\r\n");var p="-----BEGIN "+x+" PRIVATE KEY-----\r\n";p+="Proc-Type: 4,ENCRYPTED\r\n";p+="DEK-Info: "+t+","+r+"\r\n";p+="\r\n";p+=q;p+="\r\n-----END "+x+" PRIVATE KEY-----\r\n";return p},parseHexOfEncryptedPKCS8:function(y){var B=ASN1HEX;var z=B.getChildIdx;var w=B.getV;var t={};var r=z(y,0);if(r.length!=2){throw"malformed format: SEQUENCE(0).items != 2: "+r.length}t.ciphertext=w(y,r[1]);var A=z(y,r[0]);if(A.length!=2){throw"malformed format: SEQUENCE(0.0).items != 2: "+A.length}if(w(y,A[0])!="2a864886f70d01050d"){throw"this only supports pkcs5PBES2"}var p=z(y,A[1]);if(A.length!=2){throw"malformed format: SEQUENCE(0.0.1).items != 2: "+p.length}var q=z(y,p[1]);if(q.length!=2){throw"malformed format: SEQUENCE(0.0.1.1).items != 2: "+q.length}if(w(y,q[0])!="2a864886f70d0307"){throw"this only supports TripleDES"}t.encryptionSchemeAlg="TripleDES";t.encryptionSchemeIV=w(y,q[1]);var s=z(y,p[0]);if(s.length!=2){throw"malformed format: SEQUENCE(0.0.1.0).items != 2: "+s.length}if(w(y,s[0])!="2a864886f70d01050c"){throw"this only supports pkcs5PBKDF2"}var x=z(y,s[1]);if(x.length<2){throw"malformed format: SEQUENCE(0.0.1.0.1).items < 2: "+x.length}t.pbkdf2Salt=w(y,x[0]);var u=w(y,x[1]);try{t.pbkdf2Iter=parseInt(u,16)}catch(v){throw"malformed format pbkdf2Iter: "+u}return t},getPBKDF2KeyHexFromParam:function(u,p){var t=CryptoJS.enc.Hex.parse(u.pbkdf2Salt);var q=u.pbkdf2Iter;var s=CryptoJS.PBKDF2(p,t,{keySize:192/32,iterations:q});var r=CryptoJS.enc.Hex.stringify(s);return r},_getPlainPKCS8HexFromEncryptedPKCS8PEM:function(x,y){var r=pemtohex(x,"ENCRYPTED PRIVATE KEY");var p=this.parseHexOfEncryptedPKCS8(r);var u=KEYUTIL.getPBKDF2KeyHexFromParam(p,y);var v={};v.ciphertext=CryptoJS.enc.Hex.parse(p.ciphertext);var t=CryptoJS.enc.Hex.parse(u);var s=CryptoJS.enc.Hex.parse(p.encryptionSchemeIV);var w=CryptoJS.TripleDES.decrypt(v,t,{iv:s});var q=CryptoJS.enc.Hex.stringify(w);return q},getKeyFromEncryptedPKCS8PEM:function(s,q){var p=this._getPlainPKCS8HexFromEncryptedPKCS8PEM(s,q);var r=this.getKeyFromPlainPrivatePKCS8Hex(p);return r},parsePlainPrivatePKCS8Hex:function(s){var v=ASN1HEX;var u=v.getChildIdx;var t=v.getV;var q={};q.algparam=null;if(s.substr(0,2)!="30"){throw"malformed plain PKCS8 private key(code:001)"}var r=u(s,0);if(r.length!=3){throw"malformed plain PKCS8 private key(code:002)"}if(s.substr(r[1],2)!="30"){throw"malformed PKCS8 private key(code:003)"}var p=u(s,r[1]);if(p.length!=2){throw"malformed PKCS8 private key(code:004)"}if(s.substr(p[0],2)!="06"){throw"malformed PKCS8 private key(code:005)"}q.algoid=t(s,p[0]);if(s.substr(p[1],2)=="06"){q.algparam=t(s,p[1])}if(s.substr(r[2],2)!="04"){throw"malformed PKCS8 private key(code:006)"}q.keyidx=v.getVidx(s,r[2]);return q},getKeyFromPlainPrivatePKCS8PEM:function(q){var p=pemtohex(q,"PRIVATE KEY");var r=this.getKeyFromPlainPrivatePKCS8Hex(p);return r},getKeyFromPlainPrivatePKCS8Hex:function(p){var q=this.parsePlainPrivatePKCS8Hex(p);var r;if(q.algoid=="2a864886f70d010101"){r=new RSAKey()}else{if(q.algoid=="2a8648ce380401"){r=new KJUR.crypto.DSA()}else{if(q.algoid=="2a8648ce3d0201"){r=new KJUR.crypto.ECDSA()}else{throw"unsupported private key algorithm"}}}r.readPKCS8PrvKeyHex(p);return r},_getKeyFromPublicPKCS8Hex:function(q){var p;var r=ASN1HEX.getVbyList(q,0,[0,0],"06");if(r==="2a864886f70d010101"){p=new RSAKey()}else{if(r==="2a8648ce380401"){p=new KJUR.crypto.DSA()}else{if(r==="2a8648ce3d0201"){p=new KJUR.crypto.ECDSA()}else{throw"unsupported PKCS#8 public key hex"}}}p.readPKCS8PubKeyHex(q);return p},parsePublicRawRSAKeyHex:function(r){var u=ASN1HEX;var t=u.getChildIdx;var s=u.getV;var p={};if(r.substr(0,2)!="30"){throw"malformed RSA key(code:001)"}var q=t(r,0);if(q.length!=2){throw"malformed RSA key(code:002)"}if(r.substr(q[0],2)!="02"){throw"malformed RSA key(code:003)"}p.n=s(r,q[0]);if(r.substr(q[1],2)!="02"){throw"malformed RSA key(code:004)"}p.e=s(r,q[1]);return p},parsePublicPKCS8Hex:function(t){var v=ASN1HEX;var u=v.getChildIdx;var s=v.getV;var q={};q.algparam=null;var r=u(t,0);if(r.length!=2){throw"outer DERSequence shall have 2 elements: "+r.length}var w=r[0];if(t.substr(w,2)!="30"){throw"malformed PKCS8 public key(code:001)"}var p=u(t,w);if(p.length!=2){throw"malformed PKCS8 public key(code:002)"}if(t.substr(p[0],2)!="06"){throw"malformed PKCS8 public key(code:003)"}q.algoid=s(t,p[0]);if(t.substr(p[1],2)=="06"){q.algparam=s(t,p[1])}else{if(t.substr(p[1],2)=="30"){q.algparam={};q.algparam.p=v.getVbyList(t,p[1],[0],"02");q.algparam.q=v.getVbyList(t,p[1],[1],"02");q.algparam.g=v.getVbyList(t,p[1],[2],"02")}}if(t.substr(r[1],2)!="03"){throw"malformed PKCS8 public key(code:004)"}q.key=s(t,r[1]).substr(2);return q},}}();KEYUTIL.getKey=function(l,k,n){var G=ASN1HEX,L=G.getChildIdx,v=G.getV,d=G.getVbyList,c=KJUR.crypto,i=c.ECDSA,C=c.DSA,w=RSAKey,M=pemtohex,F=KEYUTIL;if(typeof w!="undefined"&&l instanceof w){return l}if(typeof i!="undefined"&&l instanceof i){return l}if(typeof C!="undefined"&&l instanceof C){return l}if(l.curve!==undefined&&l.xy!==undefined&&l.d===undefined){return new i({pub:l.xy,curve:l.curve})}if(l.curve!==undefined&&l.d!==undefined){return new i({prv:l.d,curve:l.curve})}if(l.kty===undefined&&l.n!==undefined&&l.e!==undefined&&l.d===undefined){var P=new w();P.setPublic(l.n,l.e);return P}if(l.kty===undefined&&l.n!==undefined&&l.e!==undefined&&l.d!==undefined&&l.p!==undefined&&l.q!==undefined&&l.dp!==undefined&&l.dq!==undefined&&l.co!==undefined&&l.qi===undefined){var P=new w();P.setPrivateEx(l.n,l.e,l.d,l.p,l.q,l.dp,l.dq,l.co);return P}if(l.kty===undefined&&l.n!==undefined&&l.e!==undefined&&l.d!==undefined&&l.p===undefined){var P=new w();P.setPrivate(l.n,l.e,l.d);return P}if(l.p!==undefined&&l.q!==undefined&&l.g!==undefined&&l.y!==undefined&&l.x===undefined){var P=new C();P.setPublic(l.p,l.q,l.g,l.y);return P}if(l.p!==undefined&&l.q!==undefined&&l.g!==undefined&&l.y!==undefined&&l.x!==undefined){var P=new C();P.setPrivate(l.p,l.q,l.g,l.y,l.x);return P}if(l.kty==="RSA"&&l.n!==undefined&&l.e!==undefined&&l.d===undefined){var P=new w();P.setPublic(b64utohex(l.n),b64utohex(l.e));return P}if(l.kty==="RSA"&&l.n!==undefined&&l.e!==undefined&&l.d!==undefined&&l.p!==undefined&&l.q!==undefined&&l.dp!==undefined&&l.dq!==undefined&&l.qi!==undefined){var P=new w();P.setPrivateEx(b64utohex(l.n),b64utohex(l.e),b64utohex(l.d),b64utohex(l.p),b64utohex(l.q),b64utohex(l.dp),b64utohex(l.dq),b64utohex(l.qi));return P}if(l.kty==="RSA"&&l.n!==undefined&&l.e!==undefined&&l.d!==undefined){var P=new w();P.setPrivate(b64utohex(l.n),b64utohex(l.e),b64utohex(l.d));return P}if(l.kty==="EC"&&l.crv!==undefined&&l.x!==undefined&&l.y!==undefined&&l.d===undefined){var j=new i({curve:l.crv});var t=j.ecparams.keylen/4;var B=("0000000000"+b64utohex(l.x)).slice(-t);var z=("0000000000"+b64utohex(l.y)).slice(-t);var u="04"+B+z;j.setPublicKeyHex(u);return j}if(l.kty==="EC"&&l.crv!==undefined&&l.x!==undefined&&l.y!==undefined&&l.d!==undefined){var j=new i({curve:l.crv});var t=j.ecparams.keylen/4;var B=("0000000000"+b64utohex(l.x)).slice(-t);var z=("0000000000"+b64utohex(l.y)).slice(-t);var u="04"+B+z;var b=("0000000000"+b64utohex(l.d)).slice(-t);j.setPublicKeyHex(u);j.setPrivateKeyHex(b);return j}if(n==="pkcs5prv"){var J=l,G=ASN1HEX,N,P;N=L(J,0);if(N.length===9){P=new w();P.readPKCS5PrvKeyHex(J)}else{if(N.length===6){P=new C();P.readPKCS5PrvKeyHex(J)}else{if(N.length>2&&J.substr(N[1],2)==="04"){P=new i();P.readPKCS5PrvKeyHex(J)}else{throw"unsupported PKCS#1/5 hexadecimal key"}}}return P}if(n==="pkcs8prv"){var P=F.getKeyFromPlainPrivatePKCS8Hex(l);return P}if(n==="pkcs8pub"){return F._getKeyFromPublicPKCS8Hex(l)}if(n==="x509pub"){return X509.getPublicKeyFromCertHex(l)}if(l.indexOf("-END CERTIFICATE-",0)!=-1||l.indexOf("-END X509 CERTIFICATE-",0)!=-1||l.indexOf("-END TRUSTED CERTIFICATE-",0)!=-1){return X509.getPublicKeyFromCertPEM(l)}if(l.indexOf("-END PUBLIC KEY-")!=-1){var O=pemtohex(l,"PUBLIC KEY");return F._getKeyFromPublicPKCS8Hex(O)}if(l.indexOf("-END RSA PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")==-1){var m=M(l,"RSA PRIVATE KEY");return F.getKey(m,null,"pkcs5prv")}if(l.indexOf("-END DSA PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")==-1){var I=M(l,"DSA PRIVATE KEY");var E=d(I,0,[1],"02");var D=d(I,0,[2],"02");var K=d(I,0,[3],"02");var r=d(I,0,[4],"02");var s=d(I,0,[5],"02");var P=new C();P.setPrivate(new BigInteger(E,16),new BigInteger(D,16),new BigInteger(K,16),new BigInteger(r,16),new BigInteger(s,16));return P}if(l.indexOf("-END PRIVATE KEY-")!=-1){return F.getKeyFromPlainPrivatePKCS8PEM(l)}if(l.indexOf("-END RSA PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")!=-1){var o=F.getDecryptedKeyHex(l,k);var H=new RSAKey();H.readPKCS5PrvKeyHex(o);return H}if(l.indexOf("-END EC PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")!=-1){var I=F.getDecryptedKeyHex(l,k);var P=d(I,0,[1],"04");var f=d(I,0,[2,0],"06");var A=d(I,0,[3,0],"03").substr(2);var e="";if(KJUR.crypto.OID.oidhex2name[f]!==undefined){e=KJUR.crypto.OID.oidhex2name[f]}else{throw"undefined OID(hex) in KJUR.crypto.OID: "+f}var j=new i({curve:e});j.setPublicKeyHex(A);j.setPrivateKeyHex(P);j.isPublic=false;return j}if(l.indexOf("-END DSA PRIVATE KEY-")!=-1&&l.indexOf("4,ENCRYPTED")!=-1){var I=F.getDecryptedKeyHex(l,k);var E=d(I,0,[1],"02");var D=d(I,0,[2],"02");var K=d(I,0,[3],"02");var r=d(I,0,[4],"02");var s=d(I,0,[5],"02");var P=new C();P.setPrivate(new BigInteger(E,16),new BigInteger(D,16),new BigInteger(K,16),new BigInteger(r,16),new BigInteger(s,16));return P}if(l.indexOf("-END ENCRYPTED PRIVATE KEY-")!=-1){return F.getKeyFromEncryptedPKCS8PEM(l,k)}throw"not supported argument"};KEYUTIL.generateKeypair=function(a,c){if(a=="RSA"){var b=c;var h=new RSAKey();h.generate(b,"10001");h.isPrivate=true;h.isPublic=true;var f=new RSAKey();var e=h.n.toString(16);var i=h.e.toString(16);f.setPublic(e,i);f.isPrivate=false;f.isPublic=true;var k={};k.prvKeyObj=h;k.pubKeyObj=f;return k}else{if(a=="EC"){var d=c;var g=new KJUR.crypto.ECDSA({curve:d});var j=g.generateKeyPairHex();var h=new KJUR.crypto.ECDSA({curve:d});h.setPublicKeyHex(j.ecpubhex);h.setPrivateKeyHex(j.ecprvhex);h.isPrivate=true;h.isPublic=false;var f=new KJUR.crypto.ECDSA({curve:d});f.setPublicKeyHex(j.ecpubhex);f.isPrivate=false;f.isPublic=true;var k={};k.prvKeyObj=h;k.pubKeyObj=f;return k}else{throw"unknown algorithm: "+a}}};KEYUTIL.getPEM=function(b,D,y,m,q,j){var F=KJUR,k=F.asn1,z=k.DERObjectIdentifier,f=k.DERInteger,l=k.ASN1Util.newObject,a=k.x509,C=a.SubjectPublicKeyInfo,e=F.crypto,u=e.DSA,r=e.ECDSA,n=RSAKey;function A(s){var G=l({seq:[{"int":0},{"int":{bigint:s.n}},{"int":s.e},{"int":{bigint:s.d}},{"int":{bigint:s.p}},{"int":{bigint:s.q}},{"int":{bigint:s.dmp1}},{"int":{bigint:s.dmq1}},{"int":{bigint:s.coeff}}]});return G}function B(G){var s=l({seq:[{"int":1},{octstr:{hex:G.prvKeyHex}},{tag:["a0",true,{oid:{name:G.curveName}}]},{tag:["a1",true,{bitstr:{hex:"00"+G.pubKeyHex}}]}]});return s}function x(s){var G=l({seq:[{"int":0},{"int":{bigint:s.p}},{"int":{bigint:s.q}},{"int":{bigint:s.g}},{"int":{bigint:s.y}},{"int":{bigint:s.x}}]});return G}if(((n!==undefined&&b instanceof n)||(u!==undefined&&b instanceof u)||(r!==undefined&&b instanceof r))&&b.isPublic==true&&(D===undefined||D=="PKCS8PUB")){var E=new C(b);var w=E.getEncodedHex();return hextopem(w,"PUBLIC KEY")}if(D=="PKCS1PRV"&&n!==undefined&&b instanceof n&&(y===undefined||y==null)&&b.isPrivate==true){var E=A(b);var w=E.getEncodedHex();return hextopem(w,"RSA PRIVATE KEY")}if(D=="PKCS1PRV"&&r!==undefined&&b instanceof r&&(y===undefined||y==null)&&b.isPrivate==true){var i=new z({name:b.curveName});var v=i.getEncodedHex();var h=B(b);var t=h.getEncodedHex();var p="";p+=hextopem(v,"EC PARAMETERS");p+=hextopem(t,"EC PRIVATE KEY");return p}if(D=="PKCS1PRV"&&u!==undefined&&b instanceof u&&(y===undefined||y==null)&&b.isPrivate==true){var E=x(b);var w=E.getEncodedHex();return hextopem(w,"DSA PRIVATE KEY")}if(D=="PKCS5PRV"&&n!==undefined&&b instanceof n&&(y!==undefined&&y!=null)&&b.isPrivate==true){var E=A(b);var w=E.getEncodedHex();if(m===undefined){m="DES-EDE3-CBC"}return this.getEncryptedPKCS5PEMFromPrvKeyHex("RSA",w,y,m,j)}if(D=="PKCS5PRV"&&r!==undefined&&b instanceof r&&(y!==undefined&&y!=null)&&b.isPrivate==true){var E=B(b);var w=E.getEncodedHex();if(m===undefined){m="DES-EDE3-CBC"}return this.getEncryptedPKCS5PEMFromPrvKeyHex("EC",w,y,m,j)}if(D=="PKCS5PRV"&&u!==undefined&&b instanceof u&&(y!==undefined&&y!=null)&&b.isPrivate==true){var E=x(b);var w=E.getEncodedHex();if(m===undefined){m="DES-EDE3-CBC"}return this.getEncryptedPKCS5PEMFromPrvKeyHex("DSA",w,y,m,j)}var o=function(G,s){var I=c(G,s);var H=new l({seq:[{seq:[{oid:{name:"pkcs5PBES2"}},{seq:[{seq:[{oid:{name:"pkcs5PBKDF2"}},{seq:[{octstr:{hex:I.pbkdf2Salt}},{"int":I.pbkdf2Iter}]}]},{seq:[{oid:{name:"des-EDE3-CBC"}},{octstr:{hex:I.encryptionSchemeIV}}]}]}]},{octstr:{hex:I.ciphertext}}]});return H.getEncodedHex()};var c=function(N,O){var H=100;var M=CryptoJS.lib.WordArray.random(8);var L="DES-EDE3-CBC";var s=CryptoJS.lib.WordArray.random(8);var I=CryptoJS.PBKDF2(O,M,{keySize:192/32,iterations:H});var J=CryptoJS.enc.Hex.parse(N);var K=CryptoJS.TripleDES.encrypt(J,I,{iv:s})+"";var G={};G.ciphertext=K;G.pbkdf2Salt=CryptoJS.enc.Hex.stringify(M);G.pbkdf2Iter=H;G.encryptionSchemeAlg=L;G.encryptionSchemeIV=CryptoJS.enc.Hex.stringify(s);return G};if(D=="PKCS8PRV"&&n!=undefined&&b instanceof n&&b.isPrivate==true){var g=A(b);var d=g.getEncodedHex();var E=l({seq:[{"int":0},{seq:[{oid:{name:"rsaEncryption"}},{"null":true}]},{octstr:{hex:d}}]});var w=E.getEncodedHex();if(y===undefined||y==null){return hextopem(w,"PRIVATE KEY")}else{var t=o(w,y);return hextopem(t,"ENCRYPTED PRIVATE KEY")}}if(D=="PKCS8PRV"&&r!==undefined&&b instanceof r&&b.isPrivate==true){var g=new l({seq:[{"int":1},{octstr:{hex:b.prvKeyHex}},{tag:["a1",true,{bitstr:{hex:"00"+b.pubKeyHex}}]}]});var d=g.getEncodedHex();var E=l({seq:[{"int":0},{seq:[{oid:{name:"ecPublicKey"}},{oid:{name:b.curveName}}]},{octstr:{hex:d}}]});var w=E.getEncodedHex();if(y===undefined||y==null){return hextopem(w,"PRIVATE KEY")}else{var t=o(w,y);return hextopem(t,"ENCRYPTED PRIVATE KEY")}}if(D=="PKCS8PRV"&&u!==undefined&&b instanceof u&&b.isPrivate==true){var g=new f({bigint:b.x});var d=g.getEncodedHex();var E=l({seq:[{"int":0},{seq:[{oid:{name:"dsa"}},{seq:[{"int":{bigint:b.p}},{"int":{bigint:b.q}},{"int":{bigint:b.g}}]}]},{octstr:{hex:d}}]});var w=E.getEncodedHex();if(y===undefined||y==null){return hextopem(w,"PRIVATE KEY")}else{var t=o(w,y);return hextopem(t,"ENCRYPTED PRIVATE KEY")}}throw"unsupported object nor format"};KEYUTIL.getKeyFromCSRPEM=function(b){var a=pemtohex(b,"CERTIFICATE REQUEST");var c=KEYUTIL.getKeyFromCSRHex(a);return c};KEYUTIL.getKeyFromCSRHex=function(a){var c=KEYUTIL.parseCSRHex(a);var b=KEYUTIL.getKey(c.p8pubkeyhex,null,"pkcs8pub");return b};KEYUTIL.parseCSRHex=function(d){var i=ASN1HEX;var f=i.getChildIdx;var c=i.getTLV;var b={};var g=d;if(g.substr(0,2)!="30"){throw"malformed CSR(code:001)"}var e=f(g,0);if(e.length<1){throw"malformed CSR(code:002)"}if(g.substr(e[0],2)!="30"){throw"malformed CSR(code:003)"}var a=f(g,e[0]);if(a.length<3){throw"malformed CSR(code:004)"}b.p8pubkeyhex=c(g,a[2]);return b};KEYUTIL.getJWKFromKey=function(d){var b={};if(d instanceof RSAKey&&d.isPrivate){b.kty="RSA";b.n=hextob64u(d.n.toString(16));b.e=hextob64u(d.e.toString(16));b.d=hextob64u(d.d.toString(16));b.p=hextob64u(d.p.toString(16));b.q=hextob64u(d.q.toString(16));b.dp=hextob64u(d.dmp1.toString(16));b.dq=hextob64u(d.dmq1.toString(16));b.qi=hextob64u(d.coeff.toString(16));return b}else{if(d instanceof RSAKey&&d.isPublic){b.kty="RSA";b.n=hextob64u(d.n.toString(16));b.e=hextob64u(d.e.toString(16));return b}else{if(d instanceof KJUR.crypto.ECDSA&&d.isPrivate){var a=d.getShortNISTPCurveName();if(a!=="P-256"&&a!=="P-384"){throw"unsupported curve name for JWT: "+a}var c=d.getPublicKeyXYHex();b.kty="EC";b.crv=a;b.x=hextob64u(c.x);b.y=hextob64u(c.y);b.d=hextob64u(d.prvKeyHex);return b}else{if(d instanceof KJUR.crypto.ECDSA&&d.isPublic){var a=d.getShortNISTPCurveName();if(a!=="P-256"&&a!=="P-384"){throw"unsupported curve name for JWT: "+a}var c=d.getPublicKeyXYHex();b.kty="EC";b.crv=a;b.x=hextob64u(c.x);b.y=hextob64u(c.y);return b}}}}throw"not supported key object"}; +RSAKey.getPosArrayOfChildrenFromHex=function(a){return ASN1HEX.getChildIdx(a,0)};RSAKey.getHexValueArrayOfChildrenFromHex=function(f){var n=ASN1HEX;var i=n.getV;var k=RSAKey.getPosArrayOfChildrenFromHex(f);var e=i(f,k[0]);var j=i(f,k[1]);var b=i(f,k[2]);var c=i(f,k[3]);var h=i(f,k[4]);var g=i(f,k[5]);var m=i(f,k[6]);var l=i(f,k[7]);var d=i(f,k[8]);var k=new Array();k.push(e,j,b,c,h,g,m,l,d);return k};RSAKey.prototype.readPrivateKeyFromPEMString=function(d){var c=pemtohex(d);var b=RSAKey.getHexValueArrayOfChildrenFromHex(c);this.setPrivateEx(b[1],b[2],b[3],b[4],b[5],b[6],b[7],b[8])};RSAKey.prototype.readPKCS5PrvKeyHex=function(c){var b=RSAKey.getHexValueArrayOfChildrenFromHex(c);this.setPrivateEx(b[1],b[2],b[3],b[4],b[5],b[6],b[7],b[8])};RSAKey.prototype.readPKCS8PrvKeyHex=function(e){var c,j,l,b,a,f,d,k;var m=ASN1HEX;var g=m.getVbyList;if(m.isASN1HEX(e)===false){throw"not ASN.1 hex string"}try{c=g(e,0,[2,0,1],"02");j=g(e,0,[2,0,2],"02");l=g(e,0,[2,0,3],"02");b=g(e,0,[2,0,4],"02");a=g(e,0,[2,0,5],"02");f=g(e,0,[2,0,6],"02");d=g(e,0,[2,0,7],"02");k=g(e,0,[2,0,8],"02")}catch(i){throw"malformed PKCS#8 plain RSA private key"}this.setPrivateEx(c,j,l,b,a,f,d,k)};RSAKey.prototype.readPKCS5PubKeyHex=function(c){var e=ASN1HEX;var b=e.getV;if(e.isASN1HEX(c)===false){throw"keyHex is not ASN.1 hex string"}var a=e.getChildIdx(c,0);if(a.length!==2||c.substr(a[0],2)!=="02"||c.substr(a[1],2)!=="02"){throw"wrong hex for PKCS#5 public key"}var f=b(c,a[0]);var d=b(c,a[1]);this.setPublic(f,d)};RSAKey.prototype.readPKCS8PubKeyHex=function(b){var c=ASN1HEX;if(c.isASN1HEX(b)===false){throw"not ASN.1 hex string"}if(c.getTLVbyList(b,0,[0,0])!=="06092a864886f70d010101"){throw"not PKCS8 RSA public key"}var a=c.getTLVbyList(b,0,[1,0]);this.readPKCS5PubKeyHex(a)};RSAKey.prototype.readCertPubKeyHex=function(b,d){var a,c;a=new X509();a.readCertHex(b);c=a.getPublicKeyHex();this.readPKCS8PubKeyHex(c)}; +var _RE_HEXDECONLY=new RegExp("");_RE_HEXDECONLY.compile("[^0-9a-f]","gi");function _rsasign_getHexPaddedDigestInfoForString(d,e,a){var b=function(f){return KJUR.crypto.Util.hashString(f,a)};var c=b(d);return KJUR.crypto.Util.getPaddedDigestInfoHex(c,a,e)}function _zeroPaddingOfSignature(e,d){var c="";var a=d/4-e.length;for(var b=0;b>24,(d&16711680)>>16,(d&65280)>>8,d&255]))));d+=1}return b}RSAKey.prototype.signPSS=function(e,a,d){var c=function(f){return KJUR.crypto.Util.hashHex(f,a)};var b=c(rstrtohex(e));if(d===undefined){d=-1}return this.signWithMessageHashPSS(b,a,d)};RSAKey.prototype.signWithMessageHashPSS=function(l,a,k){var b=hextorstr(l);var g=b.length;var m=this.n.bitLength()-1;var c=Math.ceil(m/8);var d;var o=function(i){return KJUR.crypto.Util.hashHex(i,a)};if(k===-1||k===undefined){k=g}else{if(k===-2){k=c-g-2}else{if(k<-2){throw"invalid salt length"}}}if(c<(g+k+2)){throw"data too long"}var f="";if(k>0){f=new Array(k);new SecureRandom().nextBytes(f);f=String.fromCharCode.apply(String,f)}var n=hextorstr(o(rstrtohex("\x00\x00\x00\x00\x00\x00\x00\x00"+b+f)));var j=[];for(d=0;d>(8*c-m))&255;q[0]&=~p;for(d=0;dthis.n.bitLength()){return 0}var i=this.doPublic(b);var e=i.toString(16).replace(/^1f+00/,"");var g=_rsasign_getAlgNameAndHashFromHexDisgestInfo(e);if(g.length==0){return false}var d=g[0];var h=g[1];var a=function(k){return KJUR.crypto.Util.hashString(k,d)};var c=a(f);return(h==c)};RSAKey.prototype.verifyWithMessageHash=function(e,a){a=a.replace(_RE_HEXDECONLY,"");a=a.replace(/[ \n]+/g,"");var b=parseBigInt(a,16);if(b.bitLength()>this.n.bitLength()){return 0}var h=this.doPublic(b);var g=h.toString(16).replace(/^1f+00/,"");var c=_rsasign_getAlgNameAndHashFromHexDisgestInfo(g);if(c.length==0){return false}var d=c[0];var f=c[1];return(f==e)};RSAKey.prototype.verifyPSS=function(c,b,a,f){var e=function(g){return KJUR.crypto.Util.hashHex(g,a)};var d=e(rstrtohex(c));if(f===undefined){f=-1}return this.verifyWithMessageHashPSS(d,b,a,f)};RSAKey.prototype.verifyWithMessageHashPSS=function(f,s,l,c){var k=new BigInteger(s,16);if(k.bitLength()>this.n.bitLength()){return false}var r=function(i){return KJUR.crypto.Util.hashHex(i,l)};var j=hextorstr(f);var h=j.length;var g=this.n.bitLength()-1;var m=Math.ceil(g/8);var q;if(c===-1||c===undefined){c=h}else{if(c===-2){c=m-h-2}else{if(c<-2){throw"invalid salt length"}}}if(m<(h+c+2)){throw"data too long"}var a=this.doPublic(k).toByteArray();for(q=0;q>(8*m-g))&255;if((d.charCodeAt(0)&p)!==0){throw"bits beyond keysize not zero"}var n=pss_mgf1_str(e,d.length,r);var o=[];for(q=0;q0){var b=":"+n.join(":")+":";if(b.indexOf(":"+k+":")==-1){throw"algorithm '"+k+"' not accepted in the list"}}if(k!="none"&&B===null){throw"key shall be specified to verify."}if(typeof B=="string"&&B.indexOf("-----BEGIN ")!=-1){B=KEYUTIL.getKey(B)}if(z=="RS"||z=="PS"){if(!(B instanceof m)){throw"key shall be a RSAKey obj for RS* and PS* algs"}}if(z=="ES"){if(!(B instanceof p)){throw"key shall be a ECDSA obj for ES* algs"}}if(k=="none"){}var u=null;if(t.jwsalg2sigalg[l.alg]===undefined){throw"unsupported alg name: "+k}else{u=t.jwsalg2sigalg[k]}if(u=="none"){throw"not supported"}else{if(u.substr(0,4)=="Hmac"){var o=null;if(B===undefined){throw"hexadecimal key shall be specified for HMAC"}var j=new s({alg:u,pass:B});j.updateString(c);o=j.doFinal();return A==o}else{if(u.indexOf("withECDSA")!=-1){var h=null;try{h=p.concatSigToASN1Sig(A)}catch(v){return false}var g=new d({alg:u});g.init(B);g.updateString(c);return g.verify(h)}else{var g=new d({alg:u});g.init(B);g.updateString(c);return g.verify(A)}}}};KJUR.jws.JWS.parse=function(g){var c=g.split(".");var b={};var f,e,d;if(c.length!=2&&c.length!=3){throw"malformed sJWS: wrong number of '.' splitted elements"}f=c[0];e=c[1];if(c.length==3){d=c[2]}b.headerObj=KJUR.jws.JWS.readSafeJSONString(b64utoutf8(f));b.payloadObj=KJUR.jws.JWS.readSafeJSONString(b64utoutf8(e));b.headerPP=JSON.stringify(b.headerObj,null," ");if(b.payloadObj==null){b.payloadPP=b64utoutf8(e)}else{b.payloadPP=JSON.stringify(b.payloadObj,null," ")}if(d!==undefined){b.sigHex=b64utohex(d)}return b};KJUR.jws.JWS.verifyJWT=function(e,l,r){var d=KJUR,j=d.jws,o=j.JWS,n=o.readSafeJSONString,p=o.inArray,f=o.includedArray;var k=e.split(".");var c=k[0];var i=k[1];var q=c+"."+i;var m=b64utohex(k[2]);var h=n(b64utoutf8(c));var g=n(b64utoutf8(i));if(h.alg===undefined){return false}if(r.alg===undefined){throw"acceptField.alg shall be specified"}if(!p(h.alg,r.alg)){return false}if(g.iss!==undefined&&typeof r.iss==="object"){if(!p(g.iss,r.iss)){return false}}if(g.sub!==undefined&&typeof r.sub==="object"){if(!p(g.sub,r.sub)){return false}}if(g.aud!==undefined&&typeof r.aud==="object"){if(typeof g.aud=="string"){if(!p(g.aud,r.aud)){return false}}else{if(typeof g.aud=="object"){if(!f(g.aud,r.aud)){return false}}}}var b=j.IntDate.getNow();if(r.verifyAt!==undefined&&typeof r.verifyAt==="number"){b=r.verifyAt}if(r.gracePeriod===undefined||typeof r.gracePeriod!=="number"){r.gracePeriod=0}if(g.exp!==undefined&&typeof g.exp=="number"){if(g.exp+r.gracePeriodl){this.aHeader.pop()}if(this.aSignature.length>l){this.aSignature.pop()}throw"addSignature failed: "+i}};this.verifyAll=function(h){if(this.aHeader.length!==h.length||this.aSignature.length!==h.length){return false}for(var g=0;g0){this.aHeader=g.headers}else{throw"malformed header"}if(typeof g.payload==="string"){this.sPayload=g.payload}else{throw"malformed signatures"}if(g.signatures.length>0){this.aSignatures=g.signatures}else{throw"malformed signatures"}}catch(e){throw"malformed JWS-JS JSON object: "+e}}};this.getJSON=function(){return{headers:this.aHeader,payload:this.sPayload,signatures:this.aSignature}};this.isEmpty=function(){if(this.aHeader.length==0){return 1}return 0}}; diff --git a/w3c-visserver-api/test/web-client/rest-client.js b/w3c-visserver-api/test/web-client/rest-client.js index 9eca1dd..34e4b96 100644 --- a/w3c-visserver-api/test/web-client/rest-client.js +++ b/w3c-visserver-api/test/web-client/rest-client.js @@ -17,7 +17,7 @@ document.getElementById('server-doc-root').value = "vss/api/v1"; // setup event handlers document.getElementById('make-get-request').onclick = function req() {return makeRestRequest('GET');}; -document.getElementById('make-set-request').onclick = function req() {return makeRestRequest('SET');}; +document.getElementById('make-post-request').onclick = function req() {return makeRestRequest("POST");}; document.getElementById('client-token').addEventListener('paste', onTokenUpdate); @@ -48,19 +48,23 @@ function getBase64url(source) { } function getEncodedToken() { - var stringifiedHeader = CryptoJS.enc.Utf8.parse(JSON.stringify(jwtHeader)); - var encodedHeader = getBase64url(stringifiedHeader); - var data = document.getElementById("client-token").value; - var encodedData = getBase64url(CryptoJS.enc.Utf8.parse(data)); - - var signatureInput = encodedHeader + "." + encodedData; - var secret = document.getElementById("client-key-cert").value; - var signature = CryptoJS.SHA256(signatureInput, secret); - signature = getBase64url(signature); - - // update on-screen token for user - document.getElementById("auth-token-string").value = signatureInput + "." + signature; + var sHead = JSON.stringify(jwtHeader); + var head = KJUR.jws.JWS.readSafeJSONString(sHead); + var sPayload = data;//newline_toDos(document.form1.jwspayload1.value); + var sPemPrvKey = document.getElementById("client-key-cert").value; + + var jws = new KJUR.jws.JWS(); + var sResult = null; + try { + prv = KEYUTIL.getKey(sPemPrvKey); + + sResult = KJUR.jws.JWS.sign(head.alg, sHead, sPayload, prv); + document.getElementById("auth-token-string").value = sResult; + } + catch (ex) { + alert("Error: " + ex); + } return false; } @@ -77,8 +81,8 @@ function makeRestRequest(type) { requestObj.open(type, requestUrl, true); // callback handler when positive response received - requestObj.onload = function () { - if (this.readyState == 4 && this.status == 200) { + requestObj.onreadystatechange = function () { + if (this.readyState == XMLHttpRequest.DONE && this.status == 200) { document.getElementById('json-raw-output').innerHTML = this.responseText; let target = '#json-raw-output'; jsonView.format(this.responseText, target); diff --git a/w3c-visserver-api/test/web-client/style.css b/w3c-visserver-api/test/web-client/style.css index a98e653..b3d0439 100644 --- a/w3c-visserver-api/test/web-client/style.css +++ b/w3c-visserver-api/test/web-client/style.css @@ -118,6 +118,8 @@ textarea { } #auth-token textarea{ width: 96%; + height: 70px; + font-size: 9px; resize: none; /*user-select: none;*/ -webkit-border-radius: 6px; @@ -131,6 +133,6 @@ textarea { width: 96%; } -#make-get-request #make-set-request { +#make-get-request #make-post-request { width: 100px; } From c192831732b764a8bd24b14e1bd135086f780b46 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Wed, 23 Oct 2019 11:32:46 +0200 Subject: [PATCH 25/32] Update to VIS REST APi test page Improved look-and-feel of page Added log part of all executed requests and their responses For now simple REST requests can be sent (without HTTP request body data) to the VIS REST server. Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/test/web-client/index.html | 49 +++---- .../test/web-client/rest-client.js | 125 ++++++++++++++---- w3c-visserver-api/test/web-client/style.css | 59 ++++++++- 3 files changed, 181 insertions(+), 52 deletions(-) diff --git a/w3c-visserver-api/test/web-client/index.html b/w3c-visserver-api/test/web-client/index.html index cc14988..f56b7e7 100644 --- a/w3c-visserver-api/test/web-client/index.html +++ b/w3c-visserver-api/test/web-client/index.html @@ -5,11 +5,11 @@ - Tester for REST API support for W3C-VISServer-API + GUI tool for exercising REST API functionality in W3C-VISServer-API - + @@ -32,28 +32,36 @@

Current authorization token:
- +
- Input resource request to fetch for the W3C VIS Server below: + Input resource request path to fetch for the W3C VIS Server below:
-
- - + +
+ + +
-
- -
-
-
+
+
+
+ Log of REST resource requests and server responses
+

As an option, clicking on resource string of any request shown in log, given resource string will be copied back to input resouce text field. + Useful to speed-up testing when identical requests needs to be made or with slight modifications.

+ Color codes: REST Request OK REST Response ERROR REST Response +
+
    +
+
@@ -63,9 +71,9 @@
Server parameters: - Address:
- Port:
- Doc root:
+ Address:
+ Port:
+ Doc root:
@@ -73,16 +81,11 @@
- Client token and certificates configuration + Client token and key configuration Client token content: -
- Client PEM certificate content: -
+
Client key certificate content: - +
diff --git a/w3c-visserver-api/test/web-client/rest-client.js b/w3c-visserver-api/test/web-client/rest-client.js index 34e4b96..40aa277 100644 --- a/w3c-visserver-api/test/web-client/rest-client.js +++ b/w3c-visserver-api/test/web-client/rest-client.js @@ -18,33 +18,31 @@ document.getElementById('server-doc-root').value = "vss/api/v1"; // setup event handlers document.getElementById('make-get-request').onclick = function req() {return makeRestRequest('GET');}; document.getElementById('make-post-request').onclick = function req() {return makeRestRequest("POST");}; +document.getElementById('make-auth-request').onclick = function req() {return makeAuthRequest("POST");}; document.getElementById('client-token').addEventListener('paste', onTokenUpdate); -//document.getElementById('client-token').addEventListener('keyup', onTokenUpdate); +document.getElementById('client-token').addEventListener('keyup', onTokenUpdate); document.getElementById('client-token').addEventListener('change', onTokenUpdate); document.getElementById('client-key-cert').addEventListener('paste', onTokenUpdate); -//document.getElementById('client-key-cert').addEventListener('keyup', onTokenUpdate); +document.getElementById('client-key-cert').addEventListener('keyup', onTokenUpdate); document.getElementById('client-key-cert').addEventListener('change', onTokenUpdate); /////////////////// // Implementation function onTokenUpdate() { - getEncodedToken(); + if((document.getElementById("client-token").value != '') && + (document.getElementById('client-key-cert').value != '')) { + getEncodedToken(); + } return false; } -function getBase64url(source) { - // make source base64 encoded - encodedSource = CryptoJS.enc.Base64.stringify(source); - // convert base64 base64url specifications - encodedSource = encodedSource.replace(/=+$/, ''); - encodedSource = encodedSource.replace(/\+/g, '-'); - encodedSource = encodedSource.replace(/\//g, '_'); - - return encodedSource; +function onListResourceClick() { + document.getElementById('resource-path').value = this.innerText; + return false; } function getEncodedToken() { @@ -69,6 +67,94 @@ function getEncodedToken() { return false; } +function addReqToLog(type, resource) { + var newListItem = document.createElement("LI"); + newListItem.setAttribute('class', 'li-request'); + + var newTypeDiv = document.createElement("div"); + newTypeDiv.setAttribute('class', 'liType'); + newTypeDiv.textContent = type; + newListItem.appendChild(newTypeDiv); + + var newResourceDiv = document.createElement("div"); + newResourceDiv.setAttribute('class', 'liResource'); + newResourceDiv.textContent = resource; + newResourceDiv.onclick = onListResourceClick; + newListItem.appendChild(newResourceDiv); + + var list = document.getElementById("log-list"); + list.insertBefore(newListItem, list.childNodes[0]); + +} + +function addRespToLog(htmlStatus, message) { + var type = 'SUCCESS'; + + if (htmlStatus == 200) { + } else { + type = 'ERROR: HTML status = ' + htmlStatus; + // if no response + if (message == '') { + message = 'No response, check server status'; + } + } + var newListItem = document.createElement("LI"); + if (htmlStatus == 200) { + newListItem.setAttribute('class', 'li-response-ok'); + } else { + newListItem.setAttribute('class', 'li-response-nok'); + } + + var newTypeDiv = document.createElement("div"); + newTypeDiv.setAttribute('class', 'liType'); + newTypeDiv.textContent = type; + newListItem.appendChild(newTypeDiv); + + var newResourceDiv = document.createElement("div"); + newResourceDiv.setAttribute('class', 'liResource'); + newResourceDiv.textContent = message; + newListItem.appendChild(newResourceDiv); + + var list = document.getElementById("log-list"); + list.insertBefore(newListItem, list.childNodes[0]); + +} + +function makeHttpRequest(type, requestUrl, resourcePath) { + requestObj = new XMLHttpRequest(); + var completeUrl = requestUrl + resourcePath; + requestObj.open(type, completeUrl, true); + + addReqToLog(type, resourcePath); + + // callback handler when positive response received + requestObj.onreadystatechange = function () { + if (this.readyState == XMLHttpRequest.DONE) { + addRespToLog(this.status, this.responseText); + if (this.status == 200) { + let target = '#json-table'; + document.getElementById('json-table').innerHTML = ''; + jsonView.format(this.responseText, target); + } + } + }; + requestObj.send(null); +} + +function makeAuthRequest(type) { + let server_address = document.getElementById('server-address').value; + let server_port = document.getElementById('server-port').value; + let doc_root = document.getElementById('server-doc-root').value; + let resource = document.getElementById('auth-token-string').value; + + if (server_address != "" && server_port != "") { + requestUrl = 'http://' + server_address + ':' + server_port + '/' + doc_root + '/'; + makeHttpRequest(type, requestUrl, 'authorize?token=' + resource); + } + // prevent reloading of page + return false; +} + function makeRestRequest(type) { let server_address = document.getElementById('server-address').value; let server_port = document.getElementById('server-port').value; @@ -76,19 +162,8 @@ function makeRestRequest(type) { let resource = document.getElementById('resource-path').value; if (server_address != "" && server_port != "") { - requestObj = new XMLHttpRequest(); - requestUrl = 'http://' + server_address + ':' + server_port + '/' + doc_root + '/' + resource; - requestObj.open(type, requestUrl, true); - - // callback handler when positive response received - requestObj.onreadystatechange = function () { - if (this.readyState == XMLHttpRequest.DONE && this.status == 200) { - document.getElementById('json-raw-output').innerHTML = this.responseText; - let target = '#json-raw-output'; - jsonView.format(this.responseText, target); - } - }; - requestObj.send(null); + requestUrl = 'http://' + server_address + ':' + server_port + '/' + doc_root + '/'; + makeHttpRequest(type, requestUrl, resource); } // prevent reloadig of page return false; diff --git a/w3c-visserver-api/test/web-client/style.css b/w3c-visserver-api/test/web-client/style.css index b3d0439..702f2db 100644 --- a/w3c-visserver-api/test/web-client/style.css +++ b/w3c-visserver-api/test/web-client/style.css @@ -22,13 +22,13 @@ html { } #main-content-tab { - margin-right: -300px; + margin-right: -360px; width: 100%; float: left; } #config-content-tab { - width: 300px; + width: 350px; float: left; } @@ -37,7 +37,7 @@ html { } .row { - margin-right: 320px; + margin-right: 370px; } .row::after { @@ -80,6 +80,57 @@ html { width: 100%; } +#logging { + margin-left: 30px; +} + +#log { + width: 46%; + border: 1px solid; + border-color: #1e9b8a; + border-radius: 6px; + padding: 0; +} + +#log ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +#log ul li { + border-radius: 6px; +} + +.li-request { + background-color: #d6f9ff; +} + +.li-response-ok { + background-color: #d8ffd6; +} + +.li-response-nok { + background-color: #ffd6d6; +} + +.liType { + width: auto; +} + +.liResource { + width: auto; + white-space: pre-wrap; + word-wrap: break-word; +} + +#json-table { + width: 47%; + border: 1px solid; + border-color: #1e9b8a; + border-radius: 6px; +} + fieldset { border: 1px solid; border-color: #1e9b8a; @@ -105,7 +156,7 @@ input:[type=number]focus { textarea { font-family: monospace; - font-size: 7px; + font-size: 8px; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; From cf4714a875a3b3006c667873a63eb84b803dfb77 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Fri, 25 Oct 2019 07:37:11 +0200 Subject: [PATCH 26/32] Added support for PUT requests for REST API - Small refactor of REST handling code to support PUT requests for setting signal values. - Updated test client page with PUT support. - Added simple OpenAPI 3.0 standard description of current 'v1' REST API implementation in 'yaml' format. Benefit of OpenAPI is standardized format and rich tool options. As REST API definitions become more detailed, different tools available can generate both server and client running test mockups to excersize API and validate it. Also, tools can generate live documentation which can invore REST API directly. For demonstration of this, run VIS server. Open 'https://editor.swagger.io/' page which is OpenAPI editor. Paste content of rest-api.yaml into (or import) and live REST API documentation will be shown. As examples are included in documentation by design, we can excercise API by trying different endpoints with default example values or changing it to our own. Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/doc/rest-api.yaml | 369 ++++++++++++++++++ .../include/RestV1ApiHandler.hpp | 2 +- w3c-visserver-api/src/RestV1ApiHandler.cpp | 58 ++- w3c-visserver-api/test/web-client/index.html | 1 + .../test/web-client/rest-client.js | 2 +- 5 files changed, 422 insertions(+), 10 deletions(-) create mode 100644 w3c-visserver-api/doc/rest-api.yaml diff --git a/w3c-visserver-api/doc/rest-api.yaml b/w3c-visserver-api/doc/rest-api.yaml new file mode 100644 index 0000000..9f35fe0 --- /dev/null +++ b/w3c-visserver-api/doc/rest-api.yaml @@ -0,0 +1,369 @@ +openapi: '3.0.1' +info: + title: W3C Vehicle Information Specification REST API documentation + version: 0.0.1 + description: Documentation for initial implementation of REST API for accessing VIS information + based on Web-Sockets definition.

+ Implemented REST API re-use defined target paths for both requests and responses.

+ Current REST API definition do not support request with passing data in HTTP body of requests + due to current simple use-cases. + But transferring request data through request HTTP body could be investigated as a means of more + complex GET/SET requests; e.g. retrieve/update multiple signals in single request by + providing JSON array of signals, more complex query support of signal states/values, etc...

+ REST API supports both existing Web-socket signal path format with '.' as path separator, and more + 'REST-like' '/' path separator.

+ Due to REST API design supporting by default only client -> server flow, support for + 'subscribe' and 'unsubscribe' requests is intentionally ommitted. + It should be technically possible to add subscribe functionality, but this is currently out-of-scope + and could be discussion point in future.

+ This API definition proposes explicit API path and API version information through base URL. + This allows us to cleanly support different versions of API and|or W3C VIS standard. Aditional REST API + endpoint could be added to support providing information about versions' support of server + (through e.g. /vss/api/versions endpoint), but this is currently out-of-scope and could be + discussion point in future. + + contact: + email: kuksa-dev@eclipse.org + url: 'https://www.eclipse.org/kuksa/' + license: + name: Eclipse Public License v2.0 + url: 'https://www.eclipse.org/legal/epl-2.0/' + termsOfService: 'https://www.eclipse.org/legal/termsofuse.php' +servers: + - url: http://localhost:8090/vss/api/v1/ + - url: https://localhost:8090/vss/api/v1/ +tags: +- name: signals + description: "Acces specific signal or signal branch information" +- name: metadata + description: "Access metadata information" +- name: authorize + description: "Authorize user access" + +components: + parameters: + + signalBranchPathWithDots: + name: signalBranchPathWithDots + in: path + description: String defining branch structure, where branch items are separated by '.' character. + schema: + type: string + pattern: '^[A-Za-z0-9\.]+$' + required: true + example: Vehicle.Drivetrain.Transmission + + signalBranchPathWithSlashes: + name: signalBranchPathWithSlashes + in: path + description: String defining branch structure, where branch items are separated by '/' character. + schema: + type: string + pattern: '^[A-Za-z0-9/]+$' + allowReserved: true + required: true + example: Vehicle/Drivetrain/Transmission + + signalId: + name: signalId + in: path + description: Signal identifier + schema: + type: string + pattern: '^[A-Za-z0-9]+$' + required: true + example: "TravelledDistance" + + token: + name: token + in: query + description: JWT token string in 'base64url' format + required: true + schema: + type: string + format: base64url + + signalValue: + name: value + in: query + description: Signal value + required: true + schema: + type: integer + example: 123 + + responses: + + Success: + description: "Request completed successfully" + content: + application/json: + schema: + $ref: '#/components/schemas/SuccessResponse' + + SuccessAuthorize: + description: "Authorize request completed successfully" + content: + application/json: + schema: + $ref: '#/components/schemas/AuthSuccessResponse' + + BadRequestRsp: + description: "The specified resource was not found" + content: + application/json: + schema: + $ref: '#/components/schemas/BadRequestContent' + + schemas: + + Action: + type: string + enum: ['get', 'set', 'getMetadata', 'authorize'] + description: Action request + + Timestamp: + type: integer + description: Timestamp of event + example: 615154112 + + RequestId: + type: integer + description: Unique request identifier + example: 817151 + + SuccessResponse: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + path: + type: string + description: Requested resource path + example: "Vehicle.Drivetrain.Transmission.TravelledDistance" + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' + value: + type: object + description: Response content + + AuthSuccessResponse: + type: object + properties: + TTL: + type: integer + description: Token expiration time + example: 123456 + action: + type: string + example: authorize + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' + + ErrorDesc: + type: object + properties: + number: + type: integer + description: "HTML status code" + example: 400 + reason: + type: string + description: "Error reason" + message: + type: string + description: "Error description" + + BadRequestContent: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + error: + $ref: '#/components/schemas/ErrorDesc' + timestamp: + $ref: '#/components/schemas/Timestamp' + +paths: + /signals/{signalBranchPathWithDots}: + get: + tags: + - signals + summary: "Get signal branch information" + description: + Get information for specific signal branch. Branch items are separated by '.' character.
+ Response can contain array of other branches and|or signals. + operationId: getSignalBranchDataWithDots + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /signals/{signalBranchPathWithSlashes}: + get: + tags: + - signals + summary: "Get signal branch information" + description: + Get information for specific signal branch. Branch items are separated by '/' character.
+ Response can contain array of other branches and|or signals. + operationId: getSignalBranchDataWithSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /signals/{signalBranchPathWithDots}.{signalId}: + get: + tags: + - signals + summary: "Get specific signal information" + description: Get complete information for single specific signal + operationId: "getSingleSignalInformationDots" + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + - $ref: '#/components/parameters/signalId' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + put: + tags: + - signals + summary: "Set specific signal value" + description: Set value for single specific signal + operationId: "putSingleSignalInformationDots" + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + - $ref: '#/components/parameters/signalId' + - $ref: '#/components/parameters/signalValue' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /signals/{signalBranchPathWithSlashes}/{signalId}: + get: + tags: + - signals + summary: "Get single signal information" + description: Get complete information for single specific signal + operationId: getSingleSignalInformationSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + - $ref: '#/components/parameters/signalId' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + put: + tags: + - signals + summary: "Set single signal information" + description: Set value for single specific signal + operationId: putSingleSignalInformationSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + - $ref: '#/components/parameters/signalId' + - $ref: '#/components/parameters/signalValue' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /metadata/{signalBranchPathWithDots}: + get: + tags: + - metadata + summary: "Get metadata for specified branch" + description: + Get metadata information for specific signal branch. Branch items are separated by '.' character.
+ Response can contain array of other branches and|or signals. + operationId: getSignalBranchMetadataWithDots + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /metadata/{signalBranchPathWithSlashes}: + get: + tags: + - metadata + summary: "Get metadata for specified branch" + description: + Get metadata information for specific signal branch. Branch items are separated by '/' character.
+ Response can contain array of other branches and|or signals. + operationId: getSignalBranchMetadataWithSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /metadata/{signalBranchPathWithDots}.{signalId}: + get: + tags: + - metadata + summary: "Get metadata for specific signal" + description: Get metadata information for single specific signal + operationId: getSingleSignalMetadataInformationDots + parameters: + - $ref: '#/components/parameters/signalBranchPathWithDots' + - $ref: '#/components/parameters/signalId' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /metadata/{signalBranchPathWithSlashes}/{signalId}: + get: + tags: + - metadata + summary: "Get metadata for specific signal" + description: Get metadata information for single specific signal + operationId: getSingleSignalMetadataInformationSlashes + parameters: + - $ref: '#/components/parameters/signalBranchPathWithSlashes' + - $ref: '#/components/parameters/signalId' + responses: + '200': + $ref: '#/components/responses/Success' + '400': + $ref: '#/components/responses/BadRequestRsp' + + /authorize: + post: + tags: + - authorize + summary: "Authorize client" + description: + Authorize client with JWT token.
+ Check jwt.io for more details on how JWT is generated. + operationId: postAuthorizeClient + parameters: + - $ref: '#/components/parameters/token' + responses: + '200': + $ref: '#/components/responses/SuccessAuthorize' + '400': + $ref: '#/components/responses/BadRequestRsp' diff --git a/w3c-visserver-api/include/RestV1ApiHandler.hpp b/w3c-visserver-api/include/RestV1ApiHandler.hpp index 442c56f..1d1a5c6 100644 --- a/w3c-visserver-api/include/RestV1ApiHandler.hpp +++ b/w3c-visserver-api/include/RestV1ApiHandler.hpp @@ -55,7 +55,7 @@ class RestV1ApiHandler : public IRestHandler { private: bool GetSignalPath(uint32_t requestId, jsoncons::json& json, - std::string&& restTarget); + std::string& restTarget); /** * @brief Verify that HTTP target begins with correct root path and remove it if found * @param restTarget HTTP target path diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp index b1416ef..547a045 100644 --- a/w3c-visserver-api/src/RestV1ApiHandler.cpp +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -66,14 +66,16 @@ bool RestV1ApiHandler::verifyPathAndStrip(std::string& restTarget, std::string& bool RestV1ApiHandler::GetSignalPath(uint32_t requestId, jsoncons::json& json, - std::string&& restTarget) { + std::string& restTarget) { std::string signalPath; std::string foundStr; - std::string delimiter("/"); + std::string restDelimiter("/"); + std::string defaultDelimiter("."); + std::string queryDelimiter("?"); std::smatch sm; bool ret = true; - if (restTarget.size() && verifyPathAndStrip(restTarget, delimiter)) { + if (restTarget.size() && verifyPathAndStrip(restTarget, restDelimiter)) { while (restTarget.size()) { // we only accept clean printable characters const std::regex regexValidWord("^([A-Za-z]+)"); @@ -85,8 +87,17 @@ bool RestV1ApiHandler::GetSignalPath(uint32_t requestId, if ((restTarget.size() == 0)) { break; } - else if (verifyPathAndStrip(restTarget, delimiter)) { - signalPath += '.'; + // we support both '/' and '.' as branch/signal delimiters + else if (verifyPathAndStrip(restTarget, restDelimiter)) { + signalPath += defaultDelimiter; + } + // we support both '/' and '.' as branch/signal delimiters + else if (verifyPathAndStrip(restTarget, defaultDelimiter)) { + signalPath += defaultDelimiter; + } + // if we got to query start '?' char, end extraction if signal path + else if (restTarget[0] == '?') { + break; } else { JsonResponses::malFormedRequest( @@ -171,9 +182,40 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, // signals handler if (foundStr == "signals") { if (httpMethod == "get") { - ret = GetSignalPath(requestId, json, std::move(restTarget)); + if (GetSignalPath(requestId, json, restTarget)) { + json["action"] = "get"; + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Invalid path", + json); + } + } + else if (httpMethod == "put") { + if (GetSignalPath(requestId, json, restTarget)) { + json["action"] = "set"; + std::string queryStr("?value="); - json["action"] = "get"; + if (verifyPathAndStrip(restTarget, queryStr)) { + json["value"] = restTarget; + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Invalid query parameter", + json); + } + } + else { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Invalid path", + json); + } } else { // TODO: handle signal POST @@ -188,7 +230,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, // metadata handler else if (foundStr == "metadata") { if (httpMethod == "get") { - ret = GetSignalPath(requestId, json, std::move(restTarget)); + ret = GetSignalPath(requestId, json, restTarget); json["action"] = "getMetadata"; } diff --git a/w3c-visserver-api/test/web-client/index.html b/w3c-visserver-api/test/web-client/index.html index f56b7e7..43444ff 100644 --- a/w3c-visserver-api/test/web-client/index.html +++ b/w3c-visserver-api/test/web-client/index.html @@ -44,6 +44,7 @@
+ diff --git a/w3c-visserver-api/test/web-client/rest-client.js b/w3c-visserver-api/test/web-client/rest-client.js index 40aa277..9643b7d 100644 --- a/w3c-visserver-api/test/web-client/rest-client.js +++ b/w3c-visserver-api/test/web-client/rest-client.js @@ -17,10 +17,10 @@ document.getElementById('server-doc-root').value = "vss/api/v1"; // setup event handlers document.getElementById('make-get-request').onclick = function req() {return makeRestRequest('GET');}; +document.getElementById('make-put-request').onclick = function req() {return makeRestRequest("PUT");}; document.getElementById('make-post-request').onclick = function req() {return makeRestRequest("POST");}; document.getElementById('make-auth-request').onclick = function req() {return makeAuthRequest("POST");}; - document.getElementById('client-token').addEventListener('paste', onTokenUpdate); document.getElementById('client-token').addEventListener('keyup', onTokenUpdate); document.getElementById('client-token').addEventListener('change', onTokenUpdate); From 81d3988297f858b5e84f22ab95c53827f128194a Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Fri, 25 Oct 2019 14:03:06 +0200 Subject: [PATCH 27/32] Updated README for VIS-Server and small fixes README.md is updated with configuration data, new REST support and a bit reorganized Removed DEBUG from CMakeLists.txt as its not needed any more Updated command-line parameter descriptions Updated HTTP test page to allow selecting for HTTP or HTTPS connection Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 1 - w3c-visserver-api/README.md | 250 ++++++++++-------- w3c-visserver-api/{ => doc}/pictures/jwt.png | Bin .../{ => doc}/pictures/test1.png | Bin .../{ => doc}/pictures/test2.png | Bin .../{ => doc}/pictures/test3.png | Bin .../{ => doc}/pictures/test4.png | Bin w3c-visserver-api/src/main.cpp | 22 +- w3c-visserver-api/test/web-client/index.html | 2 + .../test/web-client/rest-client.js | 6 +- 10 files changed, 151 insertions(+), 130 deletions(-) rename w3c-visserver-api/{ => doc}/pictures/jwt.png (100%) rename w3c-visserver-api/{ => doc}/pictures/test1.png (100%) rename w3c-visserver-api/{ => doc}/pictures/test2.png (100%) rename w3c-visserver-api/{ => doc}/pictures/test3.png (100%) rename w3c-visserver-api/{ => doc}/pictures/test4.png (100%) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index 198342d..43fcb8e 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -29,7 +29,6 @@ if(UNIT_TEST) add_definitions(-DUNIT_TEST) endif(UNIT_TEST) -add_definitions(-DDEBUG) add_compile_options(-std=c++11 -pthread -Wall -Wextra -Werror) if ("${ADDRESS_SAN}" STREQUAL "ON" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") diff --git a/w3c-visserver-api/README.md b/w3c-visserver-api/README.md index 7b76c04..62d0c0c 100644 --- a/w3c-visserver-api/README.md +++ b/w3c-visserver-api/README.md @@ -2,29 +2,93 @@ The implementation is based on the [W3C Vehicle Information Service Specification](https://www.w3.org/TR/2018/CR-vehicle-information-service-20180213/) +This implementation can also provide additional functionality not (yet) available in (draft) standard documents. -The implementation provides all the major funtionality defined in the above specification and also uses JWT Token for permissions handling with decent amount of uni-tests covering all the basic funtions. This project uses components from other open source projects namely +The implementation provides all the major functionality defined in the above standard specification. and also uses JWT Token for permissions handling with decent amount of unit-tests covering all the basic funtions. -1. [Simple-WebSocket-Server](https://gitlab.com/eidheim/Simple-WebSocket-Server) which is under MIT license. -2. [jsoncons](https://github.com/danielaparker/jsoncons) which is under Boost Software license. -3. [jwt-cpp](https://github.com/Thalhammer/jwt-cpp)which is under MIT license. +## Features + - Multi-client server implementing Web-Socket platform communication, with support for both secure [SSL] and insecure [plain] connections. Feature status: + - User authorization based on industry standard RFC 7519 as JSON Web Tokens + - Optional JSON signing of messages, described in **_JSON signing_** chapter + - Multi-client server implementing experimental REST API based on standard specification. REST API specification is available as OpenAPI 3.0 definition available in [rest-api.yaml](doc/rest-api.yaml) file. + Specific list of of features is listed in table below: -# How to build -w3c-visserver can be built as a library which could be used in another application. Eg: could be found in the vehicle2cloud app. + | Feature | Status | + | ------------- | ------------- | + | GET/SET | :heavy_check_mark:| + | PUB/SUB | :heavy_check_mark: | + | GETMETA | :heavy_check_mark: | + | Secure Web-Socket | :heavy_check_mark: | + | Authentification | :heavy_check_mark: | + | JSON signing | :heavy_check_mark: | + | REST API | :heavy_check_mark: | + +## Dependencies + +This project uses components from other 3rd party open-source projects: + +| Library | License | Description | +| ------------- | ------------- | ----------- | + [Boost.Beast](https://www.boost.org/doc/libs/1_67_0/libs/beast/doc/html/index.html) | Boost Software license 1.0 | Foundation library for simplified handling of various Web-Socket, HTTP or other protocols with and without security, based on Boost.Asio. + [Simple-WebSocket-Server](https://gitlab.com/eidheim/Simple-WebSocket-Server) _[deprecated]_ | MIT license | Simple implementation of Web-Socket server. + [jsoncons](https://github.com/danielaparker/jsoncons) | Boost Software license 1.0 | Utility library for handling JSON. + [jwt-cpp](https://github.com/Thalhammer/jwt-cpp) | MIT license | Utility library for handling JWT tokens. + +# Building W3C-Server + +[CMake](https://cmake.org/) is tool used to configure, build and package W3C-Server. + +## Configure build + +Build configuration options of W3C-Server are defined in CMakeLists.txt file. + +By changing different option values in CMakeLists.txt file, user can control +different build options of W3C-Server. +Available build options with optional parameters, if available, are presented below. Default parameters are shown in **bold**: + - **BUILD_EXE** [**ON**/OFF] - Default build shall produce W3C-Server executable. + If set to **OFF** W3C-Server shall be built as a library which could be used in another application. + Eg: could be found in the _vehicle2cloud_ app. + - **BUILD_TEST_CLIENT** [**ON**/OFF] - Build separate _testclient_ executable. Test client is a utility to test + Web-Socket request interface of W3C-Server and retrieve responses. + - **UNIT_TEST** [ON/**OFF**] - If enabled, build shall produce separate _w3c-unit-test_ executable which + will run existing tests for server implementation. + - **ADDRESS_SAN** [ON/**OFF**] - If enabled and _Clang_ is used as compiler, _AddressSanitizer_ will be used to build + W3C-Server for verifying run-time execution. + +After changing any of build options, new clean build should be made, as described in **_Building W3C-Server_** chapter. + +## Building W3C-Server + +To generate new clean build (e.g. after git clone or after changing build configuration options), use standard CMake build order as shown below: + + - Go to W3C-Server directory ``` cd w3c-visserver-api +``` + - Make default build directory where build artifacts will be stored, and move into it +``` mkdir build cd build +``` + - Invoke CMake pointing to location of CMakeLists.txt file to generate + Makefile build configuration which will be used to build W3C-Server +``` cmake .. -make ``` + - Run build W3C-Server. Make parameter '_-j_ ' is optional and allows running parallel build jobs to speed up compilation. +``` +make -j +``` +If all completes successfully, build artifacts shall be located in 'build' directory. + +### JSON signing JSON Signing has been introduced additionally to sign the JSON response for GET and SUBSCRIBE Response. By default this has been disabled. To enable this feature go to visconf.hpp file and uncomment the line `define JSON_SIGNING_ON`. Please note, JSON signing works only with a valid pair of public / private certificate. For testing, you could create example certificates by following the below steps. Do not add any passphrase when asked for. ``` -ssh-keygen -t rsa -b 4096 -m PEM -f signing.private.key +ssh-keygen -t rsa -b 4096 -m PEM -f signing.private.key openssl rsa -in signing.private.key -pubout -outform PEM -out signing.public.key ``` @@ -34,17 +98,71 @@ The client also needs to validate the signed JSON using the public certificate w This could also be easily extended to support JSON signing for the requests as well with very little effort. +# Running W3C-Server -# How to run -This application needs the input vss data to create the tree structure. The input file can be taken from https://github.com/GENIVI/vehicle_signal_specification/blob/master/vss_rel_1.0.json. Clone the files and place them in the build folder where the executables are built. Keep the names of the files the same. -Add the files to the location where the application executable is run from. +Depending on build options and provided parameters, W3C-Server will provide different features. +Chapter **_Parameters_** shall describe different mandatory and optional parameters in more detail. +Default configuration shall provide both Web-Socket and REST API connectivity. -# How tos +## Web-Socket specific testing + +This covers only the basic functions like get, set and getmetadata requests. You coulkd skip this and take a look at the unit-test to get better idea about the implementation. + +You could also checkout the in-vehicle apps in the [kuksa.apps](https://github.com/eclipse/kuksa.apps) repo which work with the server. + +Now the apps are ready for testing. Run w3c-visserver using `./w3c-visserver` command and then in a separate terminal start testclient using `./testclient`. + +Testclient should connect to the w3c-visserver and promt a message as below +![Alt text](./doc/pictures/test1.png?raw=true "test1") -Demo Certificates are available in the examples/demo-certificates folder and these certs are automatically copied on building the apps and api. In case you need to create new certs follow the steps below, otherwise skip the steps below. +Authenticate with the server using the JWT token +![Alt text](./doc/pictures/test4.png?raw=true "test4") + +Enter the vss path and function as set and a dummy integer value. +![Alt text](./doc/pictures/test2.png?raw=true "test2") + +Enter the same vss path as above and fuction as get. You should receive the previously set value in the JSON response. +![Alt text](./doc/pictures/test3.png?raw=true "test3") + +## REST API specific testing + +There is number of options to exercise REST API. + +_**NOTE:** If using SSL connections and self-signed certificates, make sure that 'CA.pem' or corresponding file to generated certificates is imported into browser (or other tool) used for testing. Also user can try to disable certificate verification. +Reason for this is that browsers automatically try to verify validity of server certificates, so secured connection shall fail with default configuration._ + +Similar to above mentioned testclient, there is available [client test page](./test/web-client/index.html) in git repo to aid testing. +Test page support custom GET, PUT and POST HTTP requests to W3C-Server. Additional benefit is that it can automatically generate JWT token based on input token value and provided Client key which is used in authorization. Note that if users changes Client key, user must also update 'jwt.pub.key' with corresponding public key. + +Additional tool which is quite useful is [Swagger](https://editor.swagger.io). It is a dual-use tool which allows for writing OpenAPI specifications, but also generates runnable REST API samples for moslient test endpoints. +Open Swagger editor and import our REST API [definition](./doc/rest-api.yaml) file. Swagger shall generate HTML page with API documentation. When one of the endpoints is selected, 'try' button appears which allows for making REST requests directly to running W3C-Server. + +## Parameters +Below are presented W3C-Server parameters available for user control: +- **--help** - Show W3C-Server usage and exit +- **--vss** [mandatory] - Path to VSS data file describing VSS data tree structure which W3C-Server shall handle. Sample 'vss_rel_2.0.json' file can be found [here](./unit-test/vss_rel_2.0.json). +- **--config-file** [optional] - Path to configuration file with W3C-Server input parameters. + Configuration file can replace command-line parameters and through different files multiple configurations can be handled more easily (e.g. test and production setup). + Sample of configuration file parameters is shown below: + ``` + vss=vss_rel_2.0.json + cert-path=. + insecure= + log-level=ALL + ``` +- **--cert-path** [mandatory] - Directory path where 'Server.pem', 'Server.key' and 'jwt.pub.key' are located. Server demo certificates are located in [this](https://github.com/eclipse/kuksa.invehicle/tree/master/examples/demo-certificates) directory of git repo. Certificates from 'demo-certificates' are automatically copied to build directory, so invoking '_--cert-path=._' should be enough when demo certificates are used. +If user needs to use or generate their own certificates, see chapter **_Certificates_** for more details. +For authorizing client, file 'jwt.pub.key' contains public key used to verify that JWT authorization token is valid. To generated different 'jwt.pub.key' file, see chapter **_Permissions_** for more details. +- **--insecure** [optional] - By default, W3C-Server shall accept only SSL (TLS) secured connections. If provided, W3C-Server shall also accept plain un-secured connections for Web-Socket and REST API connections, and also shall not fail connections due to self-signed certificates. +- **--wss-server** [optional][deprecated] - By default, W3C-Server uses Boost.Beast as default connection handler. If provided, W3C-Server shall use deprecated Simple Web-Socket Server, without REST API support. +- **--address** [optional] - If provided, W3C-Server shall use different server address than default _'localhost'_. +- **--port** [optional] - If provided, W3C-Server shall use different server port than default '8090' value. +- **--log-level** [optional] - Enable selected log level value. To allow for different log level combinations, parameter can be provided multiple times with different log level values. + +# How tos -### Create PKI certificates +## Certificates Go to examples/demo-certificates folder. Make changes in the openssl.cnf file regarding the Company name and the allowed IPs and DNS server names. Make sure you also add the IPs and DNS to v3.ext file as well. @@ -59,122 +177,28 @@ Go to examples/demo-certificates folder. Make changes in the openssl.cnf file re Steps were taken from [here]( https://kb.op5.com/pages/viewpage.action?pageId=19073746#sthash.GHsaFkZe.WDGgcOja.dpbs) & [here](https://stackoverflow.com/questions/18233835/creating-an-x509-v3-user-certificate-by-signing-csr). -### Build W3C-Server - -Now enable `BUILD_EXE` and `BUILD_TEST_CLIENT` flags by changing to ON in w3cvisserver/CMakeLists.txt. - -Now build using the commands in How to build section. - -Once the apps are built, copy the server.crt and server.key files to the `w3c-visserver/build` folder. Also copy the https://github.com/GENIVI/vehicle_signal_specification/blob/master/vss_rel_1.0.json files into the `w3c-visserver/build` folder. - -In this case the server and the client are built on the same folder, hence copy the generate Server.pem, Server.key ,CA.key, Client.key and Client.pem into `w3c-visserver/build` folder. +## Permissions -#### Permissions +The W3C-Server needs authorization JWT Token to allow access to server side resources. You can create a dummy JWT Token from https://jwt.io/. Use the RSA256 algorithm from the drop down and enter valid "iat" and "exp" data and set "iss : kuksa" and generate a JWT. Once the JWT is generated on the left side. Copy the Public key from the Text box on the right side to a file and rename the field to jwt.pub.key and copy the file to `w3c-visserver/build` folder. Also store the JWT token somewhere so that you could pass the Token to the server for authentication. -The w3c-visserver needs authentification Token to allow access to server side resources. You can create a dummy JWT Token from https://jwt.io/. Use the RSA256 algorithm from the drop down and enter valid "iat" and "exp" data and set "iss : kuksa" and generate a JWT. Once the JWT is generated on the left side. Copy the Public key from the Text box on the right side to a file and rename the field to jwt.pub.key and copy the file to `w3c-visserver/build` folder. Also store the JWT token somewhere so that you could pass the Token to the server for authentication. - -![Alt text](./pictures/jwt.png?raw=true "jwt") +![Alt text](./doc/pictures/jwt.png?raw=true "jwt") Permissions can be granted by modifying the JSON Claims. 1. The JWT Token should contain a "w3c-vss" claim. 2. Under the "w3c-vss" claim the permissions can be granted using key value pair. The key should be the path in the signal tree and the value should be strings with "r" for READ-ONLY, "w" for WRITE-ONLY and "rw" or "wr" for READ-AND-WRITE permission. See the image above. -3. The permissions can contain wild-cards. For eg "Signal.OBD.*" : "rw" will grant READ-WRITE access to all the signals under Signal.OBD. +3. The permissions can contain wild-cards. For eg "Signal.OBD.\*" : "rw" will grant READ-WRITE access to all the signals under Signal.OBD. 4. The permissions can be granted to a branch. For eg "Signal.Vehicle" : "rw" will grant READ-WRITE access to all the signals under Signal.Vehicle branch. -### Test Run - -This covers only the basic functions like get, set and getmetadata requests. You coulkd skip this and take a look at the unit-test to getter idea about the implementation. - -You could also checkout the in-vehicle apps in the [kuksa.apps](https://github.com/eclipse/kuksa.apps) repo which work with the server. - -Now the apps are ready for testing. Run w3c-visserver using `./w3c-visserver` command and then in a separate terminal start testclient using `./testclient`. - -Testclient should connect to the w3c-visserver and promt a message as below -![Alt text](./pictures/test1.png?raw=true "test1") - -Authenticate with the server using the JWT token -![Alt text](./pictures/test4.png?raw=true "test4") - -Enter the vss path and function as set and a dummy integer value. -![Alt text](./pictures/test2.png?raw=true "test2") - -Enter the same vss path as above and fuction as get. You should receive the previously set value in the JSON response. -![Alt text](./pictures/test3.png?raw=true "test3") - - - - - -### JSON Signing - -JSON Signing has been introduced additionally to sign the JSON response for GET and SUBSCRIBE Response. By default this has been disabled. To enable this feature go to visconf.hpp file and uncomment the line `define JSON_SIGNING_ON`. Please note, JSON signing works only with a valid pair of public / private certificate. For testing, you could create example certificates by following the below steps. -Do not add any passphrase when asked for. - -``` -ssh-keygen -t rsa -b 4096 -m PEM -f signing.private.key -openssl rsa -in signing.private.key -pubout -outform PEM -out signing.public.key -``` - -Copy the files signing.private.key & signing.public.key to the build directory. - -The client also needs to validate the signed JSON using the public certificate when JSON signing is enabled in server. - -This could also be easily extended to support JSON signing for the requests as well with very little effort. - - -### D-BUS Backend Connection - -The server also has d-bus connection, which could be used to feed the server with data from various feeders. -The W3C Sever exposes the below methods and these methods could be used (as methodcall) to fill the server with data. - - `` - - - - - - - - - - - - - - - - - - - - - `` - - - -# Implementation - -| Feature | Status | -| ------------- | ------------- | -| GET/SET | :heavy_check_mark:| -| PUB/SUB | :heavy_check_mark: | -| GETMETA | :heavy_check_mark: | -| Secure WebSocket | :heavy_check_mark: | -| Authentification | :heavy_check_mark: | -| JSON signing | :heavy_check_mark: | - ## Running on AGL on Raspberry Pi 3 * Create an AGL image using the instructions in `agl-kuksa` project. * Burn the image on to an SD card and boot the image on a Raspi 3. * w3c-visserver is deployed as a systemd service `w3c-visserver.service` which opens a secure websocket connection on port 8090. - -#### On first launch +### On first launch * ssh into the raspi 3 with root. * Go to `/usr/bin/w3c-visserver` using the ssh connection. * copy the vss data file https://github.com/GENIVI/vehicle_signal_specification/blob/master/vss_rel_1.0.json into `./usr/bin/w3c-visserver`. By default the AGL build will contain demo cerificates that work with other apps in the repo. You could create your own cerificates and tokens using the instaructions above. * Reboot the raspi 3 - diff --git a/w3c-visserver-api/pictures/jwt.png b/w3c-visserver-api/doc/pictures/jwt.png similarity index 100% rename from w3c-visserver-api/pictures/jwt.png rename to w3c-visserver-api/doc/pictures/jwt.png diff --git a/w3c-visserver-api/pictures/test1.png b/w3c-visserver-api/doc/pictures/test1.png similarity index 100% rename from w3c-visserver-api/pictures/test1.png rename to w3c-visserver-api/doc/pictures/test1.png diff --git a/w3c-visserver-api/pictures/test2.png b/w3c-visserver-api/doc/pictures/test2.png similarity index 100% rename from w3c-visserver-api/pictures/test2.png rename to w3c-visserver-api/doc/pictures/test2.png diff --git a/w3c-visserver-api/pictures/test3.png b/w3c-visserver-api/doc/pictures/test3.png similarity index 100% rename from w3c-visserver-api/pictures/test3.png rename to w3c-visserver-api/doc/pictures/test3.png diff --git a/w3c-visserver-api/pictures/test4.png b/w3c-visserver-api/doc/pictures/test4.png similarity index 100% rename from w3c-visserver-api/pictures/test4.png rename to w3c-visserver-api/doc/pictures/test4.png diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index 85b7481..0a800f5 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -25,9 +25,7 @@ #include #include "WsServer.hpp" -#include "VssDatabase.hpp" #include "exception.hpp" - #include "RestV1ApiHandler.hpp" #include "BasicLogger.hpp" #include "Authenticator.hpp" @@ -48,7 +46,7 @@ using jsoncons::json; // Websocket port #define PORT 8090 -VssDatabase* database = NULL; +static VssDatabase* gDatabase = NULL; static GDBusNodeInfo *introspection_data = NULL; @@ -100,7 +98,7 @@ handle_method_call (GDBusConnection *connection, json jsonVal; const gchar *vss_path; - if (database == NULL) { + if (gDatabase == NULL) { g_dbus_method_invocation_return_error (invocation, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED, @@ -158,7 +156,7 @@ handle_method_call (GDBusConnection *connection, // set the data in the db. try { string pathStr(vss_path); - database->setSignal(pathStr, jsonVal); + gDatabase->setSignal(pathStr, jsonVal); } catch (genException &e) { g_dbus_method_invocation_return_error (invocation, G_IO_ERROR, @@ -247,10 +245,10 @@ int main(int argc, const char *argv[]) { "Configuration file path for program parameters. " "Can be provided instead of command line options") ("vss", program_options::value(), "vss_rel*.json file") - ("cert-path", program_options::value(), + ("cert-path", program_options::value()->default_value("."), "Path to directory where 'Server.pem' and 'Server.key' are located") - ("insecure", "Run insecure") - ("wss-server", "Run old WSS server handler") + ("insecure", "Accept plain (no-SSL) connections") + ("wss-server", "Run old WSS server handler instead of Boost.Beast. Note: No REST API support") ("address", program_options::value()->default_value("localhost"), "Address") ("port", program_options::value()->default_value(8090), "Port") ("log-level", program_options::value>(&logLevels)->composing(), @@ -341,12 +339,7 @@ int main(int argc, const char *argv[]) { g_bus_unown_name (owner_id); g_dbus_node_info_unref (introspection_data); - uint8_t logLevelsActive; -#ifdef DEBUG - logLevelsActive = static_cast(LogLevel::ALL); -#else - logLevelsActive = static_cast(LogLevel::INFO & LogLevel::WARNING & LogLevel::ERROR); -#endif + // initialize pseudo random number generator std::srand(std::time(nullptr)); @@ -378,6 +371,7 @@ int main(int argc, const char *argv[]) { auto database = std::make_shared(logger, subHandler, accessCheck); auto cmdProcessor = std::make_shared(logger, database, tokenValidator, subHandler); + gDatabase = database.get(); database->initJsonTree(vss_filename); if (!useNewServer) { diff --git a/w3c-visserver-api/test/web-client/index.html b/w3c-visserver-api/test/web-client/index.html index 43444ff..2b11aed 100644 --- a/w3c-visserver-api/test/web-client/index.html +++ b/w3c-visserver-api/test/web-client/index.html @@ -72,6 +72,8 @@
Server parameters: + HTTP
+ HTTPS
Address:
Port:
Doc root:
diff --git a/w3c-visserver-api/test/web-client/rest-client.js b/w3c-visserver-api/test/web-client/rest-client.js index 9643b7d..3bd4c4a 100644 --- a/w3c-visserver-api/test/web-client/rest-client.js +++ b/w3c-visserver-api/test/web-client/rest-client.js @@ -142,13 +142,14 @@ function makeHttpRequest(type, requestUrl, resourcePath) { } function makeAuthRequest(type) { + let server_type = document.querySelector('input[name="HTTPType"]:checked').value; let server_address = document.getElementById('server-address').value; let server_port = document.getElementById('server-port').value; let doc_root = document.getElementById('server-doc-root').value; let resource = document.getElementById('auth-token-string').value; if (server_address != "" && server_port != "") { - requestUrl = 'http://' + server_address + ':' + server_port + '/' + doc_root + '/'; + requestUrl = server_type + '://' + server_address + ':' + server_port + '/' + doc_root + '/'; makeHttpRequest(type, requestUrl, 'authorize?token=' + resource); } // prevent reloading of page @@ -156,13 +157,14 @@ function makeAuthRequest(type) { } function makeRestRequest(type) { + let server_type = document.querySelector('input[name="HTTPType"]:checked').value; let server_address = document.getElementById('server-address').value; let server_port = document.getElementById('server-port').value; let doc_root = document.getElementById('server-doc-root').value; let resource = document.getElementById('resource-path').value; if (server_address != "" && server_port != "") { - requestUrl = 'http://' + server_address + ':' + server_port + '/' + doc_root + '/'; + requestUrl = server_type + '://' + server_address + ':' + server_port + '/' + doc_root + '/'; makeHttpRequest(type, requestUrl, resource); } // prevent reloadig of page From 7eb1a0b38aa9301eb2e4529b2f37d271e77bd041 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Tue, 29 Oct 2019 10:14:46 +0100 Subject: [PATCH 28/32] Add program parameter for keycloak --use-keycloak parameter added to enable keycloak permission handling Disabled if not provided Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/README.md | 29 ++++++++++++++++++++++ w3c-visserver-api/src/main.cpp | 44 +++++++++++++++++----------------- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/w3c-visserver-api/README.md b/w3c-visserver-api/README.md index 62d0c0c..f6a605d 100644 --- a/w3c-visserver-api/README.md +++ b/w3c-visserver-api/README.md @@ -190,6 +190,35 @@ Permissions can be granted by modifying the JSON Claims. 3. The permissions can contain wild-cards. For eg "Signal.OBD.\*" : "rw" will grant READ-WRITE access to all the signals under Signal.OBD. 4. The permissions can be granted to a branch. For eg "Signal.Vehicle" : "rw" will grant READ-WRITE access to all the signals under Signal.Vehicle branch. +## D-BUS Backend Connection + +The server also has d-bus connection, which could be used to feed the server with data from various feeders. +The W3C Sever exposes the below methods and these methods could be used (as methodcall) to fill the server with data. + ``` + + + + + + + + + + + + + + + + + + + + + + + ``` + ## Running on AGL on Raspberry Pi 3 * Create an AGL image using the instructions in `agl-kuksa` project. diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index 0a800f5..cdd56e2 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -248,6 +248,7 @@ int main(int argc, const char *argv[]) { ("cert-path", program_options::value()->default_value("."), "Path to directory where 'Server.pem' and 'Server.key' are located") ("insecure", "Accept plain (no-SSL) connections") + ("use-keycloak", "Use KeyCloak for permission management") ("wss-server", "Run old WSS server handler instead of Boost.Beast. Note: No REST API support") ("address", program_options::value()->default_value("localhost"), "Address") ("port", program_options::value()->default_value(8090), "Port") @@ -316,29 +317,28 @@ int main(int argc, const char *argv[]) { auto vss_filename = variables["vss"].as(); auto useNewServer = !variables.count("wss-server"); - // Start D-Bus backend connection. - guint owner_id; - GMainLoop *loop; - - introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); - g_assert (introspection_data != NULL); + if (variables.count("use-keycloak")) { + // Start D-Bus backend connection. + guint owner_id; + GMainLoop *loop; + + introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); + g_assert (introspection_data != NULL); + + owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, + "org.eclipse.kuksa.w3cbackend", + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); - - owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, - "org.eclipse.kuksa.w3cbackend", - G_BUS_NAME_OWNER_FLAGS_NONE, - on_bus_acquired, - on_name_acquired, - on_name_lost, - NULL, - NULL); - - - loop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (loop); - g_bus_unown_name (owner_id); - g_dbus_node_info_unref (introspection_data); - + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + g_bus_unown_name (owner_id); + g_dbus_node_info_unref (introspection_data); + } // initialize pseudo random number generator std::srand(std::time(nullptr)); From 6a9af81c918411d05fc6f5352fdd43f72be6b688 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Tue, 29 Oct 2019 12:52:37 +0100 Subject: [PATCH 29/32] Updates of compatibility with browsers for REST API Some browsers and/or different environments can generate CORS pre-flight checks on API by issuing OPTIONS request. Now we return correct OPTIONS response with supported actions on given REST endpoint. JSON responses are now evaluated and proper HTTP status number returned Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/src/RestV1ApiHandler.cpp | 46 +++++++++-- .../src/WebSockHttpFlexServer.cpp | 81 ++++++++++++------- .../test/web-client/rest-client.js | 3 +- w3c-visserver-api/unit-test/w3cunittest.cpp | 2 +- 4 files changed, 91 insertions(+), 41 deletions(-) diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp index 547a045..5950dc8 100644 --- a/w3c-visserver-api/src/RestV1ApiHandler.cpp +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -28,7 +28,7 @@ RestV1ApiHandler::RestV1ApiHandler(std::shared_ptr loggerUtil, std::str docRoot_(docRoot) { // Supported HTTP methods - regexHttpMethods_ = "^(GET|POST)"; + regexHttpMethods_ = "^(GET|POST|PUT|OPTIONS)"; // Resource strings for REST API hooks. Order must match order in RestV1ApiHandler::Resources enum resourceHandleNames_ = std::vector{std::string{"signals"}, @@ -180,8 +180,16 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, if (verifyPathAndStrip(restTarget, foundStr)) { // ////// // signals handler - if (foundStr == "signals") { - if (httpMethod == "get") { + if (foundStr.compare("signals") == 0) { + // handler CORS pre-flight requests from browsers + if (httpMethod.compare("options") == 0) { + json["action"] = "options"; + json["methods"] = "PUT, GET, OPTIONS"; + json["headers"] = "X-PINGOTHER, Content-Type"; + json["max-age"] = "86400"; + json["origin"] = "*"; + } + else if (httpMethod.compare("get") == 0) { if (GetSignalPath(requestId, json, restTarget)) { json["action"] = "get"; } @@ -191,9 +199,10 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, json["action"].as_string(), "Invalid path", json); + ret = false; } } - else if (httpMethod == "put") { + else if (httpMethod.compare("put") == 0) { if (GetSignalPath(requestId, json, restTarget)) { json["action"] = "set"; std::string queryStr("?value="); @@ -215,6 +224,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, json["action"].as_string(), "Invalid path", json); + ret = false; } } else { @@ -222,14 +232,23 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, JsonResponses::malFormedRequest( requestId, json["action"].as_string(), - "POST method not yet supported for 'signals' resource", + "HTTP method not yet supported for 'signals' resource", json); + ret = false; } } // ////// // metadata handler - else if (foundStr == "metadata") { - if (httpMethod == "get") { + else if (foundStr.compare("metadata") == 0) { + // handler CORS pre-flight requests from browsers + if (httpMethod.compare("options") == 0) { + json["action"] = "options"; + json["methods"] = "GET, OPTIONS"; + json["headers"] = "X-PINGOTHER, Content-Type"; + json["max-age"] = "86400"; + json["origin"] = "*"; + } + else if (httpMethod.compare("get") == 0) { ret = GetSignalPath(requestId, json, restTarget); json["action"] = "getMetadata"; @@ -241,12 +260,21 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, json["action"].as_string(), "POST method not supported for 'metadata' resource", json); + ret = false; } } // ////// // authorize handler else if (foundStr == "authorize") { - if (httpMethod == "post") { + // handler CORS pre-flight requests from browsers + if (httpMethod.compare("options") == 0) { + json["action"] = "options"; + json["methods"] = "POST, OPTIONS"; + json["headers"] = "X-PINGOTHER, Content-Type"; + json["max-age"] = "86400"; + json["origin"] = "*"; + } + else if (httpMethod == "post") { std::string tokenParam("?token="); if (verifyPathAndStrip(restTarget, tokenParam)) { const std::regex regToken(regexToken_); @@ -263,6 +291,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, json["action"].as_string(), "Token for 'authorize' not valid", json); + ret = false; } } else { @@ -271,6 +300,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, json["action"].as_string(), "Parameters for 'authorize' not valid", json); + ret = false; } } else { diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index 688d45a..99defd2 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "detect_ssl.hpp" @@ -800,39 +801,59 @@ namespace { std::string jsonRequest; // if we got correct JSON request back, send it to handler - if (restHandler->GetJson(std::string(req_.method_string()), - std::string(req_.target()), - jsonRequest)) { - auto response = requestHandler_(jsonRequest, channel); - // Respond to GET request - http::response res{ - std::piecewise_construct, - std::make_tuple(response), - std::make_tuple(http::status::ok, req_.version())}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "application/json"); - // allow cross-domain calls with setting below header - // TODO: evaluate this header for production level SW - res.set(http::field::access_control_allow_origin, "*"); - res.content_length(response.size()); - res.keep_alive(req_.keep_alive()); - queue_(std::move(res)); + auto res = restHandler->GetJson(std::string(req_.method_string()), + std::string(req_.target()), + jsonRequest); + + const std::regex getErrNumber("\"number\":\\ +(\\d+)"); + std::smatch sm; + std::string response; + + http::response httpResponse{ + std::piecewise_construct, + std::make_tuple(""), + std::make_tuple(http::status::ok, req_.version())}; + + // check if it is OPTIONS request to support CORS pre-flight checks from browsers + if (req_.method_string().compare("OPTIONS") != 0) { + if (res) { + response = requestHandler_(jsonRequest, channel); + // check if there was error so we can set correct HTTP response status + std::regex_search (response, sm, getErrNumber); + if (sm.size()) { + // set error response + httpResponse.result(std::stoi(sm.str(1))); + } + httpResponse.body() = response; + } + else { + // handle error + httpResponse.body() = jsonRequest; + // check if there was error so we can set correct HTTP response status + std::regex_search (jsonRequest, sm, getErrNumber); + if (sm.size()) { + // set error response + httpResponse.result(std::stoi(sm.str(1))); + } + } } else { - // write jsonRequest back as error response - auto const bad_request = - [=](std::string why) - { - http::response res{http::status::bad_request, req_.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "application/json"); - res.keep_alive(req_.keep_alive()); - res.body() = why; - res.prepare_payload(); - return res; - }; - queue_(bad_request(jsonRequest)); + auto jsonReq = jsoncons::json::parse(jsonRequest); + httpResponse.set(http::field::access_control_allow_methods, jsonReq["methods"].as_string()); + httpResponse.set(http::field::access_control_allow_headers, jsonReq["headers"].as_string()); + httpResponse.set(http::field::access_control_max_age, jsonReq["max-age"].as_string()); } + // Respond to request + httpResponse.set(http::field::server, BOOST_BEAST_VERSION_STRING); + httpResponse.set(http::field::content_type, "application/json"); + + // allow cross-domain calls with setting below header + // TODO: evaluate this header for production level SW + httpResponse.set(http::field::access_control_allow_origin, "*"); + + httpResponse.keep_alive(req_.keep_alive()); + httpResponse.prepare_payload(); + queue_(std::move(httpResponse)); // If we aren't at the queue limit, try to pipeline another request if(! queue_.is_full()) diff --git a/w3c-visserver-api/test/web-client/rest-client.js b/w3c-visserver-api/test/web-client/rest-client.js index 3bd4c4a..5c13027 100644 --- a/w3c-visserver-api/test/web-client/rest-client.js +++ b/w3c-visserver-api/test/web-client/rest-client.js @@ -16,7 +16,7 @@ document.getElementById('server-port').value = 8090; document.getElementById('server-doc-root').value = "vss/api/v1"; // setup event handlers -document.getElementById('make-get-request').onclick = function req() {return makeRestRequest('GET');}; +document.getElementById('make-get-request').onclick = function req() {return makeRestRequest("GET");}; document.getElementById('make-put-request').onclick = function req() {return makeRestRequest("PUT");}; document.getElementById('make-post-request').onclick = function req() {return makeRestRequest("POST");}; document.getElementById('make-auth-request').onclick = function req() {return makeAuthRequest("POST");}; @@ -124,7 +124,6 @@ function makeHttpRequest(type, requestUrl, resourcePath) { requestObj = new XMLHttpRequest(); var completeUrl = requestUrl + resourcePath; requestObj.open(type, completeUrl, true); - addReqToLog(type, resourcePath); // callback handler when positive response received diff --git a/w3c-visserver-api/unit-test/w3cunittest.cpp b/w3c-visserver-api/unit-test/w3cunittest.cpp index 164297a..c9b2d99 100755 --- a/w3c-visserver-api/unit-test/w3cunittest.cpp +++ b/w3c-visserver-api/unit-test/w3cunittest.cpp @@ -48,7 +48,7 @@ using namespace std; "w3c-vss": { "Signal.OBD.*": "rw", "Signal.Chassis.Axle.*": "rw", - "Signal.Drivetrain.*": "rw", + "Vehicle.Drivetrain.*": "rw", "Signal.Cabin.Infotainment.*": "rw" } } From 19c6949afd4252899cd44b8f6e4c2ab351b4055e Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Wed, 30 Oct 2019 07:14:30 +0100 Subject: [PATCH 30/32] Stability improvement for malformed requests for all connection types Prevent un-handled exceptions when malformed inputs are evaluated or validated. Small clang warnings fixed Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/doc/rest-api.yaml | 86 ++++++++++++++++++- w3c-visserver-api/include/exception.hpp | 12 +++ w3c-visserver-api/src/Authenticator.cpp | 42 +++++---- w3c-visserver-api/src/RestV1ApiHandler.cpp | 5 +- w3c-visserver-api/src/VssDatabase.cpp | 44 +++++++--- .../src/WebSockHttpFlexServer.cpp | 37 ++++++-- w3c-visserver-api/src/main.cpp | 12 +-- .../test/web-client/rest-client.js | 2 +- 8 files changed, 187 insertions(+), 53 deletions(-) diff --git a/w3c-visserver-api/doc/rest-api.yaml b/w3c-visserver-api/doc/rest-api.yaml index 9f35fe0..be5ddf7 100644 --- a/w3c-visserver-api/doc/rest-api.yaml +++ b/w3c-visserver-api/doc/rest-api.yaml @@ -115,6 +115,27 @@ components: schema: $ref: '#/components/schemas/BadRequestContent' + UnauthorizedRsp: + description: "Provided JWT token could not be authorized" + content: + application/json: + schema: + $ref: '#/components/schemas/UnauthorizedContent' + + ForbiddenRsp: + description: "Access to resource is forbidden" + content: + application/json: + schema: + $ref: '#/components/schemas/ForbiddenContent' + + NotFoundRsp: + description: "Requested resource not found" + content: + application/json: + schema: + $ref: '#/components/schemas/NotFoundContent' + schemas: Action: @@ -170,7 +191,7 @@ components: number: type: integer description: "HTML status code" - example: 400 + example: 400, 401, 403, 404 reason: type: string description: "Error reason" @@ -188,6 +209,41 @@ components: timestamp: $ref: '#/components/schemas/Timestamp' + UnauthorizedContent: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + error: + $ref: '#/components/schemas/ErrorDesc' + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' + + ForbiddenContent: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + error: + $ref: '#/components/schemas/ErrorDesc' + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' + + NotFoundContent: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + error: + $ref: '#/components/schemas/ErrorDesc' + requestId: + $ref: '#/components/schemas/RequestId' + timestamp: + $ref: '#/components/schemas/Timestamp' paths: /signals/{signalBranchPathWithDots}: get: @@ -205,6 +261,10 @@ paths: $ref: '#/components/responses/Success' '400': $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' /signals/{signalBranchPathWithSlashes}: get: @@ -222,6 +282,10 @@ paths: $ref: '#/components/responses/Success' '400': $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' /signals/{signalBranchPathWithDots}.{signalId}: get: @@ -238,6 +302,11 @@ paths: $ref: '#/components/responses/Success' '400': $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' + put: tags: - signals @@ -253,6 +322,10 @@ paths: $ref: '#/components/responses/Success' '400': $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' /signals/{signalBranchPathWithSlashes}/{signalId}: get: @@ -269,6 +342,11 @@ paths: $ref: '#/components/responses/Success' '400': $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' + put: tags: - signals @@ -284,6 +362,10 @@ paths: $ref: '#/components/responses/Success' '400': $ref: '#/components/responses/BadRequestRsp' + '403': + $ref: '#/components/responses/ForbiddenRsp' + '404': + $ref: '#/components/responses/NotFoundRsp' /metadata/{signalBranchPathWithDots}: get: @@ -367,3 +449,5 @@ paths: $ref: '#/components/responses/SuccessAuthorize' '400': $ref: '#/components/responses/BadRequestRsp' + '401': + $ref: '#/components/responses/UnauthorizedRsp' diff --git a/w3c-visserver-api/include/exception.hpp b/w3c-visserver-api/include/exception.hpp index aeb3af2..6db6ecf 100644 --- a/w3c-visserver-api/include/exception.hpp +++ b/w3c-visserver-api/include/exception.hpp @@ -63,4 +63,16 @@ class outOfBoundException : public std::exception { virtual const char* what() const throw() { return message.c_str(); } }; + +// value out of bound exception +class notValidException : public std::exception { + private: + std::string message; + + public: + notValidException(std::string msg) { message = msg; } + + virtual const char* what() const throw() { return message.c_str(); } +}; + #endif diff --git a/w3c-visserver-api/src/Authenticator.cpp b/w3c-visserver-api/src/Authenticator.cpp index 705e811..ffbc71d 100644 --- a/w3c-visserver-api/src/Authenticator.cpp +++ b/w3c-visserver-api/src/Authenticator.cpp @@ -46,27 +46,35 @@ void Authenticator::updatePubKey(string key) { // utility method to validate token. int Authenticator::validateToken(WsChannel& channel, string authToken) { - auto decoded = jwt::decode(authToken); json claims; - (void) channel; - for (auto& e : decoded.get_payload_claims()) { - logger->Log(LogLevel::INFO, e.first + " = " + e.second.to_json().to_str()); - claims[e.first] = e.second.to_json().to_str(); - } - - auto verifier = jwt::verify().allow_algorithm( - jwt::algorithm::rs256(pubkey, "", "", "")); + int ttl = -1; + try { - verifier.verify(decoded); - } catch (const std::runtime_error& e) { + auto decoded = jwt::decode(authToken); + + for (auto& e : decoded.get_payload_claims()) { + logger->Log(LogLevel::INFO, e.first + " = " + e.second.to_json().to_str()); + claims[e.first] = e.second.to_json().to_str(); + } + + auto verifier = jwt::verify().allow_algorithm( + jwt::algorithm::rs256(pubkey, "", "", "")); + try { + verifier.verify(decoded); + } catch (const std::runtime_error& e) { + logger->Log(LogLevel::ERROR, "Authenticator::validate: " + string(e.what()) + + " Exception occurred while authentication. Token is not valid!"); + return -1; + } + + channel.setAuthorized(true); + channel.setAuthToken(authToken); + ttl = claims["exp"].as(); + } + catch (std::exception &e) { logger->Log(LogLevel::ERROR, "Authenticator::validate: " + string(e.what()) - + " Exception occured while authentication. Token is not valid!"); - return -1; + + " Exception occurred while decoding token!"); } - - int ttl = claims["exp"].as(); - channel.setAuthorized(true); - channel.setAuthToken(authToken); return ttl; } diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp index 5950dc8..8e55760 100644 --- a/w3c-visserver-api/src/RestV1ApiHandler.cpp +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -168,6 +168,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, if (sm.size()) { std::string httpMethod = sm.str(1); boost::algorithm::to_lower(httpMethod); + json["action"] = httpMethod; if (verifyPathAndStrip(restTarget, docRoot_)) { const std::regex regResources(regexResources_, std::regex_constants::icase); @@ -314,7 +315,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, } } else { - JsonResponses::malFormedRequest( + JsonResponses::pathNotFound( requestId, json["action"].as_string(), "Requested resource do not exist", @@ -333,7 +334,7 @@ bool RestV1ApiHandler::GetJson(std::string&& restMethod, } else { - JsonResponses::malFormedRequest( + JsonResponses::pathNotFound( requestId, json["action"].as_string(), "Requested resource do not exist", diff --git a/w3c-visserver-api/src/VssDatabase.cpp b/w3c-visserver-api/src/VssDatabase.cpp index 11c25c8..9417fd6 100644 --- a/w3c-visserver-api/src/VssDatabase.cpp +++ b/w3c-visserver-api/src/VssDatabase.cpp @@ -29,13 +29,29 @@ using namespace jsoncons::jsonpath; using jsoncons::json; namespace { + // Check if the value can be converted to requested output type + template + void ValidateValue(std::shared_ptr logger, const jsoncons::json &val, OutType &outVal) { + try{ + outVal = val.as(); + } + catch (exception &e) { + std::stringstream msg; + msg << "The input value '" + val.as_string() + "' is not valid!"; + logger->Log(LogLevel::ERROR, "VssDatabase::setSignal: " + msg.str()); + + throw notValidException(msg.str()); + } + } + // Check the value type and if the value is within the range void checkTypeAndBound(std::shared_ptr logger, string value_type, jsoncons::json val) { bool typeValid = false; if (value_type == "uint8") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; @@ -45,10 +61,10 @@ namespace { throw outOfBoundException(msg.str()); } - } else if (value_type == "uint16") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; @@ -58,10 +74,10 @@ namespace { throw outOfBoundException(msg.str()); } - } else if (value_type == "uint32") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; @@ -71,10 +87,10 @@ namespace { throw outOfBoundException(msg.str()); } - } else if (value_type == "int8") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; @@ -86,7 +102,8 @@ namespace { } } else if (value_type == "int16") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; @@ -96,10 +113,10 @@ namespace { throw outOfBoundException(msg.str()); } - } else if (value_type == "int32") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); if (!((longDoubleVal <= numeric_limits::max()) && (longDoubleVal >= numeric_limits::min()))) { std::stringstream msg; @@ -111,7 +128,8 @@ namespace { } } else if (value_type == "float") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); float max = numeric_limits::max(); float min = numeric_limits::lowest(); if (!((longDoubleVal <= max) && (longDoubleVal >= min))) { @@ -124,7 +142,8 @@ namespace { } } else if (value_type == "double") { typeValid = true; - long double longDoubleVal = val.as(); + long double longDoubleVal; + ValidateValue(logger, val, longDoubleVal); double max = numeric_limits::max(); double min = numeric_limits::lowest(); if (!((longDoubleVal <= max) && (longDoubleVal >= min))) { @@ -135,7 +154,6 @@ namespace { throw outOfBoundException(msg.str()); } - } else if (value_type == "boolean") { typeValid = true; } else if (value_type == "string") { diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index 99defd2..3f5daef 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -805,20 +805,24 @@ namespace { std::string(req_.target()), jsonRequest); + // regex for extracting HTTP status code from error response const std::regex getErrNumber("\"number\":\\ +(\\d+)"); std::smatch sm; std::string response; + // HTTP response object http::response httpResponse{ std::piecewise_construct, std::make_tuple(""), std::make_tuple(http::status::ok, req_.version())}; - // check if it is OPTIONS request to support CORS pre-flight checks from browsers + // HTTP 'OPTIONS' method is handled differently to support CORS pre-flight checks from browsers if (req_.method_string().compare("OPTIONS") != 0) { + // if all ok, process further generated JSON request if (res) { response = requestHandler_(jsonRequest, channel); - // check if there was error so we can set correct HTTP response status + + // check if error was returned so we can set correct HTTP response status std::regex_search (response, sm, getErrNumber); if (sm.size()) { // set error response @@ -826,24 +830,37 @@ namespace { } httpResponse.body() = response; } + // if getting of JSON request failed, set error HTTP response else { - // handle error - httpResponse.body() = jsonRequest; // check if there was error so we can set correct HTTP response status std::regex_search (jsonRequest, sm, getErrNumber); if (sm.size()) { // set error response httpResponse.result(std::stoi(sm.str(1))); } + httpResponse.body() = jsonRequest; } } + // handle HTTP 'OPTIONS' method else { - auto jsonReq = jsoncons::json::parse(jsonRequest); - httpResponse.set(http::field::access_control_allow_methods, jsonReq["methods"].as_string()); - httpResponse.set(http::field::access_control_allow_headers, jsonReq["headers"].as_string()); - httpResponse.set(http::field::access_control_max_age, jsonReq["max-age"].as_string()); + // if all ok, prepare OPTIONS response + if (res) { + auto jsonReq = jsoncons::json::parse(jsonRequest); + httpResponse.set(http::field::access_control_allow_methods, jsonReq["methods"].as_string()); + httpResponse.set(http::field::access_control_allow_headers, jsonReq["headers"].as_string()); + httpResponse.set(http::field::access_control_max_age, jsonReq["max-age"].as_string()); + } + else { + // handle error + httpResponse.body() = jsonRequest; + // check if there was error so we can set correct HTTP response status + std::regex_search (jsonRequest, sm, getErrNumber); + if (sm.size()) { + // set error response + httpResponse.result(std::stoi(sm.str(1))); + } + } } - // Respond to request httpResponse.set(http::field::server, BOOST_BEAST_VERSION_STRING); httpResponse.set(http::field::content_type, "application/json"); @@ -853,6 +870,8 @@ namespace { httpResponse.keep_alive(req_.keep_alive()); httpResponse.prepare_payload(); + + // add response object in transmit queue queue_(std::move(httpResponse)); // If we aren't at the queue limit, try to pipeline another request diff --git a/w3c-visserver-api/src/main.cpp b/w3c-visserver-api/src/main.cpp index cdd56e2..3031a9f 100644 --- a/w3c-visserver-api/src/main.cpp +++ b/w3c-visserver-api/src/main.cpp @@ -96,7 +96,7 @@ handle_method_call (GDBusConnection *connection, (void) sender; json jsonVal; - const gchar *vss_path; + const gchar *vss_path = NULL; if (gDatabase == NULL) { g_dbus_method_invocation_return_error (invocation, @@ -176,15 +176,7 @@ static const GDBusInterfaceVTable interface_vtable = NULL, NULL, /* Padding for future expansion */ - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - + {NULL} }; static void diff --git a/w3c-visserver-api/test/web-client/rest-client.js b/w3c-visserver-api/test/web-client/rest-client.js index 5c13027..634d570 100644 --- a/w3c-visserver-api/test/web-client/rest-client.js +++ b/w3c-visserver-api/test/web-client/rest-client.js @@ -95,7 +95,7 @@ function addRespToLog(htmlStatus, message) { type = 'ERROR: HTML status = ' + htmlStatus; // if no response if (message == '') { - message = 'No response, check server status'; + message = 'No response: Check server status or if requested HTTP method is supported by VIS Server'; } } var newListItem = document.createElement("LI"); From 495babc49b76d23e16b31cca1bdb88181ac087a9 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Fri, 1 Nov 2019 07:58:02 +0100 Subject: [PATCH 31/32] Fix for subscription notifications Value of connection id was truncated, so no response was sent on wire Signed-off-by: Miladinovic Bojan --- w3c-visserver-api/CMakeLists.txt | 2 -- w3c-visserver-api/include/SubscriptionHandler.hpp | 6 ++++-- w3c-visserver-api/src/SubscriptionHandler.cpp | 3 ++- w3c-visserver-api/src/VssDatabase.cpp | 4 ---- w3c-visserver-api/src/WebSockHttpFlexServer.cpp | 4 ++-- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/w3c-visserver-api/CMakeLists.txt b/w3c-visserver-api/CMakeLists.txt index 43fcb8e..894d39f 100644 --- a/w3c-visserver-api/CMakeLists.txt +++ b/w3c-visserver-api/CMakeLists.txt @@ -141,8 +141,6 @@ target_include_directories(w3c-unit-test INTERFACE ${OPENSSL_INCLUDE_DIR}) target_link_libraries(w3c-unit-test INTERFACE ${OPENSSL_LIBRARIES}) endif(UNIT_TEST) - - if(BUILD_TEST_CLIENT) add_executable(testclient ${CMAKE_CURRENT_SOURCE_DIR}/test/testclient.cpp) target_link_libraries(testclient simple-websocket-server) diff --git a/w3c-visserver-api/include/SubscriptionHandler.hpp b/w3c-visserver-api/include/SubscriptionHandler.hpp index 23512c7..e033240 100644 --- a/w3c-visserver-api/include/SubscriptionHandler.hpp +++ b/w3c-visserver-api/include/SubscriptionHandler.hpp @@ -36,8 +36,10 @@ class WsChannel; class WsServer; class ILogger; +using SubConnId = uint64_t; + // Subscription ID: Client ID -typedef std::unordered_map subscriptions_t; +typedef std::unordered_map subscriptions_t; // Subscription UUID typedef std::string uuid_t; @@ -52,7 +54,7 @@ class SubscriptionHandler : public ISubscriptionHandler { std::mutex subMutex; std::thread subThread; bool threadRun; - std::queue> buffer; + std::queue> buffer; public: SubscriptionHandler(std::shared_ptr loggerUtil, diff --git a/w3c-visserver-api/src/SubscriptionHandler.cpp b/w3c-visserver-api/src/SubscriptionHandler.cpp index 78f9170..487897a 100644 --- a/w3c-visserver-api/src/SubscriptionHandler.cpp +++ b/w3c-visserver-api/src/SubscriptionHandler.cpp @@ -129,7 +129,7 @@ int SubscriptionHandler::updateByUUID(const string &signalUUID, for (auto subID : handle->second) { std::lock_guard lock(subMutex); - pair newSub; + pair newSub; newSub = std::make_pair(subID.second, value); buffer.push(newSub); } @@ -159,6 +159,7 @@ void* SubscriptionHandler::subThreadRunner() { buffer.pop(); auto connId = newSub.first; + jsoncons::json value = newSub.second; jsoncons::json answer; diff --git a/w3c-visserver-api/src/VssDatabase.cpp b/w3c-visserver-api/src/VssDatabase.cpp index 9417fd6..c8ce666 100644 --- a/w3c-visserver-api/src/VssDatabase.cpp +++ b/w3c-visserver-api/src/VssDatabase.cpp @@ -532,10 +532,8 @@ void VssDatabase::setSignal(WsChannel& channel, for (size_t i = 0; i < setValues.size(); i++) { jsoncons::json item = setValues[i]; string jPath = item["path"].as(); -#ifdef DEBUG logger->Log(LogLevel::VERBOSE, "vssdatabase::setSignal: path found = " + jPath); logger->Log(LogLevel::VERBOSE, "value to set asstring = " + item["value"].as()); -#endif rwMutex.lock(); jsoncons::json resArray = json_query(data_tree, jPath); rwMutex.unlock(); @@ -551,9 +549,7 @@ void VssDatabase::setSignal(WsChannel& channel, rwMutex.lock(); json_replace(data_tree, jPath, resJson); rwMutex.unlock(); -#ifdef DEBUG logger->Log(LogLevel::VERBOSE, "vssdatabase::setSignal: new value set at path " + jPath); -#endif string uuid = resJson["uuid"].as(); diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index 3f5daef..3f101a5 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -95,7 +96,6 @@ namespace { newChannel->setConnID(reinterpret_cast(session)); newChannel->setType(WsChannel::Type::WEBSOCKET_PLAIN); connPlainWebSock_[session] = newChannel; - return *newChannel; } @@ -199,7 +199,7 @@ namespace { std::shared_ptr logger; std::shared_ptr restHandler; - const unsigned DEFAULT_TIMEOUT_VALUE = 60u; // in seconds + const unsigned DEFAULT_TIMEOUT_VALUE = std::numeric_limits::max(); // in seconds const unsigned WEBSOCKET_TIMEOUT_VALUE = DEFAULT_TIMEOUT_VALUE; const unsigned HTTP_TIMEOUT_VALUE = DEFAULT_TIMEOUT_VALUE; From 6427df0e58321ef30b2a29d71829cd2de70cd1c3 Mon Sep 17 00:00:00 2001 From: Miladinovic Bojan Date: Tue, 5 Nov 2019 09:33:49 +0100 Subject: [PATCH 32/32] Improved handling of multiple connections and small refactor Now server is able to handle corrently when multiple write requests are made to client. Tested with parallel Web-Socket and REST connections with subscribed clients. Small update of code based on pull request comments. Signed-off-by: Miladinovic Bojan --- .../include/ISubscriptionHandler.hpp | 6 ++-- .../include/SubscriptionHandler.hpp | 9 ++--- w3c-visserver-api/src/RestV1ApiHandler.cpp | 13 +++++++- w3c-visserver-api/src/SubscriptionHandler.cpp | 13 ++++---- w3c-visserver-api/src/VssDatabase.cpp | 3 ++ .../src/WebSockHttpFlexServer.cpp | 33 +++++++++++++++++-- 6 files changed, 60 insertions(+), 17 deletions(-) diff --git a/w3c-visserver-api/include/ISubscriptionHandler.hpp b/w3c-visserver-api/include/ISubscriptionHandler.hpp index 9c846c1..03d2107 100644 --- a/w3c-visserver-api/include/ISubscriptionHandler.hpp +++ b/w3c-visserver-api/include/ISubscriptionHandler.hpp @@ -24,6 +24,8 @@ class WsServer; class IVssDatabase; class IServer; +using SubscriptionId = uint32_t; + class ISubscriptionHandler { public: virtual ~ISubscriptionHandler() {} @@ -31,8 +33,8 @@ class ISubscriptionHandler { virtual uint64_t subscribe(WsChannel& channel, std::shared_ptr db, const std::string &path) = 0; - virtual int unsubscribe(uint32_t subscribeID) = 0; - virtual int unsubscribeAll(uint32_t connectionID) = 0; + virtual int unsubscribe(SubscriptionId subscribeID) = 0; + virtual int unsubscribeAll(SubscriptionId connectionID) = 0; virtual int updateByUUID(const std::string &signalUUID, const jsoncons::json &value) = 0; virtual int updateByPath(const std::string &path, const jsoncons::json &value) = 0; virtual std::shared_ptr getServer() = 0; diff --git a/w3c-visserver-api/include/SubscriptionHandler.hpp b/w3c-visserver-api/include/SubscriptionHandler.hpp index e033240..7233b52 100644 --- a/w3c-visserver-api/include/SubscriptionHandler.hpp +++ b/w3c-visserver-api/include/SubscriptionHandler.hpp @@ -36,10 +36,11 @@ class WsChannel; class WsServer; class ILogger; + using SubConnId = uint64_t; // Subscription ID: Client ID -typedef std::unordered_map subscriptions_t; +using subscriptions_t = std::unordered_map; // Subscription UUID typedef std::string uuid_t; @@ -54,7 +55,7 @@ class SubscriptionHandler : public ISubscriptionHandler { std::mutex subMutex; std::thread subThread; bool threadRun; - std::queue> buffer; + std::queue> buffer; public: SubscriptionHandler(std::shared_ptr loggerUtil, @@ -66,8 +67,8 @@ class SubscriptionHandler : public ISubscriptionHandler { uint64_t subscribe(WsChannel& channel, std::shared_ptr db, const std::string &path); - int unsubscribe(uint32_t subscribeID); - int unsubscribeAll(uint32_t connectionID); + int unsubscribe(SubscriptionId subscribeID); + int unsubscribeAll(SubscriptionId connectionID); int updateByUUID(const std::string &signalUUID, const jsoncons::json &value); int updateByPath(const std::string &path, const jsoncons::json &value); std::shared_ptr getServer(); diff --git a/w3c-visserver-api/src/RestV1ApiHandler.cpp b/w3c-visserver-api/src/RestV1ApiHandler.cpp index 8e55760..8908e78 100644 --- a/w3c-visserver-api/src/RestV1ApiHandler.cpp +++ b/w3c-visserver-api/src/RestV1ApiHandler.cpp @@ -78,10 +78,19 @@ bool RestV1ApiHandler::GetSignalPath(uint32_t requestId, if (restTarget.size() && verifyPathAndStrip(restTarget, restDelimiter)) { while (restTarget.size()) { // we only accept clean printable characters - const std::regex regexValidWord("^([A-Za-z]+)"); + const std::regex regexValidWord("^([A-Za-z0-9]+)"); if (std::regex_search(restTarget, sm, regexValidWord)) { foundStr = sm.str(1); + if (foundStr.size() == 0) { + JsonResponses::malFormedRequest( + requestId, + json["action"].as_string(), + "Signal path not valid", + json); + ret = false; + break; + } signalPath += foundStr; if (verifyPathAndStrip(restTarget, foundStr)) { if ((restTarget.size() == 0)) { @@ -115,6 +124,7 @@ bool RestV1ApiHandler::GetSignalPath(uint32_t requestId, "Signal path not valid", json); ret = false; + break; } } else { @@ -124,6 +134,7 @@ bool RestV1ApiHandler::GetSignalPath(uint32_t requestId, "Signal path URI not valid", json); ret = false; + break; } } } diff --git a/w3c-visserver-api/src/SubscriptionHandler.cpp b/w3c-visserver-api/src/SubscriptionHandler.cpp index 487897a..709d5cb 100644 --- a/w3c-visserver-api/src/SubscriptionHandler.cpp +++ b/w3c-visserver-api/src/SubscriptionHandler.cpp @@ -22,8 +22,8 @@ #include "Authenticator.hpp" #include "exception.hpp" #include "visconf.hpp" +#include "WsChannel.hpp" #include "VssDatabase.hpp" -#include "WsServer.hpp" #include "ILogger.hpp" using namespace std; @@ -129,8 +129,8 @@ int SubscriptionHandler::updateByUUID(const string &signalUUID, for (auto subID : handle->second) { std::lock_guard lock(subMutex); - pair newSub; - newSub = std::make_pair(subID.second, value); + tuple newSub; + newSub = std::make_tuple(subID.first, subID.second, value); buffer.push(newSub); } @@ -158,13 +158,12 @@ void* SubscriptionHandler::subThreadRunner() { auto newSub = buffer.front(); buffer.pop(); - auto connId = newSub.first; - - jsoncons::json value = newSub.second; + auto connId = std::get<1>(newSub); + jsoncons::json value = std::get<2>(newSub); jsoncons::json answer; answer["action"] = "subscribe"; - answer["subscriptionId"] = connId; + answer["subscriptionId"] = std::get<0>(newSub); answer.insert_or_assign("value", value); answer["timestamp"] = time(NULL); diff --git a/w3c-visserver-api/src/VssDatabase.cpp b/w3c-visserver-api/src/VssDatabase.cpp index c8ce666..2dac680 100644 --- a/w3c-visserver-api/src/VssDatabase.cpp +++ b/w3c-visserver-api/src/VssDatabase.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "exception.hpp" #include "visconf.hpp" @@ -48,6 +49,8 @@ namespace { void checkTypeAndBound(std::shared_ptr logger, string value_type, jsoncons::json val) { bool typeValid = false; + boost::algorithm::to_lower(value_type); + if (value_type == "uint8") { typeValid = true; long double longDoubleVal; diff --git a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp index 3f101a5..0bf73ae 100644 --- a/w3c-visserver-api/src/WebSockHttpFlexServer.cpp +++ b/w3c-visserver-api/src/WebSockHttpFlexServer.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "detect_ssl.hpp" #include "ssl_stream.hpp" @@ -242,6 +243,7 @@ namespace { boost::asio::steady_timer timer_; RequestHandler requestHandler_; WsChannel channel; + std::list writeQueue_; public: // Construct the session explicit WebSocketSession(boost::asio::io_context& ioc, @@ -423,7 +425,14 @@ namespace { } void write(const std::string &message) { - // TODO: add queuing as async_write should be called one at a time + writeQueue_.push_back(message); + + // there can be only one async_write request at any single time, + // so queue additional transfers + if (writeQueue_.size() > 1) { + return; + } + boost::asio::buffer_copy(bufferWrite_.prepare(message.size()), boost::asio::buffer(message)); bufferWrite_.commit(message.size()); // commit copied data for write @@ -440,8 +449,6 @@ namespace { } void onWrite(boost::system::error_code ec, std::size_t bytesTransferred) { - boost::ignore_unused(bytesTransferred); - // Happens when the timer closes the socket if(ec == boost::asio::error::operation_aborted) return; @@ -453,6 +460,26 @@ namespace { // Clear the buffer bufferWrite_.consume(bytesTransferred); + + writeQueue_.pop_front(); + + // check if there is more to write + if (!writeQueue_.empty()) { + auto message = writeQueue_.front(); + boost::asio::buffer_copy(bufferWrite_.prepare(message.size()), boost::asio::buffer(message)); + bufferWrite_.commit(message.size()); // commit copied data for write + + // send message + derived().ws().async_write( + bufferWrite_.data(), + boost::asio::bind_executor( + strand_, + std::bind( + &WebSocketSession::onWrite, + derived().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } } };