Back to index

nordugrid-arc-nox  1.1.0~rc6
MCCMsgValidator.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 #include "MCCMsgValidator.h"
00006 
00007 #include <arc/message/PayloadRaw.h>
00008 #include <arc/message/SOAPEnvelope.h>
00009 #include <arc/message/PayloadSOAP.h>
00010 #include <arc/XMLNode.h>
00011 #include <arc/loader/Plugin.h>
00012 #include <arc/ws-addressing/WSA.h>
00013 
00014 #include <libxml/tree.h>
00015 #include <libxml/parser.h>
00016 #include <libxml/xpath.h>
00017 #include <libxml/xmlschemas.h>
00018 //#include <libxml/xmlstring.h>
00019 
00020 Arc::Logger Arc::MCC_MsgValidator::logger(Arc::MCC::logger,"MsgValidator");
00021 
00022 
00023 Arc::MCC_MsgValidator::MCC_MsgValidator(Arc::Config *cfg) : Arc::MCC(cfg) {
00024     // Collect services to be validated
00025     for(int i = 0;;++i) {
00026         Arc::XMLNode n = (*cfg)["ValidatedService"][i];
00027         if(!n) break;
00028         std::string servicepath = n["ServicePath"];
00029         if(servicepath.empty()) {
00030             //missing path
00031             logger.msg(Arc::WARNING, "Skipping service: no ServicePath found!");
00032             continue;
00033         };
00034         std::string schemapath = n["SchemaPath"];
00035         if(schemapath.empty()) {
00036             //missing path
00037             logger.msg(Arc::WARNING, "Skipping service: no SchemaPath found!");
00038             continue;
00039         };
00040 
00041         // register schema path with service
00042         schemas[servicepath] = schemapath;
00043     }
00044 }
00045 
00046 static Arc::Plugin* get_mcc_service(Arc::PluginArgument* arg) {
00047     Arc::MCCPluginArgument* mccarg =
00048             arg?dynamic_cast<Arc::MCCPluginArgument*>(arg):NULL;
00049     if(!mccarg) return NULL;
00050     return new Arc::MCC_MsgValidator_Service((Arc::Config*)(*mccarg));
00051 }
00052 
00053 Arc::PluginDescriptor PLUGINS_TABLE_NAME[] = {
00054     { "msg.validator.service", "HED:MCC", 0, &get_mcc_service },
00055     { "msg.validator.client", "HED:MCC", 0, &get_mcc_service },
00056     { NULL, NULL, 0, NULL }
00057 };
00058 
00059 namespace Arc {
00060 
00061 MCC_MsgValidator_Service::MCC_MsgValidator_Service(Config *cfg):MCC_MsgValidator(cfg) {
00062 }
00063 
00064 MCC_MsgValidator_Service::~MCC_MsgValidator_Service(void) {
00065 }
00066 
00067 std::string MCC_MsgValidator::getSchemaPath(std::string servicePath) {
00068     // Look for servicePath in the map.
00069     // Using a const_iterator since we are not going to change the values.
00070     for(std::map<std::string,std::string>::const_iterator iter = schemas.begin(); iter != schemas.end(); ++iter)
00071     {
00072         if(iter->first == servicePath) {
00073             // found servicePath; returning schemaPath
00074             return iter->second;
00075         }
00076     }
00077     // nothing found; returning empty path
00078     return "";
00079 }
00080 
00081 bool MCC_MsgValidator::validateMessage(Message& msg, std::string schemaPath){
00082     // create parser ctxt for schema accessible on schemaPath
00083     xmlSchemaParserCtxtPtr schemaParserP = xmlSchemaNewParserCtxt(schemaPath.c_str());
00084 
00085     if(!schemaParserP) {
00086         // could not create context
00087         logger.msg(ERROR, "Parser Context creation failed!");
00088         return false;
00089     }
00090 
00091     // parse schema
00092     xmlSchemaPtr schemaP = xmlSchemaParse(schemaParserP);
00093 
00094     if(!schemaP) {
00095         // could not parse schema
00096         logger.msg(ERROR, "Cannot parse schema!");
00097         // have to free parser ctxt
00098         xmlSchemaFreeParserCtxt(schemaParserP);
00099         return false;
00100     }
00101 
00102     // we do not need schemaParserP any more, so it can be freed
00103     xmlSchemaFreeParserCtxt(schemaParserP);
00104 
00105     // Extracting payload
00106     MessagePayload* payload = msg.Payload();
00107     if(!payload) {
00108         logger.msg(ERROR, "Empty payload!");
00109         return false;
00110     }
00111 
00112     // Converting payload to SOAP
00113     PayloadSOAP* plsp = NULL; 
00114     plsp = dynamic_cast<PayloadSOAP*>(payload);
00115     if(!plsp) {
00116         // cast failed
00117         logger.msg(ERROR, "Could not convert payload!");
00118         return false;
00119     }
00120     PayloadSOAP soapPL(*plsp);
00121 
00122     if(!soapPL) {
00123         logger.msg(ERROR, "Could not create PayloadSOAP!");
00124         return false;
00125     }
00126 
00127     std::string arcPSstr;
00128     // get SOAP payload as string
00129     soapPL.GetXML(arcPSstr);
00130 
00131     // parse string into libxml2 xmlDoc
00132     xmlDocPtr lxdocP = xmlParseDoc(xmlCharStrdup(arcPSstr.c_str()));
00133 
00134     // create XPath context; later, we will have to free it!
00135     xmlXPathContextPtr xpCtxtP = xmlXPathNewContext(lxdocP);
00136 
00137     // content is the first child _element_ of SOAP Body
00138     std::string exprstr = "//*[local-name()='Body' and namespace-uri()='http://schemas.xmlsoap.org/soap/envelope/'][1]/*[1]";
00139 
00140     // result is a xmlXPathObjectPtr; later, we will have to free it!
00141     xmlXPathObjectPtr xpObP = xmlXPathEval(xmlCharStrdup(exprstr.c_str()),xpCtxtP);
00142 
00143     // xnsP is the set of result nodes
00144     xmlNodeSetPtr xnsP = xpObP->nodesetval;
00145 
00146     // the set has only one member (content) - see above (exprstr)
00147     xmlNodePtr content = xnsP->nodeTab[0];
00148     
00149     // create a new xmlDoc for content
00150     xmlDocPtr doc = xmlNewDoc(xmlCharStrdup("1.0"));
00151 
00152     // create schema validation context
00153     xmlSchemaValidCtxtPtr validity_ctxt_ptr = xmlSchemaNewValidCtxt(schemaP);
00154 
00155     // copy & add content to doc as a child
00156     xmlNodePtr tmpNode = xmlDocCopyNode(content, doc, 1);
00157     xmlAddChild((xmlNodePtr)doc, tmpNode);
00158 
00159     // validate against schema
00160     bool result = (xmlSchemaValidateDoc(validity_ctxt_ptr, doc) == 0);
00161 
00162     // free resources and return result
00163     xmlSchemaFreeValidCtxt(validity_ctxt_ptr);
00164 
00165     xmlSchemaFree(schemaP);
00166 
00167     xmlFreeDoc(doc);
00168 
00169     xmlFreeDoc(lxdocP);
00170 
00171     xmlXPathFreeContext(xpCtxtP);
00172 
00173     xmlXPathFreeObject(xpObP);
00174 
00175     return result;
00176 }
00177 
00178 static MCC_Status make_raw_fault(Message& outmsg,const char* = NULL) 
00179 {
00180   NS ns;
00181   SOAPEnvelope soap(ns,true);
00182   soap.Fault()->Code(SOAPFault::Receiver);
00183   std::string xml; soap.GetXML(xml);
00184   PayloadRaw* payload = new PayloadRaw;
00185   payload->Insert(xml.c_str());
00186   outmsg.Payload(payload);
00187   return MCC_Status(GENERIC_ERROR);
00188 }
00189 
00190 static MCC_Status make_soap_fault(Message& outmsg,const char* = NULL) {
00191   PayloadSOAP* soap = new PayloadSOAP(NS(),true);
00192   soap->Fault()->Code(SOAPFault::Receiver);
00193   outmsg.Payload(soap);
00194   return MCC_Status(GENERIC_ERROR);
00195 }
00196 
00197 static MCC_Status make_soap_fault(Message& outmsg,Message& oldmsg,const char* desc = NULL) {
00198   if(oldmsg.Payload()) {
00199     delete oldmsg.Payload();
00200     oldmsg.Payload(NULL);
00201   };
00202   return make_soap_fault(outmsg,desc);
00203 }
00204 
00205 MCC_Status MCC_MsgValidator_Service::process(Message& inmsg,Message& outmsg) {
00206   // Extracting payload
00207   MessagePayload* inpayload = inmsg.Payload();
00208   if(!inpayload) {
00209     logger.msg(WARNING, "Empty input payload!");
00210     return make_raw_fault(outmsg);
00211   }
00212 
00213   // Converting payload to SOAP
00214   PayloadSOAP* plsp = NULL;
00215   plsp = dynamic_cast<PayloadSOAP*>(inpayload);
00216   if(!plsp) {
00217       // cast failed
00218       logger.msg(ERROR, "Could not convert incoming payload!");
00219       return make_raw_fault(outmsg);
00220   }
00221 
00222   PayloadSOAP nextpayload(*plsp);
00223   
00224   if(!nextpayload) {
00225     logger.msg(ERROR, "Could not create PayloadSOAP!");
00226     return make_raw_fault(outmsg);
00227   }
00228 
00229   // Creating message to pass to next MCC and setting new payload.. 
00230   // Using separate message. But could also use same inmsg.
00231   // Just trying to keep it intact as much as possible.
00232   Message nextinmsg = inmsg;
00233   nextinmsg.Payload(&nextpayload);
00234   
00235   std::string endpoint_attr = inmsg.Attributes()->get("ENDPOINT");
00236 
00237   // extract service path
00238   std::string servicePath = getPath(endpoint_attr);
00239 
00240   // check config fog corresponding service
00241   std::string schemaPath = getSchemaPath(servicePath);
00242 
00243   if("" == schemaPath) {
00244       // missing schema
00245       logger.msg(WARNING, "Missing schema! Skipping validation...");
00246   } else {
00247       // try to validate message against service schema
00248       if(!validateMessage(nextinmsg,schemaPath)) {
00249           // message validation failed for some reason
00250           logger.msg(ERROR, "Could not validate message!");
00251           return make_raw_fault(outmsg);
00252       }
00253   }
00254 
00255   // Call next MCC 
00256   MCCInterface* next = Next();
00257   if(!next) {
00258     logger.msg(WARNING, "empty next chain element");
00259     return make_raw_fault(outmsg);
00260   }
00261   Message nextoutmsg = outmsg; nextoutmsg.Payload(NULL);
00262   MCC_Status ret = next->process(nextinmsg,nextoutmsg); 
00263   // Do checks and extract SOAP response
00264   if(!ret) {
00265     if(nextoutmsg.Payload()) delete nextoutmsg.Payload();
00266     logger.msg(WARNING, "next element of the chain returned error status");
00267     return make_raw_fault(outmsg);
00268   }
00269   if(!nextoutmsg.Payload()) {
00270     logger.msg(WARNING, "next element of the chain returned empty payload");
00271     return make_raw_fault(outmsg);
00272   }
00273   PayloadSOAP* retpayload = NULL;
00274   try {
00275     retpayload = dynamic_cast<PayloadSOAP*>(nextoutmsg.Payload());
00276   } catch(std::exception& e) { };
00277   if(!retpayload) {
00278     logger.msg(WARNING, "next element of the chain returned invalid payload");
00279     delete nextoutmsg.Payload(); 
00280     return make_raw_fault(outmsg); 
00281   };
00282   if(!(*retpayload)) { delete retpayload; return make_raw_fault(outmsg); };
00283 
00284   // replace old payload with retpayload
00285   // then delete old payload
00286   delete outmsg.Payload(retpayload);
00287   return MCC_Status(STATUS_OK);
00288 }
00289 
00290 std::string MCC_MsgValidator_Service::getPath(std::string url){
00291     std::string::size_type ds, ps;
00292     ds=url.find("//");
00293     if (ds==std::string::npos)
00294         ps=url.find("/");
00295     else
00296         ps=url.find("/", ds+2);
00297     if (ps==std::string::npos)
00298         return "";
00299     else
00300         return url.substr(ps);
00301 }
00302 
00303 } // namespace Arc