Initial draft of protocol bootstrapper, reworked ACS lookup (again).
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 13 Aug 2010 04:23:23 +0000 (04:23 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Fri, 13 Aug 2010 04:23:23 +0000 (04:23 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/branches/REL_2@3293 cb58f699-b61c-0410-a6fe-9272a202ed29

26 files changed:
Shibboleth.sln
adfs/adfs.cpp
configs/Makefile.am
configs/protocols.xml [new file with mode: 0644]
schemas/Makefile.am
schemas/catalog.xml.in
schemas/shibboleth-2.0-native-sp-protocols.xsd [new file with mode: 0644]
shibsp/Application.cpp
shibsp/Application.h
shibsp/Makefile.am
shibsp/SPConfig.cpp
shibsp/SPConfig.h
shibsp/binding/ProtocolProvider.h [new file with mode: 0644]
shibsp/binding/impl/XMLProtocolProvider.cpp [new file with mode: 0644]
shibsp/handler/Handler.h
shibsp/handler/impl/AbstractHandler.cpp
shibsp/handler/impl/SAML2SessionInitiator.cpp
shibsp/handler/impl/Shib1SessionInitiator.cpp
shibsp/handler/impl/WAYFSessionInitiator.cpp
shibsp/impl/XMLServiceProvider.cpp
shibsp/shibsp-lite.vcxproj
shibsp/shibsp-lite.vcxproj.filters
shibsp/shibsp.vcxproj
shibsp/shibsp.vcxproj.filters
shibsp/util/SPConstants.cpp
shibsp/util/SPConstants.h

index 72675b5..112d8f2 100644 (file)
@@ -23,6 +23,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Schemas", "Schemas", "{0F17
                schemas\shibboleth-2.0-afp.xsd = schemas\shibboleth-2.0-afp.xsd
                schemas\shibboleth-2.0-attribute-map.xsd = schemas\shibboleth-2.0-attribute-map.xsd
                schemas\shibboleth-2.0-native-sp-config.xsd = schemas\shibboleth-2.0-native-sp-config.xsd
+               schemas\shibboleth-2.0-native-sp-protocols.xsd = schemas\shibboleth-2.0-native-sp-protocols.xsd
                schemas\shibboleth-2.0-sp-notify.xsd = schemas\shibboleth-2.0-sp-notify.xsd
                schemas\shibboleth-metadata-1.0.xsd = schemas\shibboleth-metadata-1.0.xsd
                schemas\shibboleth.xsd = schemas\shibboleth.xsd
@@ -52,6 +53,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{2543BC
                configs\native.logger.in = configs\native.logger.in
                configs\partialLogout.html = configs\partialLogout.html
                configs\postTemplate.html = configs\postTemplate.html
+               configs\protocols.xml = configs\protocols.xml
                configs\security-policy.xml = configs\security-policy.xml
                configs\sessionError.html = configs\sessionError.html
                configs\shibboleth2.xml = configs\shibboleth2.xml
index c8f4bdd..cac8e85 100644 (file)
@@ -370,24 +370,16 @@ pair<bool,long> ADFSSessionInitiator::run(SPRequest& request, string& entityID,
 
     if (!ACS) {
         pair<bool,unsigned int> index = getUnsignedInt("acsIndex", request, HANDLER_PROPERTY_MAP|HANDLER_PROPERTY_FIXED);
-        if (index.first) {
+        if (index.first)
             ACS = app.getAssertionConsumerServiceByIndex(index.second);
-            if (!ACS)
-                request.log(SPRequest::SPWarn, "invalid acsIndex property, using default ACS location");
-        }
-        if (!ACS) {
-            ACS = app.getAssertionConsumerServiceByBinding(WSFED_NS);
-            if (!ACS) {
-                m_log.error("unable to locate a compatible ACS");
-                throw ConfigurationException("Unable to locate an ADFS-compatible ACS in the configuration.");
-            }
-        }
     }
 
     // Validate the ACS for use with this protocol.
-    if (!XMLString::equals(getProtocolFamily(), ACS->getProtocolFamily())) {
-        m_log.error("configured or requested ACS has non-ADFS binding");
-        throw ConfigurationException("Configured or requested ACS has non-ADFS binding ($1).", params(1, ACS->getString("Binding").second));
+    if (!ACS || !XMLString::equals(getProtocolFamily(), ACS->getProtocolFamily())) {
+        request.log(SPRequest::SPWarn, "invalid acsIndex property, or non-ADFS ACS, using default ADFS ACS");
+        ACS = app.getAssertionConsumerServiceByProtocol(getProtocolFamily());
+        if (!ACS)
+            throw ConfigurationException("Unable to locate an ADFS-compatible ACS in the configuration.");
     }
 
     // Since we're not passing by index, we need to fully compute the return URL.
index 3618623..3b3f8de 100644 (file)
@@ -35,6 +35,7 @@ CONFIGFILES = \
        shibboleth2.xml \
        attribute-map.xml \
        attribute-policy.xml \
+       protocols.xml \
        security-policy.xml \
        example-metadata.xml \
        example-shibboleth2.xml \
diff --git a/configs/protocols.xml b/configs/protocols.xml
new file mode 100644 (file)
index 0000000..22d69c0
--- /dev/null
@@ -0,0 +1,42 @@
+<Protocols xmlns="urn:mace:shibboleth:2.0:native:sp:protocols">\r
+  \r
+  <Protocol id="SAML2">\r
+    <Service id="SSO" in="SAML2" out="SAML2">\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" path="/SAML2/Redirect" response="false"/>\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" path="/SAML2/POST" />\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" path="/SAML2/POST-SimpleSign" />\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" path="/SAML2/Artifact" artifact="true"/>\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" path="/SAML2/ECP" request="false"/>\r
+    </Service>\r
+    <Service id="Logout" in="SAML2" out="SAML2">\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" path="/SLO/SOAP" />\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" path="/SLO/Redirect" />\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" path="/SLO/POST" />\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" path="/SLO/Artifact" artifact="true"/>\r
+    </Service>\r
+    <Service id="NameIDMgmt" in="SAML2">\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" path="/NIM/SOAP" />\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" path="/NIM/Redirect" />\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" path="/NIM/POST" />\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" path="/NIM/Artifact" artifact="true"/>\r
+    </Service>\r
+    <Service id="ArtifactResolution" in="SAML2">\r
+      <Binding id="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" path="/Artifact/SOAP" />\r
+    </Service>\r
+  </Protocol>\r
+\r
+  <Protocol id="SAML1">\r
+    <Service id="SSO" in="SAML1" out="Shib1">\r
+      <Binding id="urn:oasis:names:tc:SAML:1.0:profiles:browser-post" path="/SAML/POST" />\r
+      <Binding id="urn:oasis:names:tc:SAML:1.0:profiles:artifact-01" path="/SAML/Artifact" artifact="true"/>\r
+    </Service>\r
+  </Protocol>\r
+\r
+  <Protocol id="ADFS">\r
+    <Service id="SSO" in="ADFS" out="ADFS">\r
+      <Binding id="http://schemas.xmlsoap.org/ws/2003/07/secext" path="/ADFS" />\r
+    </Service>\r
+    <Service id="Logout" out="ADFS"/>\r
+  </Protocol>\r
+\r
+</Protocols>\r
index 666c0e6..05bbace 100644 (file)
@@ -8,11 +8,12 @@ schemafiles = \
     shibboleth.xsd \
     shibboleth-metadata-1.0.xsd \
     shibboleth-2.0-native-sp-config.xsd \
+    shibboleth-2.0-native-sp-protocols.xsd \
+    shibboleth-2.0-sp-notify.xsd \
     shibboleth-2.0-afp.xsd \
     shibboleth-2.0-afp-mf-basic.xsd \
     shibboleth-2.0-afp-mf-saml.xsd \
     shibboleth-2.0-attribute-map.xsd \
-    shibboleth-2.0-sp-notify.xsd \
     WS-Trust.xsd
 
 pkgxml_DATA = \
index e59eb48..ca7c797 100644 (file)
@@ -2,11 +2,12 @@
 <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
     <system systemId="urn:mace:shibboleth:metadata:1.0" uri="@-PKGXMLDIR-@/shibboleth-metadata-1.0.xsd"/>
     <system systemId="urn:mace:shibboleth:2.0:native:sp:config" uri="@-PKGXMLDIR-@/shibboleth-2.0-native-sp-config.xsd"/>
+    <system systemId="urn:mace:shibboleth:2.0:native:sp:protocols" uri="@-PKGXMLDIR-@/shibboleth-2.0-native-sp-protocols.xsd"/>
+    <system systemId="urn:mace:shibboleth:2.0:sp:notify" uri="@-PKGXMLDIR-@/shibboleth-2.0-sp-notify.xsd"/>
     <system systemId="urn:mace:shibboleth:2.0:afp" uri="@-PKGXMLDIR-@/shibboleth-2.0-afp.xsd"/>
     <system systemId="urn:mace:shibboleth:2.0:afp:mf:basic" uri="@-PKGXMLDIR-@/shibboleth-2.0-afp-mf-basic.xsd"/>
     <system systemId="urn:mace:shibboleth:2.0:afp:mf:saml" uri="@-PKGXMLDIR-@/shibboleth-2.0-afp-mf-saml.xsd"/>
     <system systemId="urn:mace:shibboleth:2.0:attribute-map" uri="@-PKGXMLDIR-@/shibboleth-2.0-attribute-map.xsd"/>
-    <system systemId="urn:mace:shibboleth:2.0:sp:notify" uri="@-PKGXMLDIR-@/shibboleth-2.0-sp-notify.xsd"/>
     <system systemId="urn:mace:shibboleth:1.0" uri="@-PKGXMLDIR-@/shibboleth.xsd"/>
     <system systemId="http://schemas.xmlsoap.org/ws/2005/02/trust" uri="@-PKGXMLDIR-@/WS-Trust.xsd"/>
 </catalog>
diff --git a/schemas/shibboleth-2.0-native-sp-protocols.xsd b/schemas/shibboleth-2.0-native-sp-protocols.xsd
new file mode 100644 (file)
index 0000000..b45ccc6
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="US-ASCII"?>\r
+<schema targetNamespace="urn:mace:shibboleth:2.0:native:sp:protocols"\r
+        xmlns:prot="urn:mace:shibboleth:2.0:native:sp:protocols"\r
+        xmlns:ds="http://www.w3.org/2000/09/xmldsig#"\r
+        xmlns="http://www.w3.org/2001/XMLSchema"\r
+        attributeFormDefault="unqualified"\r
+        elementFormDefault="qualified"\r
+        blockDefault="substitution"\r
+        version="2.4">\r
+\r
+  <annotation>\r
+    <documentation>\r
+      Schema for specifying protocols, services, and bindings, and defaults for the locations of handlers.\r
+      First appearing in Shibboleth 2.4 release.\r
+    </documentation>\r
+  </annotation>\r
+  \r
+  <import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="xmldsig-core-schema.xsd" />\r
+\r
+  <simpleType name="string">\r
+    <restriction base="string">\r
+      <minLength value="1"/>\r
+    </restriction>\r
+  </simpleType>\r
+\r
+  <element name="Protocols">\r
+    <complexType>\r
+      <sequence>\r
+        <element name="Protocol" maxOccurs="unbounded">\r
+          <complexType>\r
+            <sequence>\r
+              <element name="Service" maxOccurs="unbounded">\r
+                <complexType>\r
+                  <sequence>\r
+                    <element name="Binding" minOccurs="0" maxOccurs="unbounded">\r
+                      <complexType>\r
+                        <attribute name="id" type="prot:string" use="required" />\r
+                        <attribute name="path" type="prot:string" use="required" />\r
+                        <attribute name="request" type="boolean" use="optional" />\r
+                        <attribute name="response" type="boolean" use="optional" />\r
+                        <attribute name="artifact" type="boolean" use="optional" />\r
+                      </complexType>\r
+                    </element>\r
+                  </sequence>\r
+                  <attribute name="id" type="prot:string" use="required" />\r
+                  <attribute name="in" type="prot:string" />\r
+                  <attribute name="out" type="prot:string "/>\r
+                </complexType>\r
+              </element>\r
+            </sequence>\r
+            <attribute name="id" type="prot:string" use="required" />\r
+          </complexType>\r
+        </element>\r
+        <element ref="ds:Signature" minOccurs="0"/>\r
+      </sequence>\r
+    </complexType>\r
+  </element>\r
+\r
+</schema>\r
index f13e1f1..d962cf1 100644 (file)
@@ -134,7 +134,7 @@ void Application::clearAttributeHeaders(SPRequest& request) const
         request.clearHeader(i->first.c_str(), i->second.c_str());
 }
 
-const Handler* Application::getAssertionConsumerServiceByBinding(const char* binding) const
+const Handler* Application::getAssertionConsumerServiceByProtocol(const XMLCh* protocol, const char* binding) const
 {
     auto_ptr_XMLCh b(binding);
     const vector<const Handler*>& handlers = getAssertionConsumerServicesByBinding(b.get());
index 29ae8e2..9b120ff 100644 (file)
@@ -283,12 +283,13 @@ namespace shibsp {
 
         /**
          * Returns an AssertionConsumerService Handler that supports
-         * a particular protocol binding.
+         * a particular protocol "family" and optional binding.
          *
-         * @param binding   a protocol binding identifier
+         * @param protocol  a protocol identifier
+         * @param binding   a binding identifier
          * @return a matching AssertionConsumerService, or nullptr
          */
-        virtual const Handler* getAssertionConsumerServiceByBinding(const char* binding) const;
+        virtual const Handler* getAssertionConsumerServiceByProtocol(const XMLCh* protocol, const char* binding=nullptr) const;
 
         /**
          * @deprecated
index 39ca0e2..b677beb 100644 (file)
@@ -63,6 +63,7 @@ attrresinclude_HEADERS = \
 
 bindinclude_HEADERS = \
        binding/ArtifactResolver.h \
+       binding/ProtocolProvider.h \
        binding/SOAPClient.h
 
 handinclude_HEADERS = \
@@ -113,6 +114,7 @@ common_sources = \
        attribute/SimpleAttribute.cpp \
        attribute/ScopedAttribute.cpp \
        attribute/XMLAttribute.cpp \
+       binding/impl/XMLProtocolProvider.cpp \
        handler/impl/AbstractHandler.cpp \
        handler/impl/AssertionConsumerService.cpp \
        handler/impl/AssertionLookup.cpp \
index 55e15e5..44240f2 100644 (file)
@@ -43,6 +43,7 @@
 #include "SPConfig.h"
 #include "TransactionLog.h"
 #include "attribute/Attribute.h"
+#include "binding/ProtocolProvider.h"
 #include "handler/LogoutInitiator.h"
 #include "handler/SessionInitiator.h"
 #include "remoting/ListenerService.h"
@@ -241,9 +242,14 @@ bool SPConfig::init(const char* catalog_path, const char* inst_prefix)
 #endif
 
     registerAttributeFactories();
-    registerHandlers();
-    registerLogoutInitiators();
-    registerSessionInitiators();
+
+    if (isEnabled(Handlers)) {
+        registerHandlers();
+        registerLogoutInitiators();
+        registerSessionInitiators();
+        registerProtocolProviders();
+    }
+
     registerServiceProviders();
 
 #ifndef SHIBSP_LITE
@@ -294,13 +300,17 @@ void SPConfig::term()
     setArtifactResolver(nullptr);
 #endif
 
-    ArtifactResolutionServiceManager.deregisterFactories();
-    AssertionConsumerServiceManager.deregisterFactories();
-    LogoutInitiatorManager.deregisterFactories();
-    ManageNameIDServiceManager.deregisterFactories();
-    SessionInitiatorManager.deregisterFactories();
-    SingleLogoutServiceManager.deregisterFactories();
-    HandlerManager.deregisterFactories();
+    if (isEnabled(Handlers)) {
+        ArtifactResolutionServiceManager.deregisterFactories();
+        AssertionConsumerServiceManager.deregisterFactories();
+        LogoutInitiatorManager.deregisterFactories();
+        ManageNameIDServiceManager.deregisterFactories();
+        SessionInitiatorManager.deregisterFactories();
+        SingleLogoutServiceManager.deregisterFactories();
+        HandlerManager.deregisterFactories();
+        ProtocolProviderManager.deregisterFactories();
+    }
+
     ServiceProviderManager.deregisterFactories();
     Attribute::deregisterFactories();
 
index ed3ddb5..918b102 100644 (file)
@@ -44,6 +44,7 @@ namespace shibsp {
     class SHIBSP_API Handler;
     class SHIBSP_API ListenerService;
     class SHIBSP_API RequestMapper;
+    class SHIBSP_API ProtocolProvider;
     class SHIBSP_API ServiceProvider;
     class SHIBSP_API SessionCache;
     class SHIBSP_API SessionInitiator;
@@ -251,6 +252,11 @@ namespace shibsp {
         xmltooling::PluginManager< Handler,std::string,std::pair<const xercesc::DOMElement*,const char*> > ManageNameIDServiceManager;
 
         /**
+         * Manages factories for ProtocolProvider plugins.
+         */
+        xmltooling::PluginManager<ProtocolProvider,std::string,const xercesc::DOMElement*> ProtocolProviderManager;
+
+        /**
          * Manages factories for RequestMapper plugins.
          */
         xmltooling::PluginManager<RequestMapper,std::string,const xercesc::DOMElement*> RequestMapperManager;
diff --git a/shibsp/binding/ProtocolProvider.h b/shibsp/binding/ProtocolProvider.h
new file mode 100644 (file)
index 0000000..68f7f21
--- /dev/null
@@ -0,0 +1,74 @@
+/*\r
+ *  Copyright 2010 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * @file shibsp/binding/ProtocolProvider.h\r
+ * \r
+ * Interface to protocol, binding, and default endpoint information.\r
+ */\r
+\r
+#ifndef __shibsp_protprov_h__\r
+#define __shibsp_protprov_h__\r
+\r
+#include <shibsp/base.h>\r
+\r
+#include <vector>\r
+#include <xmltooling/Lockable.h>\r
+\r
+namespace shibsp {\r
+\r
+    class SHIBSP_API PropertySet;\r
+\r
+    /**\r
+     * Interface to protocol, binding, and default endpoint information.\r
+     */\r
+       class SHIBSP_API ProtocolProvider : public virtual xmltooling::Lockable\r
+    {\r
+        MAKE_NONCOPYABLE(ProtocolProvider);\r
+    protected:\r
+        ProtocolProvider();\r
+    public:\r
+        virtual ~ProtocolProvider();\r
+    \r
+        /**\r
+         * Returns information about a service supported by a protocol, as a PropertySet.\r
+         *\r
+         * @param protocol  the name of a protocol\r
+         * @param service   the name of a service\r
+         * @return  a PropertySet associated with a service\r
+         */\r
+        virtual const PropertySet* getService(const char* protocol, const char* service) const=0;\r
+\r
+        /**\r
+         * Returns an ordered array of protocol bindings available for a specified service.\r
+         *\r
+         * @param protocol  the name of a protocol\r
+         * @param service   name of the protocol service\r
+         * @return  the array of bindings, each represented as a PropertySet\r
+         */\r
+        virtual const std::vector<const PropertySet*>& getBindings(const char* protocol, const char* service) const=0;\r
+    };\r
+\r
+    /**\r
+     * Registers ProtocolProvider classes into the runtime.\r
+     */\r
+    void SHIBSP_API registerProtocolProviders();\r
+\r
+    /** ProtocolProvider based on an XML configuration format. */\r
+    #define XML_PROTOCOL_PROVIDER "XML"\r
+};\r
+\r
+#endif /* __shibsp_protprov_h__ */\r
diff --git a/shibsp/binding/impl/XMLProtocolProvider.cpp b/shibsp/binding/impl/XMLProtocolProvider.cpp
new file mode 100644 (file)
index 0000000..327a18f
--- /dev/null
@@ -0,0 +1,230 @@
+/*\r
+ *  Copyright 2010 Internet2\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * XMLProtocolProvider.cpp\r
+ *\r
+ * XML-based protocol provider.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "exceptions.h"\r
+#include "binding/ProtocolProvider.h"\r
+#include "util/DOMPropertySet.h"\r
+#include "util/SPConstants.h"\r
+\r
+#include <map>\r
+#include <xmltooling/io/HTTPResponse.h>\r
+#include <xmltooling/util/NDC.h>\r
+#include <xmltooling/util/ReloadableXMLFile.h>\r
+#include <xmltooling/util/Threads.h>\r
+#include <xmltooling/util/XMLHelper.h>\r
+#include <xercesc/util/XMLUniDefs.hpp>\r
+\r
+using shibspconstants::SHIB2SPPROTOCOLS_NS;\r
+using namespace shibsp;\r
+using namespace xmltooling;\r
+using namespace std;\r
+\r
+namespace shibsp {\r
+\r
+    static const XMLCh _id[] =                  UNICODE_LITERAL_2(i,d);\r
+    static const XMLCh Binding[] =              UNICODE_LITERAL_7(B,i,n,d,i,n,g);\r
+    static const XMLCh Protocol[] =             UNICODE_LITERAL_8(P,r,o,t,o,c,o,l);\r
+    static const XMLCh Protocols[] =            UNICODE_LITERAL_9(P,r,o,t,o,c,o,l,s);\r
+    static const XMLCh Service[] =              UNICODE_LITERAL_7(S,e,r,v,i,c,e);\r
+\r
+#if defined (_MSC_VER)\r
+    #pragma warning( push )\r
+    #pragma warning( disable : 4250 )\r
+#endif\r
+\r
+    class SHIBSP_DLLLOCAL XMLProtocolProviderImpl : public DOMNodeFilter, DOMPropertySet\r
+    {\r
+    public:\r
+        XMLProtocolProviderImpl(const DOMElement* e, Category& log);\r
+        ~XMLProtocolProviderImpl() {\r
+            for (protmap_t::iterator i = m_map.begin(); i != m_map.end(); ++i) {\r
+                delete i->second.first;\r
+                for_each(i->second.second.begin(), i->second.second.end(), xmltooling::cleanup<PropertySet>());\r
+            }\r
+            if (m_document)\r
+                m_document->release();\r
+        }\r
+\r
+        void setDocument(DOMDocument* doc) {\r
+            m_document = doc;\r
+        }\r
+\r
+#ifdef SHIBSP_XERCESC_SHORT_ACCEPTNODE\r
+        short\r
+#else\r
+        FilterAction\r
+#endif\r
+        acceptNode(const DOMNode* node) const {\r
+            return FILTER_REJECT;\r
+        }\r
+\r
+    private:\r
+        DOMDocument* m_document;\r
+        // Map of protocol/service pair to a service propset plus an array of Binding propsets.\r
+        typedef map< pair<string,string>, pair< PropertySet*,vector<const PropertySet*> > > protmap_t;\r
+        protmap_t m_map;\r
+\r
+        friend class SHIBSP_DLLLOCAL XMLProtocolProvider;\r
+    };\r
+\r
+    class XMLProtocolProvider : public ProtocolProvider, public ReloadableXMLFile\r
+    {\r
+    public:\r
+        XMLProtocolProvider(const DOMElement* e)\r
+                : ReloadableXMLFile(e, Category::getInstance(SHIBSP_LOGCAT".ProtocolProvider.XML")), m_impl(nullptr) {\r
+            background_load(); // guarantees an exception or the policy is loaded\r
+        }\r
+\r
+        ~XMLProtocolProvider() {\r
+            shutdown();\r
+            delete m_impl;\r
+        }\r
+\r
+        const PropertySet* getService(const char* protocol, const char* service) const {\r
+            XMLProtocolProviderImpl::protmap_t::const_iterator i = m_impl->m_map.find(pair<string,string>(protocol,service));\r
+            return (i != m_impl->m_map.end()) ? i->second.first : nullptr;\r
+        }\r
+\r
+        const vector<const PropertySet*>& getBindings(const char* protocol, const char* service) const {\r
+            XMLProtocolProviderImpl::protmap_t::const_iterator i = m_impl->m_map.find(pair<string,string>(protocol,service));\r
+            if (i != m_impl->m_map.end())\r
+                return i->second.second;\r
+            throw ConfigurationException("ProtocolProvider can't return bindings for undefined protocol and service.");\r
+        }\r
+\r
+    protected:\r
+        pair<bool,DOMElement*> load(bool backup);\r
+        pair<bool,DOMElement*> background_load();\r
+\r
+    private:\r
+        XMLProtocolProviderImpl* m_impl;\r
+    };\r
+\r
+#if defined (_MSC_VER)\r
+    #pragma warning( pop )\r
+#endif\r
+\r
+    ProtocolProvider* SHIBSP_DLLLOCAL XMLProtocolProviderFactory(const DOMElement* const & e)\r
+    {\r
+        return new XMLProtocolProvider(e);\r
+    }\r
+}\r
+\r
+void SHIBSP_API shibsp::registerProtocolProviders()\r
+{\r
+    SPConfig::getConfig().ProtocolProviderManager.registerFactory(XML_PROTOCOL_PROVIDER, XMLProtocolProviderFactory);\r
+}\r
+\r
+ProtocolProvider::ProtocolProvider()\r
+{\r
+}\r
+\r
+ProtocolProvider::~ProtocolProvider()\r
+{\r
+}\r
+\r
+XMLProtocolProviderImpl::XMLProtocolProviderImpl(const DOMElement* e, Category& log) : m_document(nullptr)\r
+{\r
+#ifdef _DEBUG\r
+    xmltooling::NDC ndc("XMLProtocolProviderImpl");\r
+#endif\r
+    //typedef map< pair<string,string>, pair< PropertySet*,vector<const PropertySet*> > > protmap_t;\r
+\r
+    if (!XMLHelper::isNodeNamed(e, SHIB2SPPROTOCOLS_NS, Protocols))\r
+        throw ConfigurationException("XML ProtocolProvider requires prot:Protocols at root of configuration.");\r
+\r
+    e = XMLHelper::getFirstChildElement(e, SHIB2SPPROTOCOLS_NS, Protocol);\r
+    while (e) {\r
+        string id = XMLHelper::getAttrString(e, nullptr, _id);\r
+        if (!id.empty()) {\r
+            const DOMElement* svc = XMLHelper::getFirstChildElement(e, SHIB2SPPROTOCOLS_NS, Service);\r
+            while (svc) {\r
+                string svcid = XMLHelper::getAttrString(svc, nullptr, _id);\r
+                if (!svcid.empty()) {\r
+                    pair< PropertySet*,vector<const PropertySet*> >& entry = m_map[make_pair(id,svcid)];\r
+                    if (!entry.first) {\r
+                        // Wrap the Service in a propset.\r
+                        DOMPropertySet* svcprop = new DOMPropertySet();\r
+                        entry.first = svcprop;\r
+                        svcprop->load(svc, &log, this);\r
+\r
+                        // Walk the Bindings.\r
+                        const DOMElement* bind = XMLHelper::getFirstChildElement(svc, SHIB2SPPROTOCOLS_NS, Binding);\r
+                        while (bind) {\r
+                            DOMPropertySet* bindprop = new DOMPropertySet();\r
+                            entry.second.push_back(bindprop);\r
+                            bindprop->load(bind, &log, this);\r
+                            bind = XMLHelper::getNextSiblingElement(bind, SHIB2SPPROTOCOLS_NS, Binding);\r
+                        }\r
+                    }\r
+                }\r
+                svc = XMLHelper::getNextSiblingElement(svc, SHIB2SPPROTOCOLS_NS, Service);\r
+            }\r
+        }\r
+        e = XMLHelper::getNextSiblingElement(e, SHIB2SPPROTOCOLS_NS, Protocol);\r
+    }\r
+}\r
+\r
+\r
+pair<bool,DOMElement*> XMLProtocolProvider::load(bool backup)\r
+{\r
+    // Load from source using base class.\r
+    pair<bool,DOMElement*> raw = ReloadableXMLFile::load(backup);\r
+\r
+    // If we own it, wrap it.\r
+    XercesJanitor<DOMDocument> docjanitor(raw.first ? raw.second->getOwnerDocument() : nullptr);\r
+\r
+    XMLProtocolProviderImpl* impl = new XMLProtocolProviderImpl(raw.second, m_log);\r
+\r
+    // If we held the document, transfer it to the impl. If we didn't, it's a no-op.\r
+    impl->setDocument(docjanitor.release());\r
+\r
+    // Perform the swap inside a lock.\r
+    if (m_lock)\r
+        m_lock->wrlock();\r
+    SharedLock locker(m_lock, false);\r
+    delete m_impl;\r
+    m_impl = impl;\r
+\r
+\r
+    return make_pair(false,(DOMElement*)nullptr);\r
+}\r
+\r
+pair<bool,DOMElement*> XMLProtocolProvider::background_load()\r
+{\r
+    try {\r
+        return load(false);\r
+    }\r
+    catch (long& ex) {\r
+        if (ex == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)\r
+            m_log.info("remote resource (%s) unchanged", m_source.c_str());\r
+        if (!m_loaded && !m_backing.empty())\r
+            return load(true);\r
+        throw;\r
+    }\r
+    catch (exception&) {\r
+        if (!m_loaded && !m_backing.empty())\r
+            return load(true);\r
+        throw;\r
+    }\r
+}\r
index 624d983..3c3d4b0 100644 (file)
@@ -140,6 +140,21 @@ namespace shibsp {
     /** Registers Handler implementations. */
     void SHIBSP_API registerHandlers();
 
+    /** Handler for SAML 1.x SSO. */
+    #define SAML1_ASSERTION_CONSUMER_SERVICE "SAML1"
+
+    /** Handler for SAML 2.0 SSO. */
+    #define SAML20_ASSERTION_CONSUMER_SERVICE "SAML2"
+
+    /** Handler for SAML 2.0 SLO. */
+    #define SAML20_LOGOUT_HANDLER "SAML2"
+
+    /** Handler for SAML 2.0 NIM. */
+    #define SAML20_NAMEID_MGMT_SERVICE "SAML2"
+
+    /** Handler for SAML 2.0 Artifact Resolution. */
+    #define SAML20_ARTIFACT_RESOLUTION_SERVICE "SAML2"
+
     /** Handler for metadata generation. */
     #define METADATA_GENERATOR_HANDLER "MetadataGenerator"
 
index 0763746..58210fc 100644 (file)
@@ -118,13 +118,16 @@ void SHIBSP_API shibsp::registerHandlers()
 {
     SPConfig& conf=SPConfig::getConfig();
 
+    conf.AssertionConsumerServiceManager.registerFactory(SAML1_ASSERTION_CONSUMER_SERVICE, SAML1ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_ARTIFACT, SAML1ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_POST, SAML1ConsumerFactory);
+    conf.AssertionConsumerServiceManager.registerFactory(SAML20_ASSERTION_CONSUMER_SERVICE, SAML2ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_PAOS, SAML2ConsumerFactory);
 
+    conf.ArtifactResolutionServiceManager.registerFactory(SAML20_ARTIFACT_RESOLUTION_SERVICE, SAML2ArtifactResolutionFactory);
     conf.ArtifactResolutionServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2ArtifactResolutionFactory);
 
     conf.HandlerManager.registerFactory(SAML20_BINDING_URI, AssertionLookupFactory);
@@ -132,12 +135,14 @@ void SHIBSP_API shibsp::registerHandlers()
     conf.HandlerManager.registerFactory(STATUS_HANDLER, StatusHandlerFactory);
     conf.HandlerManager.registerFactory(SESSION_HANDLER, SessionHandlerFactory);
 
+    conf.SingleLogoutServiceManager.registerFactory(SAML20_LOGOUT_HANDLER, SAML2LogoutFactory);
     conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2LogoutFactory);
     conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_REDIRECT, SAML2LogoutFactory);
     conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2LogoutFactory);
     conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2LogoutFactory);
     conf.SingleLogoutServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2LogoutFactory);
 
+    conf.ManageNameIDServiceManager.registerFactory(SAML20_NAMEID_MGMT_SERVICE, SAML2NameIDMgmtFactory);
     conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_SOAP, SAML2NameIDMgmtFactory);
     conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_REDIRECT, SAML2NameIDMgmtFactory);
     conf.ManageNameIDServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2NameIDMgmtFactory);
