예제 #1
0
//=======================
//             PRIVATE
//=======================
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)	

  if(DEBUG){
    qDebug() << "New REST Message:";
    qDebug() << "  VERB:" << IN.VERB << "URI:" << IN.URI;
    qDebug() << "  HEADERS:" << IN.Header;
    qDebug() << "  BODY:" << IN.Body;
  }
  //Now check for the REST-specific verbs/actions
  if(IN.VERB == "OPTIONS" || IN.VERB == "HEAD"){
    RestOutputStruct out;	  
      out.CODE = RestOutputStruct::OK;
      if(IN.VERB=="HEAD"){
	
      }else{ //OPTIONS
	out.Header << "Allow: HEAD, GET";
	out.Header << "Hosts: /syscache";	      
      }
      out.Header << "Accept: text/json";
      out.Header << "Content-Type: text/json; charset=utf-8";
    SOCKET->sendTextMessage(out.assembleMessage());
  }else{ 
    EvaluateRequest(IN);
  }
}
예제 #2
0
//=======================
//             PRIVATE
//=======================
void WebServer::EvaluateREST(QString msg){
  //Parse the message into it's elements and proceed to the main data evaluation
  RestInputStruct IN(msg);
  if(DEBUG){
    qDebug() << "New REST Message:";
    qDebug() << "  VERB:" << IN.VERB << "URI:" << IN.URI;
    qDebug() << "  HEADERS:" << IN.Header;
    qDebug() << "  BODY:" << IN.Body;
  }
  //Now check for the REST-specific verbs/actions
  if(IN.VERB == "OPTIONS" || IN.VERB == "HEAD"){
    RestOutputStruct out;	  
      out.CODE = RestOutputStruct::OK;
      if(IN.VERB=="HEAD"){
	
      }else{ //OPTIONS
	out.Header << "Allow: HEAD, GET";
	out.Header << "Hosts: /syscache";	      
      }
      out.Header << "Accept: text/json";
      out.Header << "Content-Type: text/json; charset=utf-8";
    csock->sendTextMessage(out.assembleMessage());
  }else{
    EvaluateRequest(IN);
  }
}
예제 #3
0
// ======================
//       PUBLIC SLOTS
// ======================
void WebSocket::EventUpdate(EventWatcher::EVENT_TYPE evtype, QJsonValue msg){
  if(msg.isNull()){ msg = EVENTS->lastEvent(evtype); }
  //qDebug() << "Socket Status Update:" << msg;
  if(!ForwardEvents.contains(evtype)){ return; }
  RestOutputStruct out;
    out.CODE = RestOutputStruct::OK;
    out.in_struct.namesp = "events";
    out.out_args = msg;
    out.Header << "Content-Type: text/json; charset=utf-8"; //REST header info
    out.in_struct.name = EventWatcher::typeToString(evtype);
  
  //Now send the message back through the socket
  this->sendReply(out.assembleMessage());
}
예제 #4
0
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)	

  if(DEBUG){
    qDebug() << "New REST Message:";
    qDebug() << "  VERB:" << IN.VERB << "URI:" << IN.URI;
    qDebug() << "  HEADERS:" << IN.Header;
    qDebug() << "  BODY:" << IN.Body;
    //qDebug() << " Auth:" << IN.auth;
    qDebug() << "JSON Values:";
    qDebug() << " - Name:" << IN.name;
    qDebug() << " - Namespace:" << IN.namesp;
    qDebug() << " - ID:" << IN.id;
    qDebug() << " - Has Args:" << !IN.args.isNull();
  }
  //Now check for the REST-specific verbs/actions
  if(IN.VERB == "OPTIONS" || IN.VERB == "HEAD"){
    RestOutputStruct out;	  
      out.in_struct = IN;
      out.CODE = RestOutputStruct::OK;
      if(IN.VERB=="HEAD"){
	
      }else{ //OPTIONS
	out.Header << "Allow: HEAD, GET";
	out.Header << "Hosts: /syscache";	      
      }
      out.Header << "Accept: text/json";
      out.Header << "Content-Type: text/json; charset=utf-8";
    this->sendReply(out.assembleMessage());
  }else{
    //EvaluateRequest(IN);
    if(IN.name.startsWith("auth") ){
      //Keep auth system requests in order
      EvaluateRequest(IN);
    }else{
      QtConcurrent::run(this, &WebSocket::EvaluateRequest, IN);
    }
  }
}
예제 #5
0
// ======================
//       PUBLIC SLOTS
// ======================
void WebSocket::AppCafeStatusUpdate(QString msg){
  if(!msg.isEmpty()){ lastDispatchEvent = msg; }
  else{ msg = lastDispatchEvent; }
  //qDebug() << "Socket Status Update:" << msg;
  if(!SendAppCafeEvents){ return; } //don't report events on this socket
  RestOutputStruct out;
  //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));
   ret.insert("args",outargs);	
   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";
   SOCKET->sendTextMessage(out.assembleMessage());
}
예제 #6
0
void WebServer::EvaluateRequest(const RestInputStruct &REQ){
  RestOutputStruct out;
  if(REQ.VERB != "GET"){
    //Non-supported request (at the moment) - return an error message
    out.CODE = RestOutputStruct::BADREQUEST;
  }else{
    //GET request
    //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
    QJsonDocument ret; //return message
    //parse the message and do something
    //Objects contain other key/value pairs - this is 99% of cases
    if(doc.isObject()){
      QJsonObject obj;
      QStringList keys = doc.object().keys();
      if(REQ.URI.toLower()=="/syscache"){
        QStringList reqs = keys.filter("request",Qt::CaseInsensitive);
        if(!reqs.isEmpty()){
	  qDebug() << "Parsing Inputs:" << reqs;
	  for(int r=0; r<reqs.length(); r++){
	    QString req =  JsonValueToString(doc.object().value(reqs[r]));
	    qDebug() << "  ["+reqs[r]+"]="+req;
	    QStringList values = SysCacheClient::parseInputs( QStringList() << req ); 
	    values.removeAll("");
	    //Quick check if a list of outputs was returned
	    if(values.length()==1){
	      values = values[0].split(SCLISTDELIM); //split up the return list (if necessary)
	      values.removeAll("");
	    }
	    qDebug() << " - Returns:" << values;
	    keys.removeAll(reqs[r]); //this key was already processed
	    if(values.length()<2){
	      obj.insert(reqs[r],values.join(""));
	    }else{
	      //This is an array of outputs
	      QJsonArray arr;
              for(int i=0; i<values.length(); i++){ arr.append(values[i]); }
	      obj.insert(reqs[r],arr);
            }
          }
        } //end of special "request" objects
      
      }else{
        qDebug() << "Object Variables:" << keys;
        for(int i=0; i<keys.length(); i++){
          qDebug() << keys[i]+"="+JsonValueToString(doc.object().value(keys[i]) );
        }	  
      }
      ret.setObject(obj);
    //Special case for a single syscache input (array of strings)
    }else if(doc.isArray() && REQ.URI.toLower()=="/syscache"){
        QStringList inputs = JsonArrayToStringList(doc.array());
        qDebug() << " syscache inputs:" << inputs;
        QJsonObject obj;
        QStringList values = SysCacheClient::parseInputs(inputs );
        for(int i=0; i<values.length(); i++){
	  if(values[i].contains(SCLISTDELIM)){
	    //This is an array of values
	    QStringList vals = values[i].split(SCLISTDELIM);
	    vals.removeAll("");
	    QJsonArray arr;
                for(int j=0; j<vals.length(); j++){ arr.append(vals[j]); }
	      obj.insert("Value"+QString::number(i),arr);
	  }else{
            obj.insert("Value"+QString::number(i),values[i]);
	  }
        }
      ret.setObject(obj);
    }
    //Assemble the outputs for this "GET" request
    out.CODE = RestOutputStruct::OK;
    out.Body = ret.toJson();
    out.Header << "Content-Type: text/json; charset=utf-8";
  }
  //Return any information
  csock->sendTextMessage(out.assembleMessage());
}
예제 #7
0
void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
  RestOutputStruct out;
  if(REQ.VERB != "GET"){
    //Non-supported request (at the moment) - return an error message
    out.CODE = RestOutputStruct::BADREQUEST;
  }else{
    //GET request
    //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") ){
	//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(name.startsWith("auth")){
	  //Now perform authentication based on type of auth given
	  //Note: This sets/changes the current SockAuthToken
	  AUTHSYSTEM->clearAuth(SockAuthToken); //new auth requested - clear any old token
	  if(DEBUG){ qDebug() << "Authenticate Peer:" << SOCKET->peerAddress().toString(); }
	  bool localhost = (SOCKET->peerAddress() == QHostAddress::LocalHost) || (SOCKET->peerAddress() == QHostAddress::LocalHostIPv6);
	  //Now do the auth
	  if(name=="auth" && args.isObject() ){
	    //username/password authentication
	    QString user, pass;
	    if(args.toObject().contains("username")){ user = JsonValueToString(args.toObject().value("username"));  }
	    if(args.toObject().contains("password")){ pass = JsonValueToString(args.toObject().value("password"));  }
	    SockAuthToken = AUTHSYSTEM->LoginUP(localhost, user, pass);
	  }else if(name == "auth_token" && args.isObject()){
	    SockAuthToken = JsonValueToString(args.toObject().value("token"));
	  }else if(name == "auth_clear"){
	    return; //don't send a return message after clearing an auth (already done)
	  }
	  
	  //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));
	    ret.insert("args", array);
	  }else{
	    SockAuthToken.clear(); //invalid token
	    //Bad Authentication - return error
	    SetOutputError(&ret, JsonValueToString(doc.object().value("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
	  EvaluateBackendRequest(name, doc.object().value("args"), &outargs);
            ret.insert("args",outargs);	  
        }else{
	  //Bad/No authentication
	  SetOutputError(&ret, JsonValueToString(doc.object().value("id")), 401, "Unauthorized");
	}
	      
      }else if(good && (JsonValueToString(doc.object().value("namespace"))=="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(doc.object().value("args").isObject()){ evlist << JsonValueToString(doc.object().value("args")); }
	    else if(doc.object().value("args").isArray()){ evlist = JsonArrayToStringList(doc.object().value("args").toArray()); }
	    //Now subscribe/unsubscribe to these events
	    if(JsonValueToString(doc.object().value("name"))=="subscribe"){
	      if(evlist.contains("dispatcher")){ 
	        SendAppCafeEvents = true; 
	        outargs.insert("subscribe",QJsonValue("dispatcher"));  
		QTimer::singleShot(100, this, SLOT(AppCafeStatusUpdate()) );
	      }
	    }else if(JsonValueToString(doc.object().value("name"))=="unsubscribe"){
	      if(evlist.contains("dispatcher")){ 
		SendAppCafeEvents = false; 
		outargs.insert("unsubscribe",QJsonValue("dispatcher"));
	      }
	    }else{
	      outargs.insert("unknown",QJsonValue("unknown"));
	    }
            ret.insert("args",outargs);	  
          }else{
	    //Bad/No authentication
	    SetOutputError(&ret, JsonValueToString(doc.object().value("id")), 401, "Unauthorized");
	  }
	}else{
        //Error in inputs - assemble the return error message
	QString id = "error";
	if(doc.object().contains("id")){ id = JsonValueToString(doc.object().value("id")); } //use the same ID
	SetOutputError(&ret, id, 400, "Bad Request");
      }
    }else{
      //Unknown type of JSON input - nothing to do
    }
    //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";
  }
  //Return any information
  SOCKET->sendTextMessage(out.assembleMessage());
}
예제 #8
0
void WebSocket::EvaluateRequest(const RestInputStruct &REQ){
  RestOutputStruct out;
    out.in_struct = REQ;
  QHostAddress host;
    if(SOCKET!=0){ host = SOCKET->peerAddress(); }
    else if(TSOCKET!=0){ host = TSOCKET->peerAddress(); }
  if(!REQ.VERB.isEmpty() && REQ.VERB != "GET" && REQ.VERB!="POST" && REQ.VERB!="PUT"){
    //Non-supported request (at the moment) - return an error message
    out.CODE = RestOutputStruct::BADREQUEST;
  }else if(out.in_struct.name.isEmpty() || out.in_struct.namesp.isEmpty() ){
    //Invalid JSON structure validity
    //Note: id and args are optional at this stage - let the subsystems handle those inputs
    out.CODE = RestOutputStruct::BADREQUEST;
  }else{
    //First check for a REST authorization (not stand-alone request)
    if(!out.in_struct.auth.isEmpty()){
      AUTHSYSTEM->clearAuth(SockAuthToken); //new auth requested - clear any old token
      SockAuthToken = AUTHSYSTEM->LoginUP(host, out.in_struct.auth.section(":",0,0), out.in_struct.auth.section(":",1,1));
    }
	  
    //Now check the body of the message and do what it needs
      if(out.in_struct.namesp.toLower() == "rpc"){
	if(out.in_struct.name.startsWith("auth")){
	  //Now perform authentication based on type of auth given
	  //Note: This sets/changes the current SockAuthToken
	  AUTHSYSTEM->clearAuth(SockAuthToken); //new auth requested - clear any old token
	  if(DEBUG){ qDebug() << "Authenticate Peer:" << SOCKET->peerAddress().toString(); }
	  //Now do the auth
	  if(out.in_struct.name=="auth" && out.in_struct.args.isObject() ){
	    //username/[password/cert] authentication
	    QString user, pass;
	    if(out.in_struct.args.toObject().contains("username")){ user = JsonValueToString(out.in_struct.args.toObject().value("username"));  }
	    if(out.in_struct.args.toObject().contains("password")){ pass = JsonValueToString(out.in_struct.args.toObject().value("password"));  }
	    if(!pass.isEmpty()){
	      //Use the given password
	      SockAuthToken = AUTHSYSTEM->LoginUP(host, user, pass);
	    }else{
	      //No password - use the current SSL certificates instead
	      QList<QSslCertificate> certs;
	      if(SOCKET!=0){ certs = SOCKET->sslConfiguration().peerCertificateChain(); }
	      else if(TSOCKET!=0){ certs = TSOCKET->peerCertificateChain(); }
	      SockAuthToken = AUTHSYSTEM->LoginUC(host, user, certs);
	    }
	  }else if(out.in_struct.name == "auth_token" && out.in_struct.args.isObject()){
	    SockAuthToken = JsonValueToString(out.in_struct.args.toObject().value("token"));
	  }else if(out.in_struct.name == "auth_clear"){
	    return; //don't send a return message after clearing an auth (already done)
	  }
	  
	  //Now check the auth and respond appropriately
	  if(AUTHSYSTEM->checkAuth(SockAuthToken)){
	    //Good Authentication - return the new token 
	    QJsonArray array;
	      array.append(SockAuthToken);
	      array.append(AUTHSYSTEM->checkAuthTimeoutSecs(SockAuthToken));
	    out.out_args = array;
	    out.CODE = RestOutputStruct::OK;
	  }else{
	    if(SockAuthToken=="REFUSED"){
	      out.CODE = RestOutputStruct::FORBIDDEN;
	    }
	    SockAuthToken.clear(); //invalid token
	    //Bad Authentication - return error
	      out.CODE = RestOutputStruct::UNAUTHORIZED;
	  }
		
	}else if( AUTHSYSTEM->checkAuth(SockAuthToken) ){ //validate current Authentication token	 
	  //Now provide access to the various subsystems
	  // First get/set the permissions flag into the input structure
	    out.in_struct.fullaccess = AUTHSYSTEM->hasFullAccess(SockAuthToken);
	  //Pre-set any output fields
          QJsonObject outargs;	
	    out.CODE = EvaluateBackendRequest(out.in_struct, &outargs);
            out.out_args = outargs;	  
        }else{
	  //Bad/No authentication
	  out.CODE = RestOutputStruct::UNAUTHORIZED;
	}
	    	
      }else if(out.in_struct.namesp.toLower() == "events"){
          if( AUTHSYSTEM->checkAuth(SockAuthToken) ){ //validate current Authentication token	 
	    //Pre-set any output fields
            QJsonObject outargs;	
	    //Assemble the list of input events
	    QStringList evlist;
	    if(out.in_struct.args.isObject()){ evlist << JsonValueToString(out.in_struct.args); }
	    else if(out.in_struct.args.isArray()){ evlist = JsonArrayToStringList(out.in_struct.args.toArray()); }
	    //Now subscribe/unsubscribe to these events
	    int sub = -1; //bad input
	    if(out.in_struct.name=="subscribe"){ sub = 1; }
	    else if(out.in_struct.name=="unsubscribe"){ sub = 0; }
	    //qDebug() << "Got Client Event Modification:" << sub << evlist;
	    if(sub>=0 && !evlist.isEmpty() ){
	      for(int i=0; i<evlist.length(); i++){
	        EventWatcher::EVENT_TYPE type = EventWatcher::typeFromString(evlist[i]);
		if(type==EventWatcher::BADEVENT){ continue; }
		outargs.insert(out.in_struct.name,QJsonValue(evlist[i]));
		if(sub==1){ 
		  ForwardEvents << type; 
		  EventUpdate(type);
		}else{
		  ForwardEvents.removeAll(type);
		}
	      }
	      out.out_args = outargs;
	      out.CODE = RestOutputStruct::OK;
	    }else{
	      //Bad/No authentication
	      out.CODE = RestOutputStruct::BADREQUEST;		    
	    }
          }else{
	    //Bad/No authentication
	    out.CODE = RestOutputStruct::UNAUTHORIZED;
	  }
	//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
	  // First get/set the permissions flag into the input structure
	  out.in_struct.fullaccess = AUTHSYSTEM->hasFullAccess(SockAuthToken);
	  //Pre-set any output fields
          QJsonObject outargs;	
	    out.CODE = EvaluateBackendRequest(out.in_struct, &outargs);
            out.out_args = outargs;
	}else{
	  //Error in inputs - assemble the return error message
	  out.CODE = RestOutputStruct::UNAUTHORIZED;
	}
    //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
  this->sendReply(out.assembleMessage());
  if(out.CODE == RestOutputStruct::FORBIDDEN && SOCKET!=0){
    SOCKET->close(QWebSocketProtocol::CloseCodeNormal, "Too Many Authorization Failures - Try again later");
  }
}