From 1fea33c354130d85ccdf0795e7bea40974147c6a Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Mon, 9 May 2016 13:36:40 -0400 Subject: [PATCH] Get the sysadm server message parsing system all setup for bridge relays (no additional encryption/decryption layer yet) --- src/server/RestStructs.h | 160 ++------------------------------------- src/server/WebSocket.cpp | 23 ++++-- src/server/WebSocket.h | 10 +-- src/server/server.pro | 3 +- 4 files changed, 27 insertions(+), 169 deletions(-) diff --git a/src/server/RestStructs.h b/src/server/RestStructs.h index 72df11e..dffb3a2 100644 --- a/src/server/RestStructs.h +++ b/src/server/RestStructs.h @@ -31,58 +31,11 @@ public: //User Permissions level bool fullaccess; - RestInputStruct(QString message = ""){ - HTTPVERSION = CurHttpVersion; //default value - fullaccess = false; - if(message.isEmpty()){ return; } - //Pull out any REST headers - Body = message; - //qDebug() << "Raw Message:" << message; - if(!message.startsWith("{")){ - Header = message.section("{",0,0).split("\n"); - Body = "{"+message.section("{",1, 1000000); - } - if(!Header.isEmpty()){ - QString line = Header.takeFirst(); //The first line is special (not a generic header) - VERB = line.section(" ",0,0); - URI = line.section(" ",1,1); - HTTPVERSION = line.section(" ",2,2); - //Body = message.remove(Header.join("\n")+"\n"); //chop the headers off the front - if(!Header.filter("Authorization:").isEmpty()){ - line = Header.filter("Authorization:").takeFirst().section("Authorization: ",1,50).simplified(); - if(line.section(" ",0,0).toLower()=="basic"){ - //Convert the base64-encoded string to the plain "user:pass" string - QByteArray ba; - ba.append(line.section(" ",1,1)); - auth = QByteArray::fromBase64(ba); - } - } - } - //Now Parse out the Body into the JSON fields and/or arguments structure - Body = Body.simplified(); //remove any extra whitespace on the beginning/end - if(Body.startsWith("{") && Body.endsWith("}") ){ - QJsonDocument doc = QJsonDocument::fromJson(Body.toUtf8()); - if(!doc.isNull() && doc.isObject() ){ - //Valid JSON found - if(doc.object().contains("namespace") ){ namesp = doc.object().value("namespace").toString(); } - if(doc.object().contains("name") ){ name = doc.object().value("name").toString(); } - if(doc.object().contains("id") ){ id = doc.object().value("id").toString(); } - if(doc.object().contains("args") ){ args = doc.object().value("args"); } - else{ - //no args structure - treat the entire body as the arguments struct - args = doc.object(); - } - } - } - //Now do any REST -> JSON conversions if necessary - if(!URI.isEmpty()){ - //TO-DO - name = URI.section("/",-1); //last entry - namesp = URI.section("/",1,1); //URI excluding name - } - } - ~RestInputStruct(){} + RestInputStruct(QString message = "", bool isRest = false); + ~RestInputStruct(); + void ParseBodyIntoJson(); + }; class RestOutputStruct{ @@ -98,110 +51,7 @@ public: } ~RestOutputStruct(){} - QString assembleMessage(){ - if( !in_struct.VERB.isEmpty() ){ - //REST output syntax - QStringList headers; - QString firstline = in_struct.HTTPVERSION.simplified(); - if(firstline.isEmpty()){ firstline = CurHttpVersion.simplified(); }//default value - QString Body; - if(!out_args.isNull()){ - QJsonObject obj; obj.insert("args", out_args); - Body = QJsonDocument(obj).toJson(); - } - switch(CODE){ - case PROCESSING: - firstline.append(" 102 Processing"); break; - case OK: - firstline.append(" 200 OK"); break; - case CREATED: - firstline.append(" 201 Created"); break; - case ACCEPTED: - firstline.append(" 202 Accepted"); break; - case NOCONTENT: - firstline.append(" 204 No Content"); break; - case RESETCONTENT: - firstline.append(" 205 Reset Content"); break; - case PARTIALCONTENT: - firstline.append(" 206 Partial Content"); break; - case BADREQUEST: - firstline.append(" 400 Bad Request"); break; - case UNAUTHORIZED: - firstline.append(" 401 Unauthorized"); break; - case FORBIDDEN: - firstline.append(" 403 Forbidden"); break; - case NOTFOUND: - firstline.append(" 404 Not Found"); break; - } - headers << firstline; - headers << "Server: SysAdm/1.0"; - headers << "Date: "+QDateTime::currentDateTime().toString(Qt::ISODate).simplified(); - if(!Header.isEmpty()) { - for (int i = 0; i < Header.size(); ++i) - headers << Header.at(i).simplified(); - } - //Now add the body of the return - if(!Body.isEmpty()){ headers << "Content-Length: "+QString::number(Body.toUtf8().size()); } //number of bytes for the body - headers << ""; - headers << Body; - return headers.join("\r\n"); - - }else{ - //JSON output (load all the input fields for the moment) - QString oname = "response"; //use this for valid responses (input ID found) - if(in_struct.id.isEmpty()){ oname = in_struct.name; } - QString onamesp = in_struct.namesp; - QString oid = in_struct.id; - if(CODE!=OK){ - //Format the output based on the type of error - QJsonObject out_err; - switch(CODE){ - case PROCESSING: - //oname = onamesp = "error"; - out_err.insert("code","102"); out_err.insert("message", "Processing"); break; - case CREATED: - //oname = onamesp = "error"; - out_err.insert("code","201"); out_err.insert("message", "Created"); break; - case ACCEPTED: - //oname = onamesp = "error"; - out_err.insert("code","202"); out_err.insert("message", "Accepted"); break; - case NOCONTENT: - oname = onamesp = "error"; - out_err.insert("code","204"); out_err.insert("message", "No Content"); break; - case RESETCONTENT: - //oname = onamesp = "error"; - out_err.insert("code","205"); out_err.insert("message", "Reset Content"); break; - case PARTIALCONTENT: - oname = onamesp = "error"; - out_err.insert("code","206"); out_err.insert("message", "Partial Content"); break; - case BADREQUEST: - oname = onamesp = "error"; - out_err.insert("code","400"); out_err.insert("message", "Bad Request"); break; - case UNAUTHORIZED: - oname = onamesp = "error"; - out_err.insert("code","401"); out_err.insert("message", "Unauthorized"); break; - case FORBIDDEN: - oname = onamesp = "error"; - out_err.insert("code","403"); out_err.insert("message", "Forbidden"); break; - case NOTFOUND: - oname = onamesp = "error"; - out_err.insert("code","404"); out_err.insert("message", "Not Found"); break; - default: - break; - } - out_args = out_err; - } - //Now assemple the JSON output - QJsonObject obj; - obj.insert("namespace",onamesp); - obj.insert("name",oname); - obj.insert("id",oid); - obj.insert("args", out_args); - //Convert the JSON to string form - QJsonDocument doc(obj); - return doc.toJson(QJsonDocument::Compact); - } - } + QString assembleMessage(); //normal operation - no special processing needed }; #endif diff --git a/src/server/WebSocket.cpp b/src/server/WebSocket.cpp index 7874682..b44e47a 100644 --- a/src/server/WebSocket.cpp +++ b/src/server/WebSocket.cpp @@ -90,9 +90,12 @@ void WebSocket::sendReply(QString msg){ void WebSocket::EvaluateREST(QString msg){ //Parse the message into it's elements and proceed to the main data evaluation - RestInputStruct IN(msg); - //NOTE: All the REST functionality is disabled for the moment, until we decide to turn it on again at a later time (just need websockets right now - not full REST) - + RestInputStruct IN(msg, TSOCKET!=0); + if(SOCKET!=0 && !IN.Header.isEmpty()){ + //Bridge-relay message - need to decrypt the message body before it can be parsed + //IN.Body = AUTHSYSTEM->decryptString(IN.Body, key); //TO-DO + IN.ParseBodyIntoJson(); + } if(DEBUG){ qDebug() << "New REST Message:"; qDebug() << " VERB:" << IN.VERB << "URI:" << IN.URI; @@ -120,7 +123,6 @@ void WebSocket::EvaluateREST(QString msg){ out.Header << "Content-Type: text/json; charset=utf-8"; this->sendReply(out.assembleMessage()); }else{ - //EvaluateRequest(IN); if(IN.name.startsWith("auth") || (IN.namesp.toLower()=="rpc" && IN.name.toLower()=="identify") ){ //Keep auth/pre-auth system requests in order EvaluateRequest(IN); @@ -278,12 +280,19 @@ if(out.in_struct.namesp.toLower() == "rpc"){ } } //Return any information - + QString msg = out.assembleMessage(); + if(SOCKET!=0 && !REQ.Header.isEmpty()){ + //BRIDGE RELAY - alternate format + //Need to encrypt the message for output (TO-DO) + //msg = AUTHSYSTEM->encryptString(msg, key); + //Now add the destination ID + msg.prepend( REQ.Header.join("\n")+"\n"); + } if(out.CODE == RestOutputStruct::FORBIDDEN && SOCKET!=0 && SOCKET->isValid()){ - this->sendReply(out.assembleMessage()); + this->sendReply(msg); SOCKET->close(QWebSocketProtocol::CloseCodeNormal, "Too Many Authorization Failures - Try again later"); }else{ - this->emit SendMessage(out.assembleMessage()); + this->emit SendMessage(msg); } } diff --git a/src/server/WebSocket.h b/src/server/WebSocket.h index 0275984..cf6876f 100644 --- a/src/server/WebSocket.h +++ b/src/server/WebSocket.h @@ -33,8 +33,8 @@ private: void ParseIncoming(); //Main connection comminucations procedure - void EvaluateREST(QString); //Text -> Rest/JSON struct - void EvaluateRequest(const RestInputStruct&); // Parse Rest/JSON (does auth/events) + void EvaluateREST(QString); //STAGE 1 response: Text -> Rest/JSON struct + void EvaluateRequest(const RestInputStruct&); //STAGE 2 response: Parse Rest/JSON (does auth/events) //Simplification functions QString JsonValueToString(QJsonValue); @@ -82,9 +82,9 @@ private slots: void SocketClosing(); //Currently connected socket signal/slot connections - void EvaluateMessage(const QByteArray&); - void EvaluateMessage(const QString&); - void EvaluateTcpMessage(); + void EvaluateMessage(const QByteArray&); //initial message input (raw bytes - WebSocket) + void EvaluateMessage(const QString&); //initial message input (text - WebSocket) + void EvaluateTcpMessage(); //initial message input (text - REST) //SSL signal handling void nowEncrypted(); //the socket/connection is now encrypted diff --git a/src/server/server.pro b/src/server/server.pro index 0d4eaac..3738176 100644 --- a/src/server/server.pro +++ b/src/server/server.pro @@ -7,7 +7,6 @@ QT = core network websockets concurrent sql HEADERS += globals.h globals-qt.h \ WebServer.h \ WebSocket.h \ -# syscache-client.h \ RestStructs.h \ AuthorizationManager.h \ SslServer.h \ @@ -18,9 +17,9 @@ HEADERS += globals.h globals-qt.h \ SOURCES += main.cpp \ WebServer.cpp \ WebSocket.cpp \ + RestStructs.cpp \ WebBackend.cpp \ WebBackendSlots.cpp \ -# syscache-client.cpp \ AuthorizationManager.cpp \ EventWatcher.cpp \ LogManager.cpp \