index 9707700..3d903f3 100644 (file)
@@ -310,34 +310,24 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, string& entityID,
 
     if (!ACS) {
         if (ECP) {
-            ACS = app.getAssertionConsumerServiceByBinding(samlconstants::SAML20_BINDING_PAOS);
+            ACS = app.getAssertionConsumerServiceByProtocol(getProtocolFamily(), samlconstants::SAML20_BINDING_PAOS);
             if (!ACS)
                 throw ConfigurationException("Unable to locate PAOS response endpoint.");
         }
         else {
-            // Try fixed index property, or incoming binding set, or default, in order.
+            // Try fixed index property.
             pair<bool,unsigned int> index = getUnsignedInt("acsIndex", request, HANDLER_PROPERTY_MAP|HANDLER_PROPERTY_FIXED);
-            if (index.first) {
+            if (index.first)
                 ACS = app.getAssertionConsumerServiceByIndex(index.second);
-                if (!ACS)
-                    request.log(SPRequest::SPWarn, "invalid acsIndex property, using default ACS location");
-            }
-            /*
-            for (vector<string>::const_iterator b = m_incomingBindings.begin(); !ACS && b != m_incomingBindings.end(); ++b) {
-                ACS = app.getAssertionConsumerServiceByBinding(b->c_str());
-                if (ACS && !XMLString::equals(getProtocolFamily(), ACS->getProtocolFamily()))
-                    ACS = nullptr;
-            }
-            */
-            if (!ACS)
-                ACS = app.getDefaultAssertionConsumerService();
         }
     }
 
-    // Validate the ACS for use with this protocol.
-    if (!ECP && ACS && !XMLString::equals(getProtocolFamily(), ACS->getProtocolFamily())) {
-        m_log.error("configured or requested ACS has non-SAML 2.0 binding");
-        throw ConfigurationException("Configured or requested ACS has non-SAML 2.0 binding ($1).", params(1, ACS->getString("Binding").second));
+    // If we picked by index, validate the ACS for use with this protocol.
+    if (!ECP && (!ACS || !XMLString::equals(getProtocolFamily(), ACS->getProtocolFamily()))) {
+        request.log(SPRequest::SPWarn, "invalid acsIndex property, or non-SAML 2.0 ACS, using default SAML 2.0 ACS");
+        ACS = app.getAssertionConsumerServiceByProtocol(getProtocolFamily());
+        if (!ACS)
+            throw ConfigurationException("Unable to locate a SAML 2.0 ACS endpoint to use for response.");
     }
 
     // To invoke the request builder, the key requirement is to figure out how
@@ -361,21 +351,19 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, string& entityID,
 
             // Determine index to use.
             pair<bool,const XMLCh*> ix = pair<bool,const XMLCh*>(false,nullptr);
-            if (ACS) {
-               if (!strncmp(ACSloc.c_str(), "https", 5)) {
-                       ix = ACS->getXMLString("sslIndex", shibspconstants::ASCII_SHIB2SPCONFIG_NS);
-                       if (!ix.first)
-                               ix = ACS->getXMLString("index");
-               }
-               else {
+            if (!strncmp(ACSloc.c_str(), "https", 5)) {
+               ix = ACS->getXMLString("sslIndex", shibspconstants::ASCII_SHIB2SPCONFIG_NS);
+               if (!ix.first)
                        ix = ACS->getXMLString("index");
-               }
+            }
+            else {
+               ix = ACS->getXMLString("index");
             }
 
             return doRequest(
                 app, &request, request, entityID.c_str(),
                 ix.second,
-                ACS ? XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT) : false,
+                XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT),
                 nullptr, nullptr,
                 isPassive, forceAuthn,
                 acClass.first ? acClass.second : nullptr,
@@ -388,7 +376,7 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, string& entityID,
 
         // Since we're not passing by index, we need to fully compute the return URL and binding.
         // Compute the ACS URL. We add the ACS location to the base handlerURL.
-        prop = ACS ? ACS->getString("Location") : pair<bool,const char*>(false,nullptr);
+        prop = ACS->getString("Location");
         if (prop.first)
             ACSloc += prop.second;
 
@@ -404,8 +392,8 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, string& entityID,
         return doRequest(
             app, &request, request, entityID.c_str(),
             nullptr,
-            ACS ? XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT) : false,
-            ACSloc.c_str(), ACS ? ACS->getXMLString("Binding").second : nullptr,
+            XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT),
+            ACSloc.c_str(), ACS->getXMLString("Binding").second,
             isPassive, forceAuthn,
             acClass.first ? acClass.second : nullptr,
             acComp.first ? acComp.second : nullptr,
@@ -434,35 +422,31 @@ pair<bool,long> SAML2SessionInitiator::run(SPRequest& request, string& entityID,
     if (spQual.first)
         in.addmember("SPNameQualifier").string(spQual.second);
     if (acsByIndex.first && acsByIndex.second) {
-        if (ACS) {
-            // Determine index to use.
-            pair<bool,const char*> ix = pair<bool,const char*>(false,nullptr);
-               if (!strncmp(ACSloc.c_str(), "https", 5)) {
-                       ix = ACS->getString("sslIndex", shibspconstants::ASCII_SHIB2SPCONFIG_NS);
-                       if (!ix.first)
-                               ix = ACS->getString("index");
-               }
-               else {
+        // Determine index to use.
+        pair<bool,const char*> ix = pair<bool,const char*>(false,nullptr);
+        if (!strncmp(ACSloc.c_str(), "https", 5)) {
+               ix = ACS->getString("sslIndex", shibspconstants::ASCII_SHIB2SPCONFIG_NS);
+               if (!ix.first)
                        ix = ACS->getString("index");
-               }
-            in.addmember("acsIndex").string(ix.second);
-            if (XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT))
-                in.addmember("artifact").integer(1);
         }
+        else {
+               ix = ACS->getString("index");
+        }
+        in.addmember("acsIndex").string(ix.second);
+        if (XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT))
+            in.addmember("artifact").integer(1);
     }
     else {
         // Since we're not passing by index, we need to fully compute the return URL and binding.
         // Compute the ACS URL. We add the ACS location to the base handlerURL.
-        prop = ACS ? ACS->getString("Location") : pair<bool,const char*>(false,nullptr);
+        prop = ACS->getString("Location");
         if (prop.first)
             ACSloc += prop.second;
         in.addmember("acsLocation").string(ACSloc.c_str());
-        if (ACS) {
-            prop = ACS->getString("Binding");
-            in.addmember("acsBinding").string(prop.second);
-            if (XMLString::equals(prop.second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT))
-                in.addmember("artifact").integer(1);
-        }
+        prop = ACS->getString("Binding");
+        in.addmember("acsBinding").string(prop.second);
+        if (XMLString::equals(prop.second, samlconstants::SAML20_BINDING_HTTP_ARTIFACT))
+            in.addmember("artifact").integer(1);
     }
 
     if (isHandler) {
index e4ac2e8..6242c66 100644 (file)
@@ -149,35 +149,25 @@ pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, string& entityID,
             target = request.getRequestURL();
     }
 
