https://issues.shibboleth.net/jira/browse/SSPCPP-244
[shibboleth/cpp-sp.git] / shibsp / handler / impl / WAYFSessionInitiator.cpp
index 0d5e541..aeeedf1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2007 Internet2
+ *  Copyright 2001-2009 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include "handler/AbstractHandler.h"
 #include "handler/SessionInitiator.h"
 
+#ifndef SHIBSP_LITE
+# include <saml/util/SAMLConstants.h>
+#else
+# include "lite/SAMLConstants.h"
+#endif
+
 #include <ctime>
 #include <xmltooling/XMLToolingConfig.h>
 #include <xmltooling/util/URLEncoder.h>
@@ -34,7 +40,6 @@
 using namespace shibsp;
 using namespace opensaml;
 using namespace xmltooling;
-using namespace log4cpp;
 using namespace std;
 
 namespace shibsp {
@@ -48,7 +53,7 @@ namespace shibsp {
     {
     public:
         WAYFSessionInitiator(const DOMElement* e, const char* appId)
-                : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator")), m_url(NULL) {
+                : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator.WAYF"), NULL, &m_remapper), m_url(NULL) {
             pair<bool,const char*> url = getString("URL");
             if (!url.first)
                 throw ConfigurationException("WAYF SessionInitiator requires a URL property.");
@@ -56,7 +61,7 @@ namespace shibsp {
         }
         virtual ~WAYFSessionInitiator() {}
         
-        pair<bool,long> run(SPRequest& request, const char* entityID=NULL, bool isHandler=true) const;
+        pair<bool,long> run(SPRequest& request, string& entityID, bool isHandler=true) const;
 
     private:
         const char* m_url;
@@ -73,50 +78,74 @@ namespace shibsp {
 
 };
 
-pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, const char* entityID, bool isHandler) const
+pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, string& entityID, bool isHandler) const
 {
     // The IdP CANNOT be specified for us to run. Otherwise, we'd be redirecting to a WAYF
     // anytime the IdP's metadata was wrong.
-    if (entityID && *entityID)
-        return make_pair(false,0);
+    if (!entityID.empty())
+        return make_pair(false,0L);
 
     string target;
+    string postData;
     const char* option;
     const Handler* ACS=NULL;
     const Application& app=request.getApplication();
+    pair<bool,const char*> discoveryURL = pair<bool,const char*>(true, m_url);
 
     if (isHandler) {
         option=request.getParameter("acsIndex");
         if (option) {
             ACS=app.getAssertionConsumerServiceByIndex(atoi(option));
             if (!ACS)
-                request.log(SPRequest::SPWarn, "invalid acsIndex specified in request, using default ACS location");
+                request.log(SPRequest::SPWarn, "invalid acsIndex specified in request, using acsIndex property");
         }
 
         option = request.getParameter("target");
         if (option)
             target = option;
-        recoverRelayState(request.getApplication(), request, target, false);
+        recoverRelayState(request.getApplication(), request, request, target, false);
+
+        option = request.getParameter("discoveryURL");
+        if (option)
+            discoveryURL.second = option;
     }
     else {
         // We're running as a "virtual handler" from within the filter.
         // The target resource is the current one and everything else is defaulted.
         target=request.getRequestURL();
+        discoveryURL = request.getRequestSettings().first->getString("discoveryURL");
     }
     
     // Since we're not passing by index, we need to fully compute the return URL.
     if (!ACS) {
-        pair<bool,unsigned int> index = getUnsignedInt("defaultACSIndex");
+        pair<bool,unsigned int> index = getUnsignedInt("acsIndex");
         if (index.first) {
             ACS = app.getAssertionConsumerServiceByIndex(index.second);
             if (!ACS)
-                request.log(SPRequest::SPWarn, "invalid defaultACSIndex, using default ACS location");
+                request.log(SPRequest::SPWarn, "invalid acsIndex property, using default ACS location");
         }
         if (!ACS)
             ACS = app.getDefaultAssertionConsumerService();
     }
 
-    m_log.debug("sending request to WAYF (%s)", m_url);
+    // Validate the ACS for use with this protocol.
+    pair<bool,const char*> ACSbinding = ACS ? ACS->getString("Binding") : pair<bool,const char*>(false,NULL);
+    if (ACSbinding.first) {
+        pair<bool,const char*> compatibleBindings = getString("compatibleBindings");
+        if (compatibleBindings.first && strstr(compatibleBindings.second, ACSbinding.second) == NULL) {
+            m_log.error("configured or requested ACS has non-SAML 1.x binding");
+            throw ConfigurationException("Configured or requested ACS has non-SAML 1.x binding ($1).", params(1, ACSbinding.second));
+        }
+        else if (strcmp(ACSbinding.second, samlconstants::SAML1_PROFILE_BROWSER_POST) &&
+                 strcmp(ACSbinding.second, samlconstants::SAML1_PROFILE_BROWSER_ARTIFACT)) {
+            m_log.error("configured or requested ACS has non-SAML 1.x binding");
+            throw ConfigurationException("Configured or requested ACS has non-SAML 1.x binding ($1).", params(1, ACSbinding.second));
+        }
+    }
+
+    if (!discoveryURL.first)
+        discoveryURL.second = m_url;
+    m_log.debug("sending request to WAYF (%s)", discoveryURL.second);
 
     // Compute the ACS URL. We add the ACS location to the base handlerURL.
     string ACSloc=request.getHandlerURL(target.c_str());
@@ -131,6 +160,8 @@ pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, const char* entity
             target = option;
     }
     preserveRelayState(request.getApplication(), request, target);
+    if (!isHandler)
+        preservePostData(request.getApplication(), request, request, target.c_str());
 
     // WAYF requires a target value.
     if (target.empty())
@@ -139,7 +170,7 @@ pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, const char* entity
     char timebuf[16];
     sprintf(timebuf,"%lu",time(NULL));
     const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
-    string req=string(m_url) + (strchr(m_url,'?') ? '&' : '?') + "shire=" + urlenc->encode(ACSloc.c_str()) +
+    string req=string(discoveryURL.second) + (strchr(discoveryURL.second,'?') ? '&' : '?') + "shire=" + urlenc->encode(ACSloc.c_str()) +
         "&time=" + timebuf + "&target=" + urlenc->encode(target.c_str()) +
         "&providerId=" + urlenc->encode(app.getString("entityID").second);