Back to index

nordugrid-arc-nox  1.1.0~rc6
MCCSOAP.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 #include <arc/message/PayloadRaw.h>
00006 #include <arc/message/SOAPEnvelope.h>
00007 #include <arc/message/PayloadSOAP.h>
00008 #include <arc/message/SecAttr.h>
00009 #include <arc/XMLNode.h>
00010 #include <arc/loader/Plugin.h>
00011 #include <arc/ws-addressing/WSA.h>
00012 
00013 #include "MCCSOAP.h"
00014 
00015 
00016 Arc::Logger Arc::MCC_SOAP::logger(Arc::MCC::logger,"SOAP");
00017 
00018 
00019 Arc::MCC_SOAP::MCC_SOAP(Arc::Config *cfg) : Arc::MCC(cfg) {
00020 }
00021 
00022 static Arc::Plugin* get_mcc_service(Arc::PluginArgument* arg) {
00023     Arc::MCCPluginArgument* mccarg =
00024             arg?dynamic_cast<Arc::MCCPluginArgument*>(arg):NULL;
00025     if(!mccarg) return NULL;
00026     return new Arc::MCC_SOAP_Service((Arc::Config*)(*mccarg));
00027 }
00028 
00029 static Arc::Plugin* get_mcc_client(Arc::PluginArgument* arg) {
00030     Arc::MCCPluginArgument* mccarg =
00031             arg?dynamic_cast<Arc::MCCPluginArgument*>(arg):NULL;
00032     if(!mccarg) return NULL;
00033     return new Arc::MCC_SOAP_Client((Arc::Config*)(*mccarg));
00034 }
00035 
00036 Arc::PluginDescriptor PLUGINS_TABLE_NAME[] = {
00037     { "soap.service", "HED:MCC", 0, &get_mcc_service },
00038     { "soap.client",  "HED:MCC", 0, &get_mcc_client  },
00039     { NULL, NULL, 0, NULL }
00040 };
00041 
00042 namespace Arc {
00043 
00044 class SOAPSecAttr: public SecAttr {
00045  friend class MCC_SOAP_Service;
00046  friend class MCC_SOAP_Client;
00047  public:
00048   SOAPSecAttr(PayloadSOAP& payload);
00049   virtual ~SOAPSecAttr(void);
00050   virtual operator bool(void) const;
00051   virtual bool Export(SecAttrFormat format,XMLNode &val) const;
00052  protected:
00053   std::string action_;
00054   std::string object_;
00055   std::string context_;
00056   virtual bool equal(const SecAttr &b) const;
00057 };
00058 
00059 SOAPSecAttr::SOAPSecAttr(PayloadSOAP& payload) {
00060   action_=payload.Child().Name();
00061   context_=payload.Child().Namespace();
00062   if(WSAHeader::Check(payload)) object_ = WSAHeader(payload).To();
00063 }
00064 
00065 SOAPSecAttr::~SOAPSecAttr(void) {
00066 }
00067 
00068 SOAPSecAttr::operator bool(void) const {
00069   return !action_.empty();
00070 }
00071 
00072 bool SOAPSecAttr::equal(const SecAttr &b) const {
00073   try {
00074     const SOAPSecAttr& a = (const SOAPSecAttr&)b;
00075     return ((action_ == a.action_) && (context_ == a.context_));
00076   } catch(std::exception&) { };
00077   return false;
00078 }
00079 
00080 bool SOAPSecAttr::Export(SecAttrFormat format,XMLNode &val) const {
00081   if(format == UNDEFINED) {
00082   } else if(format == ARCAuth) {
00083     NS ns;
00084     ns["ra"]="http://www.nordugrid.org/schemas/request-arc";
00085     val.Namespaces(ns); val.Name("ra:Request");
00086     XMLNode item = val.NewChild("ra:RequestItem");
00087     if(!object_.empty()) {
00088       XMLNode object = item.NewChild("ra:Resource");
00089       object=object_;
00090       object.NewAttribute("Type")="string";
00091       object.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/soap/endpoint";
00092     };
00093     if(!action_.empty()) {
00094       XMLNode action = item.NewChild("ra:Action");
00095       action=action_;
00096       action.NewAttribute("Type")="string";
00097       action.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/soap/operation";
00098     };
00099     if(!context_.empty()) {
00100       XMLNode context = item.NewChild("ra:Context").NewChild("ra:ContextAttribute");
00101       context=context_;
00102       context.NewAttribute("Type")="string";
00103       context.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/soap/namespace";
00104     };
00105     return true;
00106   } else if(format == XACML) {
00107     NS ns;
00108     ns["ra"]="urn:oasis:names:tc:xacml:2.0:context:schema:os";
00109     val.Namespaces(ns); val.Name("ra:Request");
00110     if(!object_.empty()) {
00111       XMLNode object = val.NewChild("ra:Resource");
00112       XMLNode attr = object.NewChild("ra:Attribute");
00113       attr.NewChild("ra:AttributeValue") = object_;
00114       attr.NewAttribute("DataType")="xs:string";
00115       attr.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/soap/endpoint";
00116     };
00117     if(!action_.empty()) {
00118       XMLNode action = val.NewChild("ra:Action");
00119       XMLNode attr = action.NewChild("ra:Attribute");
00120       attr.NewChild("ra:AttributeValue") = action_;
00121       attr.NewAttribute("DataType")="xs:string";
00122       attr.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/soap/operation";
00123     };
00124     if(!context_.empty()) {
00125       XMLNode context = val.NewChild("ra:Environment");
00126       XMLNode attr = context.NewChild("ra:Attribute");
00127       attr.NewChild("ra:AttributeValue") = context_;
00128       attr.NewAttribute("DataType")="xs:string";
00129       attr.NewAttribute("AttributeId")="http://www.nordugrid.org/schemas/policy-arc/types/soap/namespace";
00130     };
00131     return true;
00132   } else {
00133   };
00134   return false;
00135 }
00136 
00137 MCC_SOAP_Service::MCC_SOAP_Service(Config *cfg):MCC_SOAP(cfg) {
00138 }
00139 
00140 MCC_SOAP_Service::~MCC_SOAP_Service(void) {
00141 }
00142 
00143 MCC_SOAP_Client::MCC_SOAP_Client(Config *cfg):MCC_SOAP(cfg) {
00144 }
00145 
00146 MCC_SOAP_Client::~MCC_SOAP_Client(void) {
00147 }
00148 
00149 static MCC_Status make_raw_fault(Message& outmsg,const char* reason = NULL) 
00150 {
00151   NS ns;
00152   SOAPEnvelope soap(ns,true);
00153   soap.Fault()->Code(SOAPFault::Receiver);
00154   std::string xml; soap.GetXML(xml);
00155   if(reason != NULL) soap.Fault()->Reason(0, reason);
00156   PayloadRaw* payload = new PayloadRaw;
00157   payload->Insert(xml.c_str());
00158   outmsg.Payload(payload);
00159   return MCC_Status(STATUS_OK);
00160 }
00161 
00162 static MCC_Status make_soap_fault(Message& outmsg,const char* reason = NULL) {
00163   PayloadSOAP* soap = new PayloadSOAP(NS(),true);
00164   soap->Fault()->Code(SOAPFault::Sender);
00165   if(reason != NULL) soap->Fault()->Reason(0, reason);
00166   outmsg.Payload(soap);
00167   return MCC_Status(STATUS_OK);
00168 }
00169 
00170 static MCC_Status make_soap_fault(Message& outmsg,Message& oldmsg,const char* reason = NULL) {
00171   if(oldmsg.Payload()) {
00172     delete oldmsg.Payload();
00173     oldmsg.Payload(NULL);
00174   };
00175   return make_soap_fault(outmsg,reason);
00176 }
00177 
00178 MCC_Status MCC_SOAP_Service::process(Message& inmsg,Message& outmsg) {
00179   // Extracting payload
00180   MessagePayload* inpayload = inmsg.Payload();
00181   if(!inpayload) {
00182     logger.msg(WARNING, "empty input payload");
00183     return make_raw_fault(outmsg,"Missing incoming request");
00184   }
00185   // Converting payload to SOAP
00186   PayloadSOAP nextpayload(*inpayload);
00187   if(!nextpayload) {
00188     logger.msg(WARNING, "incoming message is not SOAP");
00189     return make_raw_fault(outmsg,"Incoming request is not SOAP");
00190   }
00191   // Creating message to pass to next MCC and setting new payload.. 
00192   // Using separate message. But could also use same inmsg.
00193   // Just trying to keep it intact as much as possible.
00194   Message nextinmsg = inmsg;
00195   nextinmsg.Payload(&nextpayload);
00196   if(WSAHeader::Check(nextpayload)) {
00197     std::string endpoint_attr = WSAHeader(nextpayload).To();
00198     nextinmsg.Attributes()->set("SOAP:ENDPOINT",endpoint_attr);
00199     nextinmsg.Attributes()->set("ENDPOINT",endpoint_attr);
00200   };
00201   SOAPSecAttr* sattr = new SOAPSecAttr(nextpayload);
00202   nextinmsg.Auth()->set("SOAP",sattr);
00203   // Checking authentication and authorization; 
00204   if(!ProcessSecHandlers(nextinmsg,"incoming")) {
00205     logger.msg(ERROR, "Security check failed in SOAP MCC for incoming message");
00206     return make_raw_fault(outmsg, "Security check failed for SOAP request");
00207   };
00208   // Call next MCC 
00209   MCCInterface* next = Next();
00210   if(!next) {
00211     logger.msg(WARNING, "empty next chain element");
00212     return make_raw_fault(outmsg,"Internal error");
00213   }
00214   Message nextoutmsg = outmsg; nextoutmsg.Payload(NULL);
00215   MCC_Status ret = next->process(nextinmsg,nextoutmsg); 
00216   // Do checks and extract SOAP response
00217   if(!ret) {
00218     if(nextoutmsg.Payload()) delete nextoutmsg.Payload();
00219     logger.msg(WARNING, "next element of the chain returned error status");
00220     return make_raw_fault(outmsg,"Internal error");
00221   }
00222   if(!nextoutmsg.Payload()) {
00223     logger.msg(WARNING, "next element of the chain returned empty payload");
00224     return make_raw_fault(outmsg,"There is no response");
00225   }
00226   PayloadSOAP* retpayload = NULL;
00227   try {
00228     retpayload = dynamic_cast<PayloadSOAP*>(nextoutmsg.Payload());
00229   } catch(std::exception& e) { };
00230   if(!retpayload) {
00231     // There is a chance that next MCC decided to return pre-rendered SOAP
00232     // or otherwise valid non-SOAP response. For that case we simply pass 
00233     // it back to previous MCC and let it decide.
00234     logger.msg(INFO, "next element of the chain returned unknown payload - passing through");
00235     //Checking authentication and authorization; 
00236     if(!ProcessSecHandlers(nextoutmsg,"outgoing")) {
00237       logger.msg(ERROR, "Security check failed in SOAP MCC for outgoing message");
00238       delete nextoutmsg.Payload();
00239       return make_raw_fault(outmsg,"Security check failed for SOAP response");
00240     };
00241     outmsg = nextoutmsg;
00242     return MCC_Status(STATUS_OK);
00243   };
00244   if(!(*retpayload)) {
00245     delete retpayload;
00246     return make_raw_fault(outmsg,"There is no valid SOAP response");
00247   };
00248   //Checking authentication and authorization; 
00249   if(!ProcessSecHandlers(nextoutmsg,"outgoing")) {
00250     logger.msg(ERROR, "Security check failed in SOAP MCC for outgoing message");
00251     delete retpayload; return make_raw_fault(outmsg,"Security check failed for SOAP response");
00252   };
00253   // Convert to Raw - TODO: more efficient conversion
00254   PayloadRaw* outpayload = new PayloadRaw;
00255   std::string xml; retpayload->GetXML(xml);
00256   outpayload->Insert(xml.c_str());
00257   outmsg = nextoutmsg; outmsg.Payload(NULL);
00258   // Specifying attributes for binding to underlying protocols - HTTP so far
00259   std::string soap_action = nextoutmsg.Attributes()->get("SOAP:ACTION");
00260   if(soap_action.empty()) soap_action=WSAHeader(*retpayload).Action();
00261   if(retpayload->Version() == SOAPEnvelope::Version_1_2) {
00262     // TODO: For SOAP 1.2 Content-Type is not sent in case of error - probably harmless
00263     std::string mime_type("application/soap+xml");
00264     if(!soap_action.empty()) mime_type+=" ;action=\""+soap_action+"\"";
00265     outmsg.Attributes()->set("HTTP:Content-Type",mime_type);
00266   } else {
00267     outmsg.Attributes()->set("HTTP:Content-Type","text/xml");
00268     outmsg.Attributes()->set("HTTP:SOAPAction",soap_action);
00269   };
00270   if(retpayload->Fault() != NULL) {
00271     // Maybe MCC_Status should be used instead ?
00272     outmsg.Attributes()->set("HTTP:CODE","500"); // TODO: For SOAP 1.2 :Sender fault must generate 400
00273     outmsg.Attributes()->set("HTTP:REASON","SOAP FAULT");
00274     // CONFUSED: SOAP 1.2 says that SOAP message is sent in response only if
00275     // HTTP code is 200 "Only if status line is 200, the SOAP message serialized according 
00276     // to the rules for carrying SOAP messages in the media type given by the Content-Type 
00277     // header field ...". But it also associates SOAP faults with HTTP error codes. So it 
00278     // looks like in case of SOAP fault SOAP fault messages is not sent. That sounds 
00279     // stupid - not implementing.
00280   };
00281   delete retpayload;
00282   outmsg.Payload(outpayload);
00283   return MCC_Status(STATUS_OK);
00284 }
00285 
00286 MCC_Status MCC_SOAP_Client::process(Message& inmsg,Message& outmsg) {
00287   // Extracting payload
00288   if(!inmsg.Payload()) return make_soap_fault(outmsg,"No message to send");
00289   PayloadSOAP* inpayload = NULL;
00290   try {
00291     inpayload = dynamic_cast<PayloadSOAP*>(inmsg.Payload());
00292   } catch(std::exception& e) { };
00293   if(!inpayload) return make_soap_fault(outmsg,"No SOAP message to send");
00294   //Checking authentication and authorization;
00295   if(!ProcessSecHandlers(inmsg,"outgoing")) {
00296     logger.msg(ERROR, "Security check failed in SOAP MCC for outgoing message");
00297     return make_soap_fault(outmsg,"Security check failed for outgoing SOAP message");
00298   };
00299   // Converting payload to Raw
00300   PayloadRaw nextpayload;
00301   std::string xml; inpayload->GetXML(xml);
00302   nextpayload.Insert(xml.c_str());
00303   // Creating message to pass to next MCC and setting new payload.. 
00304   Message nextinmsg = inmsg;
00305   nextinmsg.Payload(&nextpayload);
00306   // Specifying attributes for binding to underlying protocols - HTTP so far
00307   std::string soap_action = inmsg.Attributes()->get("SOAP:ACTION");
00308   if(soap_action.empty()) soap_action=WSAHeader(*inpayload).Action();
00309   if(inpayload->Version() == SOAPEnvelope::Version_1_2) {
00310     std::string mime_type("application/soap+xml");
00311     if(!soap_action.empty()) mime_type+=" ;action=\""+soap_action+"\"";
00312     nextinmsg.Attributes()->set("HTTP:Content-Type",mime_type);
00313   } else {
00314     nextinmsg.Attributes()->set("HTTP:Content-Type","text/xml");
00315     nextinmsg.Attributes()->set("HTTP:SOAPAction",soap_action);
00316   };
00317   // Call next MCC 
00318   MCCInterface* next = Next();
00319   if(!next) return make_soap_fault(outmsg,"Internal chain failure: no next component");
00320   Message nextoutmsg = outmsg; nextoutmsg.Payload(NULL);
00321   MCC_Status ret = next->process(nextinmsg,nextoutmsg); 
00322   // Do checks and create SOAP response
00323   if(!ret) {
00324     return make_soap_fault(outmsg,nextoutmsg,"Failed to send SOAP message");
00325   };
00326   if(!nextoutmsg.Payload()) return make_soap_fault(outmsg,nextoutmsg,"No response for SOAP message recieved");
00327   MessagePayload* retpayload = nextoutmsg.Payload();
00328   if(!retpayload) return make_soap_fault(outmsg,nextoutmsg,"No valid response for SOAP message recieved");
00329   PayloadSOAP* outpayload  = new PayloadSOAP(*retpayload);
00330   if(!outpayload) return make_soap_fault(outmsg,nextoutmsg,"Response is not SOAP");
00331   if(!(*outpayload)) {
00332     delete outpayload; return make_soap_fault(outmsg,nextoutmsg,"Response is not valid SOAP");
00333   };
00334   outmsg = nextoutmsg;
00335   outmsg.Payload(outpayload);
00336   delete nextoutmsg.Payload(); nextoutmsg.Payload(NULL);
00337   //Checking authentication and authorization; 
00338   if(!ProcessSecHandlers(outmsg,"incoming")) {
00339     logger.msg(ERROR, "Security check failed in SOAP MCC for incoming message");
00340     delete outpayload; return make_soap_fault(outmsg,"Security check failed for incoming SOAP message");
00341   };
00342   return MCC_Status(STATUS_OK);
00343 }
00344 
00345 } // namespace Arc