diff --git a/src/server/WebBackend.cpp b/src/server/WebBackend.cpp index a54c9c1..9aa55ae 100644 --- a/src/server/WebBackend.cpp +++ b/src/server/WebBackend.cpp @@ -15,18 +15,28 @@ #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 + */ + namesp = namesp.toLower(); name = name.toLower(); //Go through and forward this request to the appropriate sub-system - if(name.toLower()=="syscache"){ + if(namesp=="rpc" && name=="syscache"){ return EvaluateSyscacheRequest(args, out); - }else if(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; @@ -59,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; @@ -80,3 +91,42 @@ 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(); + 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 + + //If nothing done - return the proper code + if(!ok){ + return RestOutputStruct::BADREQUEST; + } + }else{ // if(in_args.isArray()){ + return RestOutputStruct::BADREQUEST; + } + return RestOutputStruct::OK; +} diff --git a/src/server/WebSocket.cpp b/src/server/WebSocket.cpp index f8648ad..d4d4969 100644 --- a/src/server/WebSocket.cpp +++ b/src/server/WebSocket.cpp @@ -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"){ @@ -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,43 +136,32 @@ 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)); out.out_args = array; + out.CODE = RestOutputStruct::OK; }else{ 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.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); } @@ -212,86 +181,32 @@ void WebSocket::EvaluateRequest(const RestInputStruct &REQ){ }else{ outargs.insert("unknown",QJsonValue("unknown")); } - //ret.insert("args",outargs); out.out_args = outargs; }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; + }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()); } + 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 @@ -314,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 // ===================== @@ -366,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){ @@ -374,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(){ @@ -383,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"; } // ====================== @@ -398,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 7a377ad..fca1675 100644 --- a/src/server/WebSocket.h +++ b/src/server/WebSocket.h @@ -45,15 +45,16 @@ 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 - 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); - + // -- 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();