-    // Since we're not passing by index, we need to fully compute the return URL.
     if (!ACS) {
-        // Try fixed index property, or incoming binding set, or default, in order.
+        // Try fixed index property.
         pair<bool,unsigned int> index = getUnsignedInt("acsIndex", request, HANDLER_PROPERTY_MAP|HANDLER_PROPERTY_FIXED);
-        if (index.first) {
+        if (index.first)
             ACS = app.getAssertionConsumerServiceByIndex(index.second);
-            if (!ACS)
-                request.log(SPRequest::SPWarn, "invalid acsIndex property, using default ACS location");
-        }
-        /*
-        for (vector<string>::const_iterator b = m_incomingBindings.begin(); !ACS && b != m_incomingBindings.end(); ++b) {
-            ACS = app.getAssertionConsumerServiceByBinding(b->c_str());
-            if (ACS && !XMLString::equals(getProtocolFamily(), ACS->getProtocolFamily()))
-                ACS = nullptr;
-        }
-        */
-        if (!ACS)
-            ACS = app.getDefaultAssertionConsumerService();
     }
 
-    // Validate the ACS for use with this protocol.
-    if (ACS && !XMLString::equals(getProtocolFamily(), ACS->getProtocolFamily())) {
-        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, ACS->getString("Binding").second));
+    // If we picked by index, validate the ACS for use with this protocol.
+    if (!ACS || !XMLString::equals(getProtocolFamily(), ACS->getProtocolFamily())) {
+        request.log(SPRequest::SPWarn, "invalid acsIndex property, or non-SAML 1.x ACS, using default SAML 1.x ACS");
+        ACS = app.getAssertionConsumerServiceByProtocol(getProtocolFamily());
+        if (!ACS)
+            throw ConfigurationException("Unable to locate a SAML 1.x ACS endpoint to use for response.");
     }
 
+    // Since we're not passing by index, we need to fully compute the return URL.
     // Compute the ACS URL. We add the ACS location to the base handlerURL.
     string ACSloc = request.getHandlerURL(target.c_str());
-    prop = ACS ? ACS->getString("Location") : pair<bool,const char*>(false,nullptr);
+    prop = ACS->getString("Location");
     if (prop.first)
         ACSloc += prop.second;
 
@@ -191,7 +181,7 @@ pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, string& entityID,
     }
 
     // Is the in-bound binding artifact?
-    bool artifactInbound = ACS ? XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML1_PROFILE_BROWSER_ARTIFACT) : false;
+    bool artifactInbound = XMLString::equals(ACS->getString("Binding").second, samlconstants::SAML1_PROFILE_BROWSER_ARTIFACT);
 
     m_log.debug("attempting to initiate session using Shibboleth with provider (%s)", entityID.c_str());
 
index 534a311..2947663 100644 (file)
@@ -122,39 +122,29 @@ pair<bool,long> WAYFSessionInitiator::run(SPRequest& request, string& entityID,
         discoveryURL = request.getRequestSettings().first->getString("discoveryURL");
     }
     
-    // Since we're not passing by index, we need to fully compute the return URL.
     if (!ACS) {
-        // Try fixed index property, or incoming binding set, or default, in order.
+        // Try fixed index property.
         pair<bool,unsigned int> index = getUnsignedInt("acsIndex", request, HANDLER_PROPERTY_MAP|HANDLER_PROPERTY_FIXED);
-        if (index.first) {
+        if (index.first)
             ACS = app.getAssertionConsumerServiceByIndex(index.second);
-            if (!ACS)
-                request.log(SPRequest::SPWarn, "invalid acsIndex property, using default ACS location");
-        }
-        /*
-        for (vector<string>::const_iterator b = m_incomingBindings.begin(); !ACS && b != m_incomingBindings.end(); ++b) {
-            ACS = app.getAssertionConsumerServiceByBinding(b->c_str());
-            if (ACS && !XMLString::equals(getProtocolFamily(), ACS->getProtocolFamily()))
-                ACS = nullptr;
-        }
-        */
-        if (!ACS)
-            ACS = app.getDefaultAssertionConsumerService();
     }
 
-    // Validate the ACS for use with this protocol.
-    if (ACS && !XMLString::equals(samlconstants::SAML11_PROTOCOL_ENUM, ACS->getProtocolFamily())) {
-        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, ACS->getString("Binding").second));
+    // If we picked by index, validate the ACS for use with this protocol.
+    if (!ACS || !XMLString::equals(samlconstants::SAML11_PROTOCOL_ENUM, ACS->getProtocolFamily())) {
+        request.log(SPRequest::SPWarn, "invalid acsIndex property, or non-SAML 1.x ACS, using default SAML 1.x ACS");
+        ACS = app.getAssertionConsumerServiceByProtocol(getProtocolFamily());
+        if (!ACS)
+            throw ConfigurationException("Unable to locate a SAML 1.x ACS endpoint to use for response.");
     }
 
     if (!discoveryURL.first)
         discoveryURL.second = m_url;
     m_log.debug("sending request to WAYF (%s)", discoveryURL.second);
 
+    // Since we're not passing by index, we need to fully compute the return URL.
     // Compute the ACS URL. We add the ACS location to the base handlerURL.
     string ACSloc = request.getHandlerURL(target.c_str());
-    prop = ACS ? ACS->getString("Location") : pair<bool,const char*>(false,nullptr);
+    prop = ACS->getString("Location");
     if (prop.first)
         ACSloc += prop.second;
 
index dd9c294..9b997c2 100644 (file)
@@ -168,7 +168,7 @@ namespace {
         const SessionInitiator* getSessionInitiatorById(const char* id) const;
         const Handler* getDefaultAssertionConsumerService() const;
         const Handler* getAssertionConsumerServiceByIndex(unsigned short index) const;
-        const Handler* getAssertionConsumerServiceByBinding(const char* binding) const;
+        const Handler* getAssertionConsumerServiceByProtocol(const XMLCh* protocol, const char* binding=nullptr) const;
         const vector<const Handler*>& getAssertionConsumerServicesByBinding(const XMLCh* binding) const;
         const Handler* getHandler(const char* path) const;
         void getHandlers(vector<const Handler*>& handlers) const;
@@ -225,9 +225,13 @@ namespace {
         const Handler* m_acsDefault;
 
         // maps binding strings to supporting consumer service(s)
-        typedef map< string,vector<const Handler*> > ACSBindingMap;
+        typedef map< xstring,vector<const Handler*> > ACSBindingMap;
         ACSBindingMap m_acsBindingMap;
 
+        // maps protocol strings to supporting consumer service(s)
+        typedef map< xstring,vector<const Handler*> > ACSProtocolMap;
+        ACSProtocolMap m_acsProtocolMap;
+
         // pointer to default session initiator
         const SessionInitiator* m_sessionInitDefault;
 
@@ -458,13 +462,14 @@ namespace {
     static const XMLCh _option[] =              UNICODE_LITERAL_6(o,p,t,i,o,n);
     static const XMLCh OutOfProcess[] =         UNICODE_LITERAL_12(O,u,t,O,f,P,r,o,c,e,s,s);
     static const XMLCh _path[] =                UNICODE_LITERAL_4(p,a,t,h);
+    static const XMLCh _ProtocolProvider[] =    UNICODE_LITERAL_16(P,r,o,t,o,c,o,l,P,r,o,v,i,d,e,r);
     static const XMLCh _provider[] =            UNICODE_LITERAL_8(p,r,o,v,i,d,e,r);
     static const XMLCh RelyingParty[] =         UNICODE_LITERAL_12(R,e,l,y,i,n,g,P,a,r,t,y);
     static const XMLCh _ReplayCache[] =         UNICODE_LITERAL_11(R,e,p,l,a,y,C,a,c,h,e);
     static const XMLCh _RequestMapper[] =       UNICODE_LITERAL_13(R,e,q,u,e,s,t,M,a,p,p,e,r);
     static const XMLCh RequestMap[] =           UNICODE_LITERAL_10(R,e,q,u,e,s,t,M,a,p);
     static const XMLCh SecurityPolicies[] =     UNICODE_LITERAL_16(S,e,c,u,r,i,t,y,P,o,l,i,c,i,e,s);
-    static const XMLCh SecurityPolicyProvider[] = UNICODE_LITERAL_22(S,e,c,u,r,i,t,y,P,o,l,i,c,y,P,r,o,v,i,d,e,r);
+    static const XMLCh _SecurityPolicyProvider[] = UNICODE_LITERAL_22(S,e,c,u,r,i,t,y,P,o,l,i,c,y,P,r,o,v,i,d,e,r);
     static const XMLCh _SessionCache[] =        UNICODE_LITERAL_12(S,e,s,s,i,o,n,C,a,c,h,e);
     static const XMLCh _SessionInitiator[] =    UNICODE_LITERAL_16(S,e,s,s,i,o,n,I,n,i,t,i,a,t,o,r);
     static const XMLCh _SingleLogoutService[] = UNICODE_LITERAL_19(S,i,n,g,l,e,L,o,g,o,u,t,S,e,r,v,i,c,e);
@@ -648,8 +653,11 @@ XMLApplication::XMLApplication(
                         continue;
                     }
                     handler = conf.AssertionConsumerServiceManager.newPlugin(bindprop.c_str(), make_pair(child, getId()));
-                    // Map by binding (may be > 1 per binding)
-                    m_acsBindingMap[bindprop].push_back(handler);
+                    // Map by binding and protocol (may be > 1 per protocol and binding)
+                    m_acsBindingMap[handler->getXMLString("Binding").second].push_back(handler);
+                    const XMLCh* protfamily = handler->getProtocolFamily();
+                    if (protfamily)
+                        m_acsProtocolMap[protfamily].push_back(handler);
                     m_acsIndexMap[handler->getUnsignedInt("index").second] = handler;
 
                     if (!hardACS) {
@@ -1218,17 +1226,23 @@ const Handler* XMLApplication::getAssertionConsumerServiceByIndex(unsigned short
     return m_base ? m_base->getAssertionConsumerServiceByIndex(index) : nullptr;
 }
 
-const Handler* XMLApplication::getAssertionConsumerServiceByBinding(const char* binding) const
+const Handler* XMLApplication::getAssertionConsumerServiceByProtocol(const XMLCh* protocol, const char* binding) const
 {
-    ACSBindingMap::const_iterator i=m_acsBindingMap.find(binding);
-    if (i != m_acsBindingMap.end()) return i->second.front();
-    return m_base ? m_base->getAssertionConsumerServiceByBinding(binding) : nullptr;
+    ACSProtocolMap::const_iterator i=m_acsProtocolMap.find(protocol);
+    if (i != m_acsProtocolMap.end() && !i->second.empty()) {
+        if (!binding || !*binding)
+            return i->second.front();
+        for (ACSProtocolMap::value_type::second_type::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
+            if (!strcmp(binding, (*j)->getString("Binding").second))
+                return *j;
+        }
+    }
+    return m_base ? m_base->getAssertionConsumerServiceByProtocol(protocol) : nullptr;
 }
 
 const vector<const Handler*>& XMLApplication::getAssertionConsumerServicesByBinding(const XMLCh* binding) const
 {
-    auto_ptr_char b(binding);
-    ACSBindingMap::const_iterator i=m_acsBindingMap.find(b.get());
+    ACSBindingMap::const_iterator i=m_acsBindingMap.find(binding);
     if (i != m_acsBindingMap.end())
         return i->second;
     return m_base ? m_base->getAssertionConsumerServicesByBinding(binding) : g_noHandlers;
@@ -1269,10 +1283,11 @@ XMLConfigImpl::acceptNode(const DOMNode* node) const
         XMLString::equals(name,_ArtifactMap) ||
         XMLString::equals(name,_Extensions) ||
         XMLString::equals(name,Listener) ||
+        XMLString::equals(name,_ProtocolProvider) ||
         XMLString::equals(name,_RequestMapper) ||
         XMLString::equals(name,_ReplayCache) ||
         XMLString::equals(name,SecurityPolicies) ||
-        XMLString::equals(name,SecurityPolicyProvider) ||
+        XMLString::equals(name,_SecurityPolicyProvider) ||
         XMLString::equals(name,_SessionCache) ||
         XMLString::equals(name,Site) ||
         XMLString::equals(name,_StorageService) ||
@@ -1564,7 +1579,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o
 
 #ifndef SHIBSP_LITE
         // Load security policies.
-        if (child = XMLHelper::getLastChildElement(e, SecurityPolicyProvider)) {
+        if (child = XMLHelper::getLastChildElement(e, _SecurityPolicyProvider)) {
             string t(XMLHelper::getAttrString(child, nullptr, _type));
             if (!t.empty()) {
                 log.info("building SecurityPolicyProvider of type %s...", t.c_str());
@@ -1576,7 +1591,7 @@ XMLConfigImpl::XMLConfigImpl(const DOMElement* e, bool first, const XMLConfig* o
         }
         else if (child = XMLHelper::getLastChildElement(e, SecurityPolicies)) {
             // For backward compatibility, wrap in a plugin element.
-            DOMElement* polwrapper = e->getOwnerDocument()->createElementNS(nullptr, SecurityPolicyProvider);
+            DOMElement* polwrapper = e->getOwnerDocument()->createElementNS(nullptr, _SecurityPolicyProvider);
             polwrapper->appendChild(child);
             log.info("building SecurityPolicyProvider of type %s...", XML_SECURITYPOLICY_PROVIDER);
             m_policy = conf.SecurityPolicyProviderManager.newPlugin(XML_SECURITYPOLICY_PROVIDER, polwrapper);
index 51acf69..6fbf284 100644 (file)
   <ItemGroup>\r
     <ClCompile Include="AbstractSPRequest.cpp" />\r
     <ClCompile Include="Application.cpp" />\r
+    <ClCompile Include="binding\impl\XMLProtocolProvider.cpp" />\r
     <ClCompile Include="handler\impl\LogoutInitiator.cpp" />\r
     <ClCompile Include="ServiceProvider.cpp" />\r
     <ClCompile Include="SPConfig.cpp" />\r
     <ClCompile Include="lite\SAMLConstants.cpp" />\r
   </ItemGroup>\r
   <ItemGroup>\r
+    <ClInclude Include="binding\ProtocolProvider.h" />\r
     <ClInclude Include="handler\LogoutInitiator.h" />\r
     <ClInclude Include="remoting\impl\SocketListener.h" />\r
     <ClInclude Include="AbstractSPRequest.h" />\r
index 1b75218..745daf8 100644 (file)
       <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD02}</UniqueIdentifier>\r
       <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>\r
     </Filter>\r
+    <Filter Include="Header Files\binding">\r
+      <UniqueIdentifier>{39717744-95cd-4251-8334-b2cb8a73a42c}</UniqueIdentifier>\r
+    </Filter>\r
+    <Filter Include="Source Files\binding">\r
+      <UniqueIdentifier>{b63c8dfb-c8d2-4717-a815-e554c72d43b1}</UniqueIdentifier>\r
+    </Filter>\r
+    <Filter Include="Source Files\binding\impl">\r
+      <UniqueIdentifier>{c1c74fdd-021f-4003-a8eb-c68091b6eec6}</UniqueIdentifier>\r
+    </Filter>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClCompile Include="AbstractSPRequest.cpp">\r
     <ClCompile Include="handler\impl\LogoutInitiator.cpp">\r
       <Filter>Source Files\handler\impl</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="binding\impl\XMLProtocolProvider.cpp">\r
+      <Filter>Source Files\binding\impl</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="remoting\impl\SocketListener.h">\r
     <ClInclude Include="handler\LogoutInitiator.h">\r
       <Filter>Header Files\handler</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="binding\ProtocolProvider.h">\r
+      <Filter>Header Files\binding</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="shibsp.rc">\r
index 03e747d..8fc4d3c 100644 (file)
   <ItemGroup>\r
     <ClCompile Include="AbstractSPRequest.cpp" />\r
     <ClCompile Include="Application.cpp" />\r
+    <ClCompile Include="binding\impl\XMLProtocolProvider.cpp" />\r
     <ClCompile Include="handler\impl\LogoutInitiator.cpp" />\r
     <ClCompile Include="impl\XMLSecurityPolicyProvider.cpp" />\r
     <ClCompile Include="ServiceProvider.cpp" />\r
     <ClCompile Include="handler\impl\WAYFSessionInitiator.cpp" />\r
   </ItemGroup>\r
   <ItemGroup>\r
+    <ClInclude Include="binding\ProtocolProvider.h" />\r
     <ClInclude Include="handler\LogoutInitiator.h" />\r
     <ClInclude Include="remoting\impl\SocketListener.h" />\r
     <ClInclude Include="AbstractSPRequest.h" />\r
index 696d2e7..c75241f 100644 (file)
     <ClCompile Include="handler\impl\LogoutInitiator.cpp">\r
       <Filter>Source Files\handler\impl</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="binding\impl\XMLProtocolProvider.cpp">\r
+      <Filter>Source Files\binding\impl</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="remoting\impl\SocketListener.h">\r
     <ClInclude Include="handler\LogoutInitiator.h">\r
       <Filter>Header Files\handler</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="binding\ProtocolProvider.h">\r
+      <Filter>Header Files\binding</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="shibsp.rc">\r
index 6614bad..4b62c9b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2007 Internet2
+ *  Copyright 2001-2010 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
 /**
  * SPConstants.cpp
  * 
- * SP XML namespace constants 
+ * Shibboleth SP XML constants.
  */
 
 #include "internal.h"
@@ -48,6 +48,13 @@ const XMLCh shibspconstants::SHIB2SPCONFIG_NS[] = // urn:mace:shibboleth:2.0:nat
   chLatin_s, chLatin_p, chColon, chLatin_c, chLatin_o, chLatin_n, chLatin_f, chLatin_i, chLatin_g, chNull
 };
 
+const XMLCh shibspconstants::SHIB2SPPROTOCOLS_NS[] = // urn:mace:shibboleth:2.0:native:sp:protocols
+{ chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,
+  chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,
+  chDigit_2, chPeriod, chDigit_0, chColon, chLatin_n, chLatin_a, chLatin_t, chLatin_i, chLatin_v, chLatin_e, chColon,
+  chLatin_s, chLatin_p, chColon, chLatin_p, chLatin_r, chLatin_o, chLatin_t, chLatin_o, chLatin_c, chLatin_o, chLatin_l, chLatin_s, chNull
+};
+
 const XMLCh shibspconstants::SHIB2ATTRIBUTEMAP_NS[] = // urn:mace:shibboleth:2.0:attribute-map
 { chLatin_u, chLatin_r, chLatin_n, chColon, chLatin_m, chLatin_a, chLatin_c, chLatin_e, chColon,
   chLatin_s, chLatin_h, chLatin_i, chLatin_b, chLatin_b, chLatin_o, chLatin_l, chLatin_e, chLatin_t, chLatin_h, chColon,
index 20f9cc2..9e16675 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2009 Internet2
+ *  Copyright 2001-2010 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
 /**
  * @file shibsp/util/SPConstants.h
  * 
- * Shibboleth SP XML constants. 
+ * Shibboleth SP XML constants.
  */
 
 #ifndef __shibsp_constants_h__
@@ -40,6 +40,9 @@ namespace shibspconstants {
     /** Shibboleth 2.0 SP configuration namespace ("urn:mace:shibboleth:2.0:native:sp:config") */
     extern SHIBSP_API const XMLCh SHIB2SPCONFIG_NS[];
 
+    /** Shibboleth 2.0 SP protocol provider namespace ("urn:mace:shibboleth:2.0:native:sp:protocols") */
+    extern SHIBSP_API const XMLCh SHIB2SPPROTOCOLS_NS[];
+
     /** Shibboleth 2.0 attribute mapping namespace ("urn:mace:shibboleth:2.0:attribute-map") */
     extern SHIBSP_API const XMLCh SHIB2ATTRIBUTEMAP_NS[];