Back to index

nordugrid-arc-nox  1.1.0~rc6
Defines | Functions
test_client.cpp File Reference
#include <string>
#include <iostream>
#include <fstream>
#include <signal.h>
#include <arc/ArcConfig.h>
#include <arc/Logger.h>
#include <arc/XMLNode.h>
#include <arc/message/MCCLoader.h>
#include <arc/message/SOAPEnvelope.h>
#include <arc/message/PayloadSOAP.h>
#include <arc/StringConv.h>
#include <arc/DateTime.h>
#include <arc/GUID.h>
#include <arc/credential/Credential.h>
#include "../../hed/libs/xmlsec/XmlSecUtils.h"
#include "../../hed/libs/xmlsec/XMLSecNode.h"

Go to the source code of this file.

Defines

#define SAML_NAMESPACE   "urn:oasis:names:tc:SAML:2.0:assertion"
#define SAMLP_NAMESPACE   "urn:oasis:names:tc:SAML:2.0:protocol"
#define XENC_NAMESPACE   "http://www.w3.org/2001/04/xmlenc#"
#define DSIG_NAMESPACE   "http://www.w3.org/2000/09/xmldsig#"

Functions

static std::string convert_dn (const std::string &dn)
int main (void)
 A example about how to compose a SAML <AttributeQuery> and call the Service_AA service, by using xmlsec library to compose <AttributeQuery> and process the <Response>.

Define Documentation

#define DSIG_NAMESPACE   "http://www.w3.org/2000/09/xmldsig#"

Definition at line 33 of file test_client.cpp.

#define SAML_NAMESPACE   "urn:oasis:names:tc:SAML:2.0:assertion"

Definition at line 29 of file test_client.cpp.

#define SAMLP_NAMESPACE   "urn:oasis:names:tc:SAML:2.0:protocol"

Definition at line 30 of file test_client.cpp.

#define XENC_NAMESPACE   "http://www.w3.org/2001/04/xmlenc#"

Definition at line 32 of file test_client.cpp.


Function Documentation

static std::string convert_dn ( const std::string &  dn) [static]

Definition at line 36 of file test_client.cpp.

                                                 {
  std::string ret;
  size_t pos1 = std::string::npos;
  size_t pos2;
  do {
    std::string str;
    pos2 = dn.find_last_of("/", pos1);
    if(pos2 != std::string::npos && pos1 == std::string::npos) {
      str = dn.substr(pos2+1);
      ret.append(str);
      pos1 = pos2-1;
    }
    else if (pos2 != std::string::npos && pos1 != std::string::npos) {
      str = dn.substr(pos2+1, pos1-pos2);
      ret.append(str);
      pos1 = pos2-1;
    }
    if(pos2 != (std::string::npos+1)) ret.append(",");
  }while(pos2 != std::string::npos && pos2 != (std::string::npos+1));
  return ret;
}

Here is the caller graph for this function:

int main ( void  )

A example about how to compose a SAML <AttributeQuery> and call the Service_AA service, by using xmlsec library to compose <AttributeQuery> and process the <Response>.

Definition at line 60 of file test_client.cpp.

               {
  signal(SIGTTOU,SIG_IGN);
  signal(SIGTTIN,SIG_IGN);
  signal(SIGPIPE,SIG_IGN);
  Arc::Logger logger(Arc::Logger::rootLogger, "SAMLTest");
  Arc::LogStream logcerr(std::cerr);
  Arc::Logger::rootLogger.addDestination(logcerr);

  // Create client chain
  logger.msg(Arc::INFO, "Creating client side chain");
  Arc::Config client_config("client.xml");
  if(!client_config) {
    logger.msg(Arc::ERROR, "Failed to load client configuration");
    return -1;
  };
  Arc::MCCLoader client_loader(client_config);
  logger.msg(Arc::INFO, "Client side MCCs are loaded");
  Arc::MCC* client_entry = client_loader["soap"];
  if(!client_entry) {
    logger.msg(Arc::ERROR, "Client chain does not have entry point");
    return -1;
  };
  
  // -------------------------------------------------------
  //    Compose request and send to aa service
  // -------------------------------------------------------
  //Compose request
  Arc::NS ns;
  ns["saml"] = SAML_NAMESPACE;
  ns["samlp"] = SAMLP_NAMESPACE;

  std::string cert("../../tests/echo/testcert.pem");
  std::string key("../../tests/echo/testkey-nopass.pem");
  std::string cafile("../../tests/echo/testcacert.pem");
  std::string cadir("../../tests/echo/certificates");
  Arc::Credential cred(cert, key, cadir, cafile);
  std::string local_dn = convert_dn(cred.GetDN());

  //Compose <samlp:AttributeQuery/>
  Arc::XMLNode attr_query(ns, "samlp:AttributeQuery");
  std::string sp_name("https://sp.com/SAML"); //TODO
  std::string query_id = Arc::UUID();
  attr_query.NewAttribute("ID") = query_id;
  Arc::Time t;
  std::string current_time = t.str(Arc::UTCTime);
  attr_query.NewAttribute("IssueInstant") = current_time;
  attr_query.NewAttribute("Version") = std::string("2.0");
  attr_query.NewChild("saml:Issuer") = sp_name;

  //<saml:Subject/>
  Arc::XMLNode subject = attr_query.NewChild("saml:Subject");
  Arc::XMLNode name_id = subject.NewChild("saml:NameID");
  name_id.NewAttribute("Format")=std::string("urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName");
  name_id = local_dn;

  //Add one or more <Attribute>s into AttributeQuery here, which means the Requestor would
  //get these <Attribute>s from AA
  //<saml:Attribute/>
  Arc::XMLNode attribute = attr_query.NewChild("saml:Attribute");
  attribute.NewAttribute("Name")=std::string("urn:oid:1.3.6.1.4.1.5923.1.1.1.6");
  attribute.NewAttribute("NameFormat")=std::string("urn:oasis:names:tc:SAML:2.0:attrname-format:uri");
  attribute.NewAttribute("FriendlyName")=std::string("eduPersonPrincipalName");

  Arc::init_xmlsec();
  Arc::XMLSecNode attr_query_secnd(attr_query);
  std::string attr_query_idname("ID");
  attr_query_secnd.AddSignatureTemplate(attr_query_idname, Arc::XMLSecNode::RSA_SHA1);
  if(attr_query_secnd.SignNode(key,cert)) {
    std::cout<<"Succeeded to sign the signature under <samlp:AttributeQuery/>"<<std::endl;
  }

  std::string str;
  attr_query.GetXML(str);
  std::cout<<"++++ "<<str<<std::endl;

  Arc::NS soap_ns;
  Arc::SOAPEnvelope envelope(soap_ns);
  envelope.NewChild(attr_query);
  Arc::PayloadSOAP *payload = new Arc::PayloadSOAP(envelope);

  std::string tmp;
  payload->GetXML(tmp);
  std::cout<<"SOAP request from Arc client: ++++++++++++++++"<<tmp<<std::endl;
 
  // Send request
  Arc::MessageContext context;
  Arc::Message reqmsg;
  Arc::Message repmsg;
  Arc::MessageAttributes attributes_in;
  Arc::MessageAttributes attributes_out;
  reqmsg.Payload(payload);
  reqmsg.Attributes(&attributes_in);
  reqmsg.Context(&context);
  repmsg.Attributes(&attributes_out);
  repmsg.Context(&context);

  Arc::MCC_Status status = client_entry->process(reqmsg,repmsg);
  if(!status) {
    logger.msg(Arc::ERROR, "Request failed");
    return -1;
  };
  logger.msg(Arc::INFO, "Request succeeded!!!");
  if(repmsg.Payload() == NULL) {
    logger.msg(Arc::ERROR, "There is no response");
    return -1;
  };

  Arc::PayloadSOAP* resp = NULL;
  try {
   resp = dynamic_cast<Arc::PayloadSOAP*>(repmsg.Payload());
  } catch(std::exception&) { };
  if(resp == NULL) {
    logger.msg(Arc::ERROR, "Response is not SOAP");
    delete repmsg.Payload();  Arc::final_xmlsec();
    return -1;
  };

  resp->GetXML(tmp);
  std::cout<<"SOAP resp from aa service: ++++++++++++++++"<<tmp<<std::endl;

  // -------------------------------------------------------
  //   Comsume the response from aa service
  // -------------------------------------------------------
  //
  //Consume the response from AA
  Arc::XMLNode attr_resp;
  
  //<samlp:Response/>
  attr_resp = (*resp).Body().Child(0);
 
  //TODO: metadata processing.
  //std::string aa_name = attr_resp["saml:Issuer"]; 
 
  //Check validity of the signature on <samlp:Response/>
  std::string resp_idname = "ID";
  std::string cafile1 = "../../tests/echo/testcacert.pem";
  std::string capath1 = "../../tests/echo/certificates";
  Arc::XMLSecNode attr_resp_secnode(attr_resp);
  if(attr_resp_secnode.VerifyNode(resp_idname, cafile1, capath1)) {
    logger.msg(Arc::INFO, "Succeeded to verify the signature under <samlp:Response/>");
  }
  else {
    logger.msg(Arc::ERROR, "Failed to verify the signature under <samlp:Response/>");
    delete repmsg.Payload(); Arc::final_xmlsec(); return -1;
  }
 
 
  //Check whether the "InResponseTo" is the same as the local ID
  std::string responseto_id = (std::string)(attr_resp.Attribute("InResponseTo"));
  if(query_id != responseto_id) {
    logger.msg(Arc::INFO, "The Response is not going to this end");
    delete repmsg.Payload(); Arc::final_xmlsec(); return -1;
  }

  std::string resp_time = attr_resp.Attribute("IssueInstant");

  //<samlp:Status/>
  std::string statuscode_value = attr_resp["samlp:Status"]["samlp:StatusCode"];
  if(statuscode_value == "urn:oasis:names:tc:SAML:2.0:status:Success")
    logger.msg(Arc::INFO, "The StatusCode is Success");

  //<saml:Assertion/>
  Arc::XMLNode assertion = attr_resp["saml:Assertion"];

  //TODO: metadata processing.
  //std::string aa_name = assertion["saml:Issuer"];
 
  //Check validity of the signature on <saml:Assertion/>
  std::string assertion_idname = "ID";
  std::string cafile2 = "../../tests/echo/testcacert.pem";
  std::string capath2 = "../../tests/echo/certificates";
  Arc::XMLSecNode assertion_secnode(assertion);
  if(assertion_secnode.VerifyNode(assertion_idname, cafile2, capath2)) {
    logger.msg(Arc::INFO, "Succeeded to verify the signature under <saml:Assertion/>");
  }
  else {
    logger.msg(Arc::ERROR, "Failed to verify the signature under <saml:Assertion/>");
    delete repmsg.Payload(); Arc::final_xmlsec(); return -1;
  }

  //<saml:Subject/>, TODO: 
  Arc::XMLNode subject_nd = assertion["saml:Subject"];

  //<saml:Condition/>, TODO: Condition checking
  Arc::XMLNode cond_nd = assertion["saml:Conditions"];

  //<saml:AttributeStatement/>
  Arc::XMLNode attr_statement = assertion["saml:AttributeStatement"];

  //<saml:Attribute/>
  Arc::XMLNode attr_nd;
  std::vector<std::string> attributes_value;
  for(int i=0;;i++) {
    attr_nd = attr_statement["saml:Attribute"][i];
    if(!attr_nd) break;

    std::string name = attr_nd.Attribute("Name");
    std::string nameformat = attr_nd.Attribute("NameFormat");
    std::string friendname = attribute.Attribute("FriendlyName");

    Arc::XMLNode attr_value = attr_nd["saml:AttributeValue"];

    std::string str;
    str.append("Name=").append(friendname).append(" Value=").append(attr_value);
    attributes_value.push_back(str);
  }

  for(int i=0; i<attributes_value.size(); i++) {
    std::cout<<"Attribute Value: "<<attributes_value[i]<<std::endl;  
  }

  delete repmsg.Payload();

  Arc::final_xmlsec();

  return 0;
}

Here is the call graph for this function: