From 8d57413401a8e0ac6584a69cc1edaf57c1bed5c2 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Mon, 4 Jan 2016 13:30:49 -0500 Subject: [PATCH 1/5] A few more updates to the input message parsing systems for the server: Ensure that both the namespace and the name of the input message are passed through to the backend parser/system (if authorized). --- src/server/WebBackend.cpp | 12 +++++++++--- src/server/WebSocket.cpp | 33 +++++++++++++++++---------------- src/server/WebSocket.h | 2 +- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index a54c9c1..5a0c823 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -15,11 +15,17 @@ #define DEBUG 0 #define SCLISTDELIM QString("::::") //SysCache List Delimiter -RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(QString name, const QJsonValue args, QJsonObject *out){ +RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(QString namesp, QString name, const QJsonValue args, QJsonObject *out){ + /*Inputs: + "namesp" - namespace for the request + "name" - name of the request + "args" - JSON input arguments structure + "out" - JSON output arguments structure + */ //Go through and forward this request to the appropriate sub-system - if(name.toLower()=="syscache"){ + if(namesp == "rpc" && name.toLower()=="syscache"){ return EvaluateSyscacheRequest(args, out); - }else if(name.toLower()=="dispatcher"){ + }else if(namesp == "rpc" && name.toLower()=="dispatcher"){ return EvaluateSyscacheRequest(args, out); }else{ return RestOutputStruct::BADREQUEST; diff --git a/src/server/WebSocket.cpp b/src/server/WebSocket.cpp index b1c1591..fbd2196 100644 --- a/src/server/WebSocket.cpp +++ b/src/server/WebSocket.cpp @@ -5,7 +5,7 @@ // ================================= #include "WebSocket.h" -#define DEBUG 0 +#define DEBUG 1 #define IDLETIMEOUTMINS 30 WebSocket::WebSocket(QWebSocket *sock, QString ID, AuthorizationManager *auth){ @@ -79,7 +79,7 @@ void WebSocket::EvaluateREST(QString msg){ qDebug() << " - Name:" << IN.name; qDebug() << " - Namespace:" << IN.namesp; qDebug() << " - ID:" << IN.id; - qDebug() << " - Has Args:" << IN.args.isNull(); + qDebug() << " - Has Args:" << !IN.args.isNull(); } //Now check for the REST-specific verbs/actions if(IN.VERB == "OPTIONS" || IN.VERB == "HEAD"){ @@ -163,6 +163,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ array.append(SockAuthToken); array.append(AUTHSYSTEM->checkAuthTimeoutSecs(SockAuthToken)); out.out_args = array; + out.CODE = RestOutputStruct::OK; }else{ SockAuthToken.clear(); //invalid token //Bad Authentication - return error @@ -177,7 +178,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ //ret.insert("namespace", QJsonValue("rpc")); //ret.insert("name", QJsonValue("response")); //ret.insert("id", doc.object().value("id")); //use the same ID for the return message - out.CODE = EvaluateBackendRequest(out.in_struct.name, out.in_struct.args, &outargs); + out.CODE = EvaluateBackendRequest(out.in_struct.namesp, out.in_struct.name, out.in_struct.args, &outargs); out.out_args = outargs; //ret.insert("args",outargs); }else{ out.CODE = RestOutputStruct::UNAUTHORIZED; @@ -217,22 +218,22 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ }else{ //Bad/No authentication out.CODE = RestOutputStruct::UNAUTHORIZED; - //SetOutputError(&ret, JsonValueToString(doc.object().value("id")), 401, "Unauthorized"); } - }else{ + //Other namespace - check whether auth has already been established before continuing + }else if( AUTHSYSTEM->checkAuth(SockAuthToken) ){ //validate current Authentication token + //Now provide access to the various subsystems + //Pre-set any output fields + QJsonObject outargs; + out.CODE = EvaluateBackendRequest(out.in_struct.namesp, out.in_struct.name, out.in_struct.args, &outargs); + out.out_args = outargs; //ret.insert("args",outargs); + }else{ //Error in inputs - assemble the return error message out.CODE = RestOutputStruct::BADREQUEST; - /*QString id = "error"; - if(doc.object().contains("id")){ id = JsonValueToString(doc.object().value("id")); } //use the same ID - SetOutputError(&ret, id, 400, "Bad Request");*/ - } - //Assemble the outputs for this "GET" request - out.CODE = RestOutputStruct::OK; - //Assemble the output JSON document/text - //QJsonDocument retdoc; - //retdoc.setObject(ret); - //out.Body = retdoc.toJson(); - out.Header << "Content-Type: text/json; charset=utf-8"; + } + //If this is a REST input - go ahead and format the output header + if(out.CODE == RestOutputStruct::OK){ + out.Header << "Content-Type: text/json; charset=utf-8"; + } } //Return any information if(SOCKET!=0){ SOCKET->sendTextMessage(out.assembleMessage()); } diff --git a/src/server/WebSocket.h b/src/server/WebSocket.h index 7a377ad..fff99dd 100644 --- a/src/server/WebSocket.h +++ b/src/server/WebSocket.h @@ -49,7 +49,7 @@ private: //Backend request/reply functions (contained in WebBackend.cpp) // -- Main subsystem parser - RestOutputStruct::ExitCode EvaluateBackendRequest(QString name, const QJsonValue in_args, QJsonObject *out); + RestOutputStruct::ExitCode EvaluateBackendRequest(QString namesp, QString name, const QJsonValue in_args, QJsonObject *out); // -- Individual subsystems RestOutputStruct::ExitCode EvaluateSyscacheRequest(const QJsonValue in_args, QJsonObject *out); RestOutputStruct::ExitCode EvaluateDispatcherRequest(const QJsonValue in_args, QJsonObject *out); From 7d53e59a1d1d6f9b7a6cb3c097358c149df51000 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Mon, 4 Jan 2016 13:33:44 -0500 Subject: [PATCH 2/5] Oops, forgot to turn off debug mode before committing. --- src/server/WebSocket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/WebSocket.cpp b/src/server/WebSocket.cpp index fbd2196..7297227 100644 --- a/src/server/WebSocket.cpp +++ b/src/server/WebSocket.cpp @@ -5,7 +5,7 @@ // ================================= #include "WebSocket.h" -#define DEBUG 1 +#define DEBUG 0 #define IDLETIMEOUTMINS 30 WebSocket::WebSocket(QWebSocket *sock, QString ID, AuthorizationManager *auth){ From fdf06cac1e5ec208bef5dcfea5a423dc5dc7ab50 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Mon, 4 Jan 2016 13:44:58 -0500 Subject: [PATCH 3/5] Clean up a bit more of the websocket class. --- src/server/WebSocket.cpp | 129 ++++----------------------------------- src/server/WebSocket.h | 1 - 2 files changed, 12 insertions(+), 118 deletions(-) diff --git a/src/server/WebSocket.cpp b/src/server/WebSocket.cpp index 7297227..d4aeb6f 100644 --- a/src/server/WebSocket.cpp +++ b/src/server/WebSocket.cpp @@ -113,27 +113,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ out.CODE = RestOutputStruct::BADREQUEST; }else{ //Now check the body of the message and do what it needs - /*QJsonDocument doc = QJsonDocument::fromJson(REQ.Body.toUtf8()); - if(doc.isNull()){ qWarning() << "Empty JSON Message Body!!" << REQ.Body.toUtf8(); } - //Define the output structures - QJsonObject ret; //return message - - //Objects contain other key/value pairs - this is 99% of cases - if(doc.isObject()){ - //First check/set all the various required fields (both in and out) - bool good = doc.object().contains("namespace") \ - && doc.object().contains("name") \ - && doc.object().contains("id") \ - && doc.object().contains("args"); - //Can add some fallbacks for missing fields here - but not implemented yet - */ - //parse the message and do something - //if(good && (JsonValueToString(doc.object().value("namespace"))=="rpc") ){ if(out.in_struct.namesp.toLower() == "rpc"){ - //Now fetch the outputs from the appropriate subsection - //Note: Each subsection needs to set the "name", "namespace", and "args" output objects - //QString name = JsonValueToString(doc.object().value("name")).toLower(); - //QJsonValue args = doc.object().value("args"); if(out.in_struct.name.startsWith("auth")){ //Now perform authentication based on type of auth given //Note: This sets/changes the current SockAuthToken @@ -156,9 +136,6 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ //Now check the auth and respond appropriately if(AUTHSYSTEM->checkAuth(SockAuthToken)){ //Good Authentication - return the new token - //ret.insert("namespace", QJsonValue("rpc")); - //ret.insert("name", QJsonValue("response")); - //ret.insert("id", doc.object().value("id")); //use the same ID for the return message QJsonArray array; array.append(SockAuthToken); array.append(AUTHSYSTEM->checkAuthTimeoutSecs(SockAuthToken)); @@ -168,32 +145,23 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ SockAuthToken.clear(); //invalid token //Bad Authentication - return error out.CODE = RestOutputStruct::UNAUTHORIZED; - //SetOutputError(&ret, JsonValueToString(out.in_struct.id), 401, "Unauthorized"); } }else if( AUTHSYSTEM->checkAuth(SockAuthToken) ){ //validate current Authentication token //Now provide access to the various subsystems //Pre-set any output fields QJsonObject outargs; - //ret.insert("namespace", QJsonValue("rpc")); - //ret.insert("name", QJsonValue("response")); - //ret.insert("id", doc.object().value("id")); //use the same ID for the return message - out.CODE = EvaluateBackendRequest(out.in_struct.namesp, out.in_struct.name, out.in_struct.args, &outargs); - out.out_args = outargs; //ret.insert("args",outargs); + out.CODE = EvaluateBackendRequest(out.in_struct.namesp, out.in_struct.name, out.in_struct.args, &outargs); + out.out_args = outargs; }else{ - out.CODE = RestOutputStruct::UNAUTHORIZED; //Bad/No authentication - //SetOutputError(&ret, JsonValueToString(doc.object().value("id")), 401, "Unauthorized"); + out.CODE = RestOutputStruct::UNAUTHORIZED; } - //}else if(good && (JsonValueToString(doc.object().value("namespace"))=="events") ){ }else if(out.in_struct.namesp.toLower() == "events"){ if( AUTHSYSTEM->checkAuth(SockAuthToken) ){ //validate current Authentication token //Pre-set any output fields QJsonObject outargs; - //ret.insert("namespace", QJsonValue("events")); - //ret.insert("name", QJsonValue("response")); - //ret.insert("id", doc.object().value("id")); //use the same ID for the return message //Assemble the list of input events QStringList evlist; if(out.in_struct.args.isObject()){ evlist << JsonValueToString(out.in_struct.args); } @@ -213,7 +181,6 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ }else{ outargs.insert("unknown",QJsonValue("unknown")); } - //ret.insert("args",outargs); out.out_args = outargs; }else{ //Bad/No authentication @@ -225,7 +192,7 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ //Pre-set any output fields QJsonObject outargs; out.CODE = EvaluateBackendRequest(out.in_struct.namesp, out.in_struct.name, out.in_struct.args, &outargs); - out.out_args = outargs; //ret.insert("args",outargs); + out.out_args = outargs; }else{ //Error in inputs - assemble the return error message out.CODE = RestOutputStruct::BADREQUEST; @@ -236,63 +203,10 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ } } //Return any information - if(SOCKET!=0){ SOCKET->sendTextMessage(out.assembleMessage()); } + if(SOCKET!=0){ SOCKET->sendTextMessage(out.assembleMessage()); } else if(TSOCKET!=0){ TSOCKET->write(out.assembleMessage().toUtf8().data()); } } -// === SYSCACHE REQUEST INTERACTION === -/*void WebSocket::EvaluateBackendRequest(QString name, const QJsonValue args, QJsonObject *out){ - QJsonObject obj; //output object - if(args.isObject()){ - //For the moment: all arguments are full syscache DB calls - no special ones - QStringList reqs = args.toObject().keys(); - if(!reqs.isEmpty()){ - if(DEBUG){ qDebug() << "Parsing Inputs:" << reqs; } - for(int r=0; rinsert(req, QJsonValue(values.join("")) ); } - else{ - //This is an array of outputs - QJsonArray arr; - for(int i=0; iinsert(req,arr); - } - } - } //end of special "request" objects - }else if(args.isArray()){ - QStringList inputs = JsonArrayToStringList(args.toArray()); - if(DEBUG){ qDebug() << "Parsing Array inputs:" << inputs; } - QStringList values; - if(name.toLower()=="syscache"){values = SysCacheClient::parseInputs( inputs ); } - else if(name.toLower()=="dispatcher"){values = DispatcherClient::parseInputs( inputs , AUTHSYSTEM); } - if(DEBUG){ qDebug() << " - Returns:" << values; } - for(int i=0; iinsert(inputs[i],arr); - }else{ - out->insert(inputs[i],values[i]); - } - } - } //end array of inputs - -}*/ - // === GENERAL PURPOSE UTILITY FUNCTIONS === QString WebSocket::JsonValueToString(QJsonValue val){ //Note: Do not use this on arrays - only use this on single-value values @@ -315,23 +229,13 @@ QString WebSocket::JsonValueToString(QJsonValue val){ QStringList WebSocket::JsonArrayToStringList(QJsonArray array){ //Note: This assumes that the array is only values, not additional objects QStringList out; - qDebug() << "Array to List:" << array.count(); + if(DEBUG){ qDebug() << "Array to List:" << array.count(); } for(int i=0; iinsert("namespace", QJsonValue("rpc")); - ret->insert("name", QJsonValue("error")); - ret->insert("id",QJsonValue(id)); - QJsonObject obj; - obj.insert("code", err); - obj.insert("message", QJsonValue(msg)); - ret->insert("args",obj); -} - // ===================== // PRIVATE SLOTS // ===================== @@ -367,7 +271,7 @@ void WebSocket::EvaluateMessage(const QByteArray &msg){ if(idletimer->isActive()){ idletimer->stop(); } EvaluateREST( QString(msg) ); idletimer->start(); - qDebug() << "Done with Message"; + qDebug() << " - Done with Binary Message"; } void WebSocket::EvaluateMessage(const QString &msg){ @@ -375,7 +279,7 @@ void WebSocket::EvaluateMessage(const QString &msg){ if(idletimer->isActive()){ idletimer->stop(); } EvaluateREST(msg); idletimer->start(); - qDebug() << "Done with Message"; + qDebug() << " - Done with Text Message"; } void WebSocket::EvaluateTcpMessage(){ @@ -384,7 +288,7 @@ void WebSocket::EvaluateTcpMessage(){ if(idletimer->isActive()){ idletimer->stop(); } EvaluateREST( QString(TSOCKET->readAll()) ); idletimer->start(); - qDebug() << "Done with Message"; + qDebug() << " - Done with TCP Message"; } // ====================== @@ -399,23 +303,14 @@ void WebSocket::AppCafeStatusUpdate(QString msg){ out.CODE = RestOutputStruct::OK; out.in_struct.name = "event"; out.in_struct.namesp = "events"; - //Define the output structures - //QJsonObject ret; //return message //Pre-set any output fields QJsonObject outargs; - //ret.insert("namespace", QJsonValue("events")); - //ret.insert("name", QJsonValue("event")); - //ret.insert("id", QJsonValue("")); outargs.insert("name", "dispatcher"); outargs.insert("args",QJsonValue(msg)); - out.out_args = outargs; - //ret.insert("args",outargs); + out.out_args = outargs; - //Assemble the output JSON document/text - //QJsonDocument retdoc; - //retdoc.setObject(ret); - //out.Body = retdoc.toJson(); - out.Header << "Content-Type: text/json; charset=utf-8"; + //Assemble the output JSON document/text + out.Header << "Content-Type: text/json; charset=utf-8"; //REST header info //Now send the message back through the socket if(SOCKET!=0){ SOCKET->sendTextMessage(out.assembleMessage()); } else if(TSOCKET!=0){ TSOCKET->write(out.assembleMessage().toUtf8().data()); } diff --git a/src/server/WebSocket.h b/src/server/WebSocket.h index fff99dd..3573944 100644 --- a/src/server/WebSocket.h +++ b/src/server/WebSocket.h @@ -45,7 +45,6 @@ private: //Simplification functions QString JsonValueToString(QJsonValue); QStringList JsonArrayToStringList(QJsonArray); - void SetOutputError(QJsonObject *ret, QString id, int err, QString msg); //Backend request/reply functions (contained in WebBackend.cpp) // -- Main subsystem parser From ace6c2ca4c2acaeaf252bb729b604ca4fb073d83 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Mon, 4 Jan 2016 14:29:04 -0500 Subject: [PATCH 4/5] Add a new subsystem to sysadm-server: the sysadm-network device information system. To Access: namespace="sysadm" name="network" args={"action" : "list-devices"} Output arguments structure: "" : { "ipv4" : , "ipv6" : , "netmask" : , "description" : , "MAC" : , "status" : , "is_active" : [true/false], "is_dhcp" : [true/false], "is_wireless" : [true/false] } --- src/server/WebBackend.cpp | 42 +++++++++++++++++++++++++++++++++++++-- src/server/WebSocket.h | 4 +++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index 5a0c823..8dfd6cc 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -22,17 +22,21 @@ RestOutputStruct::ExitCode WebSocket::EvaluateBackendRequest(QString namesp, QSt "args" - JSON input arguments structure "out" - JSON output arguments structure */ + namesp = namesp.toLower(); name = name.toLower(); //Go through and forward this request to the appropriate sub-system - if(namesp == "rpc" && name.toLower()=="syscache"){ + if(namesp=="rpc" && name=="syscache"){ return EvaluateSyscacheRequest(args, out); - }else if(namesp == "rpc" && name.toLower()=="dispatcher"){ + }else if(namesp=="rpc" && name=="dispatcher"){ return EvaluateSyscacheRequest(args, out); + }else if(namesp=="sysadm" && name=="network"){ + return EvaluateSysadmNetworkRequest(args, out); }else{ return RestOutputStruct::BADREQUEST; } } +//==== SYSCACHE ==== RestOutputStruct::ExitCode WebSocket::EvaluateSyscacheRequest(const QJsonValue in_args, QJsonObject *out){ //syscache only needs a list of sub-commands at the moment (might change later) QStringList in_req; @@ -65,6 +69,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSyscacheRequest(const QJsonValue i return RestOutputStruct::OK; } +//==== DISPATCHER ==== RestOutputStruct::ExitCode WebSocket::EvaluateDispatcherRequest(const QJsonValue in_args, QJsonObject *out){ //dispatcher only needs a list of sub-commands at the moment (might change later) QStringList in_req; @@ -86,3 +91,36 @@ RestOutputStruct::ExitCode WebSocket::EvaluateDispatcherRequest(const QJsonValue return RestOutputStruct::OK; } +//==== SYSADM -- Network ==== +RestOutputStruct::ExitCode WebSocket::EvaluateSysadmNetworkRequest(const QJsonValue in_args, QJsonObject *out){ + if(in_args.isObject()){ + QStringList keys = in_args.toObject().keys(); + if(keys.contains("action")){ + QString act = JsonValueToString(in_args.toObject().value("action")); + if(act=="list-devices"){ + QStringList devs = sysadm::NetDevice::listNetDevices(); + for(int i=0; iinsert(devs[i], obj); + } + + } + } + + }else if(in_args.isArray()){ + + } + return RestOutputStruct::OK; +} diff --git a/src/server/WebSocket.h b/src/server/WebSocket.h index 3573944..fca1675 100644 --- a/src/server/WebSocket.h +++ b/src/server/WebSocket.h @@ -52,7 +52,9 @@ private: // -- Individual subsystems RestOutputStruct::ExitCode EvaluateSyscacheRequest(const QJsonValue in_args, QJsonObject *out); RestOutputStruct::ExitCode EvaluateDispatcherRequest(const QJsonValue in_args, QJsonObject *out); - + // -- sysadm library/subsystems + RestOutputStruct::ExitCode EvaluateSysadmNetworkRequest(const QJsonValue in_args, QJsonObject *out); + private slots: void checkIdle(); //see if the currently-connected client is idle void SocketClosing(); From b1e451b38543049e5b46b20b96ad66a11caa115b Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Mon, 4 Jan 2016 15:07:53 -0500 Subject: [PATCH 5/5] Adjust the new sysadm/network function a bit - just add proper error code handling if nothing was performed. --- src/server/WebBackend.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index 8dfd6cc..9aa55ae 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -95,9 +95,11 @@ RestOutputStruct::ExitCode WebSocket::EvaluateDispatcherRequest(const QJsonValue RestOutputStruct::ExitCode WebSocket::EvaluateSysadmNetworkRequest(const QJsonValue in_args, QJsonObject *out){ if(in_args.isObject()){ QStringList keys = in_args.toObject().keys(); + bool ok = false; if(keys.contains("action")){ QString act = JsonValueToString(in_args.toObject().value("action")); if(act=="list-devices"){ + ok = true; QStringList devs = sysadm::NetDevice::listNetDevices(); for(int i=0; iinsert(devs[i], obj); } - } - } + + } //end of "action" key usage - }else if(in_args.isArray()){ - + //If nothing done - return the proper code + if(!ok){ + return RestOutputStruct::BADREQUEST; + } + }else{ // if(in_args.isArray()){ + return RestOutputStruct::BADREQUEST; } return RestOutputStruct::OK; }