Convert role lookups to find_if algorithm.
[shibboleth/cpp-sp.git] / shibsp / handler / impl / SAML2SessionInitiator.cpp
index 57ee0e5..1e1e0e2 100644 (file)
@@ -32,7 +32,6 @@
 
 #ifndef SHIBSP_LITE
 # include <saml/SAMLConfig.h>
-# include <saml/binding/MessageEncoder.h>
 # include <saml/saml2/core/Protocols.h>
 # include <saml/saml2/metadata/EndpointManager.h>
 # include <saml/saml2/metadata/Metadata.h>
@@ -45,7 +44,6 @@ using namespace opensaml::saml2md;
 using namespace shibsp;
 using namespace opensaml;
 using namespace xmltooling;
-using namespace log4cpp;
 using namespace std;
 
 namespace shibsp {
@@ -109,7 +107,7 @@ namespace shibsp {
 };
 
 SAML2SessionInitiator::SAML2SessionInitiator(const DOMElement* e, const char* appId)
-    : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator")), m_appId(appId)
+    : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator.SAML2")), m_appId(appId)
 {
 #ifndef SHIBSP_LITE
     m_outgoing=NULL;
@@ -124,13 +122,13 @@ SAML2SessionInitiator::SAML2SessionInitiator(const DOMElement* e, const char* ap
         pair<bool,const XMLCh*> outgoing = getXMLString("outgoingBindings");
         if (outgoing.first) {
             m_outgoing = XMLString::replicate(outgoing.second);
+            XMLString::trim(m_outgoing);
         }
         else {
             // No override, so we'll install a default binding precedence.
             string prec = string(samlconstants::SAML20_BINDING_HTTP_REDIRECT) + ' ' + samlconstants::SAML20_BINDING_HTTP_POST + ' ' +
                 samlconstants::SAML20_BINDING_HTTP_POST_SIMPLESIGN + ' ' + samlconstants::SAML20_BINDING_HTTP_ARTIFACT;
             m_outgoing = XMLString::transcode(prec.c_str());
-            XMLString::trim(m_outgoing);
         }
 
         int pos;
@@ -142,9 +140,11 @@ SAML2SessionInitiator::SAML2SessionInitiator(const DOMElement* e, const char* ap
             m_bindings.push_back(start);
             try {
                 auto_ptr_char b(start);
-                MessageEncoder * encoder = SAMLConfig::getConfig().MessageEncoderManager.newPlugin(b.get(),e);
+                MessageEncoder * encoder = SAMLConfig::getConfig().MessageEncoderManager.newPlugin(
+                    b.get(),pair<const DOMElement*,const XMLCh*>(e,NULL)
+                    );
                 m_encoders[start] = encoder;
-                m_log.info("supporting outgoing binding (%s)", b.get());
+                m_log.debug("supporting outgoing binding (%s)", b.get());
             }
             catch (exception& ex) {
                 m_log.error("error building MessageEncoder: %s", ex.what());
@@ -198,7 +198,7 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
         if (option) {
             ACS = app.getAssertionConsumerServiceByIndex(atoi(option));
             if (!ACS)
-                throw ConfigurationException("AssertionConsumerService with index ($1) not found, check configuration.", params(1,option));
+                request.log(SPRequest::SPWarn, "invalid acsIndex specified in request, using default ACS location");
         }
 
         option = request.getParameter("target");
@@ -241,13 +241,24 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
 
     m_log.debug("attempting to initiate session using SAML 2.0 with provider (%s)", entityID);
 
-    // To invoke the request builder, the key requirement is to figure out how and whether
+    if (!ACS) {
+        pair<bool,unsigned int> index = getUnsignedInt("defaultACSIndex");
+        if (index.first) {
+            ACS = app.getAssertionConsumerServiceByIndex(index.second);
+            if (!ACS)
+                request.log(SPRequest::SPWarn, "invalid defaultACSIndex, using default ACS location");
+        }
+        if (!ACS)
+            ACS = app.getDefaultAssertionConsumerService();
+    }
+
+    // To invoke the request builder, the key requirement is to figure out how
     // to express the ACS, by index or value, and if by value, where.
 
     SPConfig& conf = SPConfig::getConfig();
     if (conf.isEnabled(SPConfig::OutOfProcess)) {
         if (!acsByIndex.first || acsByIndex.second) {
-            // Pass by Index. This also allows for defaulting it entirely and sending nothing.
+            // Pass by Index.
             if (isHandler) {
                 // We may already have RelayState set if we looped back here,
                 // but just in case target is a resource, we reset it back.
@@ -267,9 +278,6 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
         }
 
         // Since we're not passing by index, we need to fully compute the return URL and binding.
-        if (!ACS)
-            ACS = app.getDefaultAssertionConsumerService();
-
         // Compute the ACS URL. We add the ACS location to the base handlerURL.
         string ACSloc=request.getHandlerURL(target.c_str());
         pair<bool,const char*> loc=ACS ? ACS->getString("Location") : pair<bool,const char*>(false,NULL);
@@ -313,9 +321,6 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, const char* entit
     }
     else {
         // Since we're not passing by index, we need to fully compute the return URL and binding.
-        if (!ACS)
-            ACS = app.getDefaultAssertionConsumerService();
-
         // Compute the ACS URL. We add the ACS location to the base handlerURL.
         string ACSloc=request.getHandlerURL(target.c_str());
         pair<bool,const char*> loc=ACS ? ACS->getString("Location") : pair<bool,const char*>(false,NULL);
@@ -404,7 +409,7 @@ pair<bool,long> SAML2SessionInitiator::doRequest(
         throw MetadataException("Unable to locate metadata for identity provider ($entityID)",
             namedparams(1, "entityID", entityID));
     }
-    const IDPSSODescriptor* role=entity->getIDPSSODescriptor(samlconstants::SAML20P_NS);
+    const IDPSSODescriptor* role=find_if(entity->getIDPSSODescriptors(), isValidForProtocol(samlconstants::SAML20P_NS));
     if (!role) {
         m_log.error("unable to locate SAML 2.0 identity provider role for provider (%s)", entityID);
         return make_pair(false,0);
@@ -481,43 +486,9 @@ pair<bool,long> SAML2SessionInitiator::doRequest(
 
     auto_ptr_char dest(ep->getLocation());
 
-    // Check for signing.
-    const PropertySet* relyingParty = app.getRelyingParty(entity);
-    pair<bool,bool> flag = relyingParty->getBool("signRequests");
-    if ((flag.first && flag.second) || role->WantAuthnRequestsSigned()) {
-        CredentialResolver* credResolver=app.getCredentialResolver();
-        if (credResolver) {
-            Locker credLocker(credResolver);
-            // Fill in criteria to use.
-            MetadataCredentialCriteria mcc(*role);
-            mcc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
-            pair<bool,const XMLCh*> sigalg = relyingParty->getXMLString("signatureAlg");
-            if (sigalg.first)
-                mcc.setXMLAlgorithm(sigalg.second);
-            const Credential* cred = credResolver->resolve(&mcc);
-            if (cred) {
-                // Signed request.
-                long ret = encoder->encode(
-                    httpResponse,
-                    req.get(),
-                    dest.get(),
-                    entityID,
-                    relayState.c_str(),
-                    cred,
-                    sigalg.second,
-                    relyingParty->getXMLString("digestAlg").second
-                    );
-                req.release();  // freed by encoder
-                return make_pair(true,ret);
-            }
-            else {
-                m_log.warn("no signing credential resolved, leaving AuthnRequest unsigned");
-            }
-        }
-    }
-
-    // Unsigned request.
-    long ret = encoder->encode(httpResponse, req.get(), dest.get(), entityID, relayState.c_str());
+    long ret = sendMessage(
+        *encoder, req.get(), relayState.c_str(), dest.get(), role, app, httpResponse, role->WantAuthnRequestsSigned()
+        );
     req.release();  // freed by encoder
     return make_pair(true,ret);
 #else