Back to index

nordugrid-arc-nox  1.1.0~rc6
Public Member Functions | Protected Member Functions | Protected Attributes
SPService::Service_SP Class Reference

This is service which accepts HTTP request from user agent (web browser) in the client side and processes the functionality of Service Provider in SAML2 SSO profile --- composing <AuthnRequest> Note: the IdP name is provided by the user agent directely when it gives a request, instead of the WRYF(where are you from) or Discovery Service in other implementation. More...

#include <SPService.h>

Inheritance diagram for SPService::Service_SP:
Inheritance graph
[legend]
Collaboration diagram for SPService::Service_SP:
Collaboration graph
[legend]

List of all members.

Public Member Functions

 Service_SP (Arc::Config *cfg)
 Constructor.
virtual ~Service_SP (void)
virtual Arc::MCC_Status process (Arc::Message &, Arc::Message &)
 Service request processing routine.
bool RegistrationCollector (Arc::XMLNode &doc)
 Service specific registartion collector, used for generate service registartions.
virtual void AddSecHandler (Config *cfg, ArcSec::SecHandler *sechandler, const std::string &label="")
 Add security components/handlers to this MCC.
virtual std::string getID ()
 Service may implement own service identitifer gathering method.

Protected Member Functions

bool ProcessSecHandlers (Message &message, const std::string &label="") const
 Executes security handlers of specified queue.

Protected Attributes

Arc::NS ns_
Arc::Logger logger
 Logger object used to print messages generated by this class.
Arc::XMLNode metadata_node_
std::string sp_name_
std::string cert_file_
std::string privkey_file_
std::string endpoint_
std::string expiration_
std::map< std::string,
std::list< ArcSec::SecHandler * > > 
sechandlers_
 Set of labeled authentication and authorization handlers.

Detailed Description

This is service which accepts HTTP request from user agent (web browser) in the client side and processes the functionality of Service Provider in SAML2 SSO profile --- composing <AuthnRequest> Note: the IdP name is provided by the user agent directely when it gives a request, instead of the WRYF(where are you from) or Discovery Service in other implementation.

Definition at line 18 of file SPService.h.


Constructor & Destructor Documentation

Constructor.

Definition at line 146 of file SPService.cpp.

                                    :RegisteredService(cfg),logger(Arc::Logger::rootLogger, "SAML2SP") {
  Arc::XMLNode chain_node = (*cfg).Parent();
  Arc::XMLNode tls_node;
  for(int i = 0; ;i++) {
    tls_node = chain_node.Child(i);
    if(!tls_node) break;
    std::string tls_name = (std::string)(tls_node.Attribute("name"));
    if(tls_name == "tls.service") break;
  }
  //Use the private key file of the main chain 
  //for signing samlp:AuthnRequest
  cert_file_ = (std::string)(tls_node["CertificatePath"]);
  privkey_file_ = (std::string)(tls_node["KeyPath"]);
/*
  cert_file_ = (std::string)((*cfg)["CertificatePath"]);
  privkey_file_ = (std::string)((*cfg)["KeyPath"]);
*/
  sp_name_ = (std::string)((*cfg)["ServiceProviderName"]);
  logger.msg(Arc::INFO, "SP Service name is %s", sp_name_);
  std::string metadata_file = (std::string)((*cfg)["MetaDataLocation"]);
  logger.msg(Arc::INFO, "SAML Metadata is from %s", metadata_file);
  metadata_node_.ReadFromFile(metadata_file);

  if(!(Arc::init_xmlsec())) return;
}

Here is the call graph for this function:

SPService::Service_SP::~Service_SP ( void  ) [virtual]

Definition at line 172 of file SPService.cpp.

Here is the call graph for this function:


Member Function Documentation

void Arc::Service::AddSecHandler ( Config cfg,
ArcSec::SecHandler sechandler,
const std::string &  label = "" 
) [virtual, inherited]

Add security components/handlers to this MCC.

For more information please see description of MCC::AddSecHandler

Definition at line 14 of file Service.cpp.

                                                                                           {
    if(sechandler) {
        sechandlers_[label].push_back(sechandler); //need polishing to put the SecHandlerFactory->getinstance here
        XMLNode cn = (*cfg)["SecHandler"];
        Config cfg_(cn);
    }
}

Here is the caller graph for this function:

virtual std::string Arc::Service::getID ( ) [inline, virtual, inherited]

Service may implement own service identitifer gathering method.

This method return identifier of service which is used for registering it Information Services.

Reimplemented in ARex::ARexService.

Definition at line 69 of file Service.h.

{ return ""; };

Service request processing routine.

Implements Arc::MCCInterface.

Definition at line 176 of file SPService.cpp.

                                                                      {

  // Check authentication and authorization
  if(!ProcessSecHandlers(inmsg, "incoming")) {
    logger.msg(Arc::ERROR, "saml2SP: Unauthorized");
    return Arc::MCC_Status(Arc::GENERIC_ERROR);
  };
  // Both input and output are supposed to be HTTP 
  // Extracting payload
  Arc::PayloadRawInterface* inpayload = dynamic_cast<Arc::PayloadRawInterface*>(inmsg.Payload());
  if(!inpayload) {
    logger.msg(Arc::WARNING, "empty input payload");
  };

  //Analyzing http request from user agent

  std::string msg_content(inpayload->Content());
  //SP service is supposed to get two types of http content from user agent:
  //1. The IdP name, which user agent sends to SP, and then SP uses to generate AuthnRequest
  //2. The saml assertion, which user agent gets from IdP, and then sends to SP 
  
  if(msg_content.substr(0,4) == "http") { //If IdP name is given from client/useragent
    //Get the IdP name from the request
    //Here we require the user agent to provide the idp name instead of the 
    //WAYF(where are you from) or Discovery Service in some other implementation of SP
    //like Shibboleth

    std::string idp_name(msg_content);

    //Compose <samlp:AuthnRequest/>
    Arc::NS saml_ns;
    saml_ns["saml"] = SAML_NAMESPACE;
    saml_ns["samlp"] = SAMLP_NAMESPACE;
    Arc::XMLNode authn_request(saml_ns, "samlp:AuthnRequest");
    //std::string sp_name("https://squark.uio.no/shibboleth-sp");
    std::string sp_name = sp_name_;
    std::string req_id = Arc::UUID();
    authn_request.NewAttribute("ID") = req_id;
    Arc::Time t1;
    std::string current_time1 = t1.str(Arc::UTCTime);
    authn_request.NewAttribute("IssueInstant") = current_time1;
    authn_request.NewAttribute("Version") = std::string("2.0");

    //Get url of assertion consumer service from metadata
    std::string assertion_consumer_url;
    for(int i = 0;;i++) {
      Arc::XMLNode nd = metadata_node_.Child(i);
      if(!nd) break;
      if(sp_name == (std::string)(nd.Attribute("entityID"))) {
        for(int j = 0;; j++) {
          Arc::XMLNode sp_nd = nd.Child(j);
          if(!sp_nd) break;
          if(MatchXMLName(sp_nd,"SPSSODescriptor")) {
            for(int k = 0;;k++) {
              Arc::XMLNode assertionconsumer_nd = sp_nd.Child(k);
              if(!assertionconsumer_nd) break;        
              if(MatchXMLName(assertionconsumer_nd, "AssertionConsumerService")) {
                if((std::string)(assertionconsumer_nd.Attribute("Binding")) == "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
                  assertion_consumer_url = (std::string)(assertionconsumer_nd.Attribute("Location"));
              }
              if(!assertion_consumer_url.empty()) break;
            }
          }
          if(!assertion_consumer_url.empty()) break;
        }
      }
    }
    authn_request.NewAttribute("AssertionConsumerServiceURL") = assertion_consumer_url;

    //Get url of sso service from metadata
    std::string sso_url;
    for(int i = 0;;i++) {
      Arc::XMLNode nd = metadata_node_.Child(i);
      if(!nd) break;
      if(idp_name == (std::string)(nd.Attribute("entityID"))) {
        for(int j = 0;; j++) {
          Arc::XMLNode idp_nd = nd.Child(j);
          if(!idp_nd) break;
          if(MatchXMLName(idp_nd,"IDPSSODescriptor")) {
            for(int k = 0;;k++) {
              Arc::XMLNode sso_nd = idp_nd.Child(k);
              if(!sso_nd) break;
              if(MatchXMLName(sso_nd, "SingleSignOnService")) {
                if((std::string)(sso_nd.Attribute("Binding")) == "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect")
                  sso_url = (std::string)(sso_nd.Attribute("Location"));
              }
              if(!sso_url.empty()) break;
            }
          }
          if(!sso_url.empty()) break;
        }
      }
    }
    authn_request.NewAttribute("Destination") = sso_url;
    authn_request.NewAttribute("ProtocolBinding") = std::string("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
    authn_request.NewChild("saml:Issuer") = sp_name;

    Arc::XMLNode nameid_policy = authn_request.NewChild("samlp:NameIDPolicy");
    nameid_policy.NewAttribute("AllowCreate") = std::string("1");

    bool must_signed = true; //TODO: get the information from metadata
    std::string authnRequestQuery;
    std::string query = "SAMLRequest=" + BuildDeflatedQuery(authn_request);
    logger.msg(Arc::DEBUG,"AuthnRequest after deflation: %s", query.c_str());
    if(must_signed) {
      //SP service uses it's private key to sign the AuthnRequest,
      //then after User Agent redirecting AuthnRequest to IdP, 
      //IdP will verify the signature by picking up SP's certificate
      //from IdP's metadata.
      logger.msg(Arc::DEBUG,"Using private key file to sign: %s", privkey_file_.c_str());
      authnRequestQuery = SignQuery(query, Arc::RSA_SHA1, privkey_file_);
      logger.msg(Arc::DEBUG,"After signature: %s", authnRequestQuery.c_str());
    }
    else authnRequestQuery = query;

#if 0
    //Verify the signature
    std::string cert_str = get_cert_str(cert_file_.c_str());
    std::cout<<"Cert to sign AuthnRequest: "<<cert_str<<std::endl;
    if(VerifyQuery(authnRequestQuery, cert_str)) {
      std::cout<<"Succeeded to verify the signature on AuthnRequest"<<std::endl;
    }
    else { std::cout<<"Failed to verify the signature on AuthnRequest"<<std::endl; }
#endif

    std::string authnRequestUrl;
    authnRequestUrl = sso_url + "?" + authnRequestQuery;

    //Return the composed url back to user agent through http
    Arc::PayloadRaw* outpayload = NULL;
    outpayload = new Arc::PayloadRaw;
    outpayload->Insert(authnRequestUrl.c_str(),0, authnRequestUrl.size());
    //outmsg.Attributes()->set("HTTP:CODE","302");
    //outmsg.Attributes()->set("HTTP:REASON","Moved Temporarily");
    delete outmsg.Payload(outpayload);
  }
  else {  
    //The http content should be <saml:EncryptedAssertion/> or <saml:Assertion/>
    //Decrypt the assertion (if it is encrypted) by using SP's private key (the key is the 
    //same as the one for the main message chain)
    //std::cout<<"saml assertion from peer side: "<<msg_content<<std::endl;
    Arc::XMLNode assertion_nd(msg_content);
    if(MatchXMLName(assertion_nd, "EncryptedAssertion")) {
      //Decrypt the encrypted saml assertion
      std::string saml_assertion;
      assertion_nd.GetXML(saml_assertion);
      logger.msg(Arc::DEBUG,"Encrypted saml assertion: %s", saml_assertion.c_str());

      Arc::XMLSecNode sec_assertion_nd(assertion_nd);
      Arc::XMLNode decrypted_assertion_nd;

      bool r = sec_assertion_nd.DecryptNode(privkey_file_, decrypted_assertion_nd);
      if(!r) { 
        logger.msg(Arc::ERROR,"Can not decrypt the EncryptedAssertion from saml response"); 
        return Arc::MCC_Status(); 
      }

      std::string decrypted_saml_assertion;
      decrypted_assertion_nd.GetXML(decrypted_saml_assertion);
      logger.msg(Arc::DEBUG,"Decrypted SAML Assertion: %s", decrypted_saml_assertion.c_str());
     
      //Decrypt the <saml:EncryptedID/> if it exists in the above saml assertion
      Arc::XMLNode nameid_nd = decrypted_assertion_nd["saml:Subject"]["saml:EncryptedID"];
      std::string nameid;
      nameid_nd.GetXML(nameid);
      logger.msg(Arc::DEBUG,"Encrypted name id: %s", nameid.c_str());

      Arc::XMLSecNode sec_nameid_nd(nameid_nd);
      Arc::XMLNode decrypted_nameid_nd;
      r = sec_nameid_nd.DecryptNode(privkey_file_, decrypted_nameid_nd);
      if(!r) { logger.msg(Arc::ERROR,"Can not decrypt the EncryptedID from saml assertion"); return Arc::MCC_Status(); }

      std::string decrypted_nameid;
      decrypted_nameid_nd.GetXML(decrypted_nameid);
      logger.msg(Arc::DEBUG,"Decrypted SAML NameID: %s", decrypted_nameid.c_str());

      //Replace the <saml:EncryptedID/> with <saml:NameID/>
      nameid_nd.Replace(decrypted_nameid_nd);

      //Check the <saml:Condition/> <saml:AuthnStatement/> 
      //and <saml:/Subject> part of saml assertion
      XMLNode subject   = decrypted_assertion_nd["saml:Subject"];
      XMLNode conditions = decrypted_assertion_nd["saml:Conditions"];
      XMLNode authnstatement = decrypted_assertion_nd["saml:AuthnStatement"];

      std::string notbefore_str = (std::string)(conditions.Attribute("NotBefore"));
      Time notbefore = notbefore_str;
      std::string notonorafter_str = (std::string)(conditions.Attribute("NotOnOrAfter"));
      Time notonorafter = notonorafter_str;
      Time now = Time();
      if(!notbefore_str.empty() && notbefore >= now) {
        logger.msg(Arc::ERROR,"saml:Conditions, current time is before the start time"); 
        return Arc::MCC_Status(); 
      } else if (!notonorafter_str.empty() && notonorafter < now) {
        logger.msg(Arc::ERROR,"saml:Conditions, current time is after the end time"); 
        return Arc::MCC_Status(); 
      }     

      XMLNode subject_confirmation = subject["saml:SubjectConfirmation"];
      std::string confirm_method = (std::string)(subject_confirmation.Attribute("Method"));
      if(confirm_method == "urn:oasis:names:tc:SAML:2.0:cm:bearer") {
        //TODO
      } else if (confirm_method == "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key") {
        //TODO
      }
      notbefore_str = (std::string)(subject_confirmation.Attribute("NotBefore"));
      notbefore = notbefore_str;
      notonorafter_str = (std::string)(subject_confirmation.Attribute("NotOnOrAfter"));
      notonorafter = notonorafter_str;
      now = Time();
      if(!notbefore_str.empty() && notbefore >= now) {
        logger.msg(Arc::ERROR,"saml:Subject, current time is before the start time");
        return Arc::MCC_Status();
      } else if (!notonorafter_str.empty() && notonorafter < now) {
        logger.msg(Arc::ERROR,"saml:Subject, current time is after the end time");
        return Arc::MCC_Status();
      }
      //TODO: "InResponseTo", need to recorde the ID?

      
  
      //Record the saml assertion into message context, this information
      //will be checked by saml2sso_serviceprovider handler later to decide
      //whether authorize the incoming message which is from the same session
      //as this saml2sso process

      SAMLAssertionSecAttr* sattr = new SAMLAssertionSecAttr(decrypted_assertion_nd);
      inmsg.Auth()->set("SAMLAssertion", sattr);

      Arc::PayloadRaw* outpayload = NULL;
      outpayload = new Arc::PayloadRaw;
      //std::string authorization_info("SAML2SSO process succeeded");
      //outpayload->Insert(authorization_info.c_str(), 0, authorization_info.size());
      delete outmsg.Payload(outpayload);
    }
    else if(MatchXMLName(assertion_nd, "Assertion")) {

    }
    else {
      logger.msg(Arc::ERROR,"Can not get saml:Assertion or saml:EncryptedAssertion from IdP");
      Arc::MCC_Status();
    }
  }

  return Arc::MCC_Status(Arc::STATUS_OK);
}

Here is the call graph for this function:

bool Arc::Service::ProcessSecHandlers ( Message message,
const std::string &  label = "" 
) const [protected, inherited]

Executes security handlers of specified queue.

For more information please see description of MCC::ProcessSecHandlers

Definition at line 22 of file Service.cpp.

                                                                              {
    std::map<std::string,std::list<ArcSec::SecHandler*> >::const_iterator q = sechandlers_.find(label);
    if(q == sechandlers_.end()) {
        logger.msg(DEBUG, "No security processing/check requested for '%s'", label);
        return true;
    }

    std::list<ArcSec::SecHandler*>::const_iterator h = q->second.begin();
    for(;h!=q->second.end();++h) {
        const ArcSec::SecHandler* handler = *h;
        if(handler) if(!(handler->Handle(&message))) {
            logger.msg(DEBUG, "Security processing/check for '%s' failed", label);
            return false;
        }
    }
    logger.msg(DEBUG, "Security processing/check for '%s' passed", label);
    return true;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Service specific registartion collector, used for generate service registartions.

In implemented service this method should generate GLUE2 document with part of service description which service wishes to advertise to Information Services.

Reimplemented from Arc::Service.

Definition at line 424 of file SPService.cpp.

                                                      {
  Arc::NS isis_ns; isis_ns["isis"] = "http://www.nordugrid.org/schemas/isis/2008/08";
  Arc::XMLNode regentry(isis_ns, "RegEntry");
  regentry.NewChild("SrcAdv").NewChild("Type") = "org.nordugrid.security.saml";
  regentry.New(doc);
  return true;
}

Here is the call graph for this function:


Member Data Documentation

std::string SPService::Service_SP::cert_file_ [protected]

Definition at line 25 of file SPService.h.

std::string SPService::Service_SP::endpoint_ [protected]

Definition at line 27 of file SPService.h.

std::string SPService::Service_SP::expiration_ [protected]

Definition at line 28 of file SPService.h.

Logger object used to print messages generated by this class.

Reimplemented from Arc::Service.

Definition at line 22 of file SPService.h.

Definition at line 23 of file SPService.h.

Definition at line 21 of file SPService.h.

std::string SPService::Service_SP::privkey_file_ [protected]

Definition at line 26 of file SPService.h.

std::map<std::string,std::list<ArcSec::SecHandler*> > Arc::Service::sechandlers_ [protected, inherited]

Set of labeled authentication and authorization handlers.

MCC calls sequence of handlers at specific point depending on associated identifier. in most aces those are "in" and "out" for incoming and outgoing messages correspondingly.

Definition at line 40 of file Service.h.

std::string SPService::Service_SP::sp_name_ [protected]

Definition at line 24 of file SPService.h.


The documentation for this class was generated from the following files: