Improve property inheritance, first batch of SessionInitiators, rename providerId.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Wed, 4 Apr 2007 04:04:33 +0000 (04:04 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Wed, 4 Apr 2007 04:04:33 +0000 (04:04 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2215 cb58f699-b61c-0410-a6fe-9272a202ed29

20 files changed:
configs/shibboleth.xml.in
nsapi_shib/nsapi_shib.cpp
schemas/shibboleth-spconfig-2.0.xsd
shibsp/Makefile.am
shibsp/attribute/resolver/impl/SimpleAttributeResolver.cpp
shibsp/binding/impl/ArtifactResolver.cpp
shibsp/handler/AbstractHandler.h
shibsp/handler/AssertionConsumerService.h
shibsp/handler/Handler.h
shibsp/handler/impl/AbstractHandler.cpp
shibsp/handler/impl/AssertionConsumerService.cpp
shibsp/handler/impl/ChainingSessionInitiator.cpp [new file with mode: 0644]
shibsp/handler/impl/SAML2Consumer.cpp
shibsp/handler/impl/Shib1SessionInitiator.cpp [new file with mode: 0644]
shibsp/impl/XMLServiceProvider.cpp
shibsp/shibsp.vcproj
shibsp/util/DOMPropertySet.cpp
shibsp/util/DOMPropertySet.h
shibsp/util/PropertySet.h
util/samlquery.cpp

index ec09c7d..6f07abf 100644 (file)
        Resource requests are mapped in the Local section into an applicationId that
        points into to this section.
        -->
-       <Applications id="default" policyId="default" providerId="https://sp.example.org/shibboleth"
+       <Applications id="default" policyId="default" entityID="https://sp.example.org/shibboleth"
                homeURL="https://sp.example.org/index.html">
 
                <!--
                        handlerURL="/Shibboleth.sso" handlerSSL="false" idpHistory="true" idpHistoryDays="7">
                        
                        <!--
-                       SessionInitiators handle session requests and relay them to a WAYF or directly
-                       to an IdP, if possible. Automatic session setup will use the default or first
+                       SessionInitiators handle session requests and relay them to a Discovery page,
+                       or to an IdP if possible. Automatic session setup will use the default or first
                        element (or requireSessionWith can specify a specific id to use). Lazy sessions
-                       can be started with any initiator by redirecting to it. The only Binding supported
-                       is the "urn:mace:shibboleth:sp:1.3:SessionInit" lazy session profile using query
-                       string parameters:
-                        *  target      the resource to direct back to later (or homeURL will be used)
+                       can be started with any initiator by redirecting to it using query string parameters:
+                       
+                        *  entityID    optional direct invocation of a specific IdP
+                        *  target      optional resource to direct back to later (or homeURL will be used)
                         *  acsIndex    optional index of an ACS to use on the way back in
-                        *  providerId  optional direct invocation of a specific IdP
                        -->
+
+                       <!-- Default example directs to a specific IdP's Shibboleth 1.x SSO service. -->
+                       <SessionInitiator type="Shibboleth" Location="/Login" isDefault="true" id="example.org"
+                               relayState="cookie" entityID="https://idp.example.org/shibboleth"/>
                        
-                       <!-- This default example directs users to a specific IdP's SSO service. -->
-                       <SessionInitiator isDefault="true" id="default" Location="/Login"
-                               Binding="urn:mace:shibboleth:sp:1.3:SessionInit" relayState="cookie"
-                               wayfURL="https://idp.example.org/shibboleth-idp/SSO"
-                               wayfBinding="urn:mace:shibboleth:1.0:profiles:AuthnRequest"/>
+                       <!-- An example using the Shibboleth 1.x protocol but with an external WAYF. -->
+                       <SessionInitiator type="Shibboleth" Location="/WAYF" id="wayf"
+                               relayState="cookie" wayfURL="https://wayf.example.org/WAYF"/>
                                
                        <!--
-                       md:AssertionConsumerService elements replace the old shireURL function with an
-                       explicit handler for particular profiles, such as SAML 1.1 POST or Artifact.
-                       The isDefault and index attributes are used when sessions are initiated
-                       to determine how to tell the IdP where and how to return the response.
+                       md:AssertionConsumerService elements handle specific SSO protocol bindings,
+                       such as SAML 2.0 POST or SAML 1.1 Artifact. The isDefault and index attributes
+                       are used when sessions are initiated to determine how to tell the IdP where and
+                       how to return the response.
                        -->
                        <md:AssertionConsumerService Location="/SAML/POST" isDefault="true" index="1"
                                Binding="urn:oasis:names:tc:SAML:1.0:profiles:browser-post"/>
index 56a5976..d9717d2 100644 (file)
@@ -439,6 +439,7 @@ public:
     void unlock() { m_stKey->setData(NULL); m_propsKey->setData(NULL); m_mapper->unlock(); }
     Settings getSettings(const SPRequest& request) const;
     
+    void setParent(const PropertySet*) {}
     pair<bool,bool> getBool(const char* name, const char* ns=NULL) const;
     pair<bool,const char*> getString(const char* name, const char* ns=NULL) const;
     pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const;
index c145ce2..5d222f1 100644 (file)
                                <element ref="conf:Application" minOccurs="0" maxOccurs="unbounded"/>\r
                        </sequence>\r
                        <attribute name="id" type="conf:string" fixed="default"/>\r
-                       <attribute name="providerId" type="anyURI" use="required"/>\r
+                       <attribute name="entityID" type="anyURI" use="required"/>\r
                        <attribute name="policyId" type="conf:string" use="required"/>\r
-                       <attribute name="homeURL" type="anyURI"/>\r
+                       <attribute name="homeURL" type="anyURI" default="/"/>\r
                        <attribute name="attributeIds" type="conf:listOfStrings"/>\r
                <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
                                <element name="CredentialResolver" type="conf:PluggableType" minOccurs="0"/>\r
                        </sequence>\r
                        <attribute name="id" type="conf:string" use="required"/>\r
-                       <attribute name="providerId" type="anyURI"/>\r
+                       <attribute name="entityID" type="anyURI"/>\r
                        <attribute name="policyId" type="conf:string"/>\r
-                       <attribute name="homeURL" type="anyURI"/>\r
+                       <attribute name="homeURL" type="anyURI" default="/"/>\r
                        <attribute name="attributeIds" type="conf:listOfStrings"/>\r
                        <anyAttribute namespace="##other" processContents="lax"/>\r
                </complexType>\r
 \r
        <element name="SessionInitiator">\r
                <annotation>\r
-                       <documentation>Used to specify handlers that can issue AuthnRequests</documentation>\r
+                       <documentation>Used to specify handlers that can issue AuthnRequests or perform discovery</documentation>\r
                </annotation>\r
                <complexType>\r
-                       <sequence>\r
-                               <any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>\r
-                       </sequence>\r
-                       <attribute name="Location" type="anyURI" use="required"/>\r
-                       <attribute name="Binding" type="anyURI" use="required"/>\r
-                       <attribute name="id" type="conf:string"/>\r
-                       <attribute name="isDefault" type="boolean"/>\r
-                       <attribute name="wayfURL" type="anyURI"/>\r
-                       <attribute name="wayfBinding" type="anyURI"/>\r
-                       <attribute name="relayState" type="conf:string"/>\r
-                       <anyAttribute namespace="##any" processContents="lax"/>\r
+                       <complexContent>\r
+                               <restriction base="conf:PluggableType">\r
+                                       <sequence>\r
+                                               <any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>\r
+                                       </sequence>\r
+                                       <attribute name="Location" type="anyURI"/>\r
+                                       <attribute name="id" type="conf:string"/>\r
+                                       <attribute name="isDefault" type="boolean"/>\r
+                                       <attribute name="wayfURL" type="anyURI"/>\r
+                                       <attribute name="relayState" type="conf:string"/>\r
+                                       <attribute name="entityID" type="anyURI"/>\r
+                                       <anyAttribute namespace="##any" processContents="lax"/>\r
+                               </restriction>\r
+                       </complexContent>\r
                </complexType>\r
        </element>\r
-\r
+       \r
        <element name="Errors">\r
                <annotation>\r
                        <documentation>Container for error templates and associated details</documentation>\r
index 88f50e4..b3045c6 100644 (file)
@@ -96,9 +96,11 @@ libshibsp_la_SOURCES = \
        binding/impl/SOAPClient.cpp \
        handler/impl/AbstractHandler.cpp \
        handler/impl/AssertionConsumerService.cpp \
+       handler/impl/ChainingSessionInitiator.cpp \
        handler/impl/RemotedHandler.cpp \
        handler/impl/SAML1Consumer.cpp \
        handler/impl/SAML2Consumer.cpp \
+       handler/impl/Shib1SessionInitiator.cpp \
        impl/RemotedSessionCache.cpp \
        impl/StorageServiceSessionCache.cpp \
        impl/XMLAccessControl.cpp \
index 90ac16a..4424921 100644 (file)
@@ -325,7 +325,7 @@ void SimpleResolverImpl::resolve(
     vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
 \r
     auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);\r
-    const char* relyingParty = ctx.getApplication().getString("providerId").second;\r
+    const char* relyingParty = ctx.getApplication().getString("entityID").second;\r
 \r
 #ifdef HAVE_GOOD_STL\r
     map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
@@ -391,7 +391,7 @@ void SimpleResolverImpl::resolve(
     vector<shibsp::Attribute*>& resolved = ctx.getResolvedAttributes();\r
 \r
     auto_ptr_char assertingParty(ctx.getEntityDescriptor() ? ctx.getEntityDescriptor()->getEntityID() : NULL);\r
-    const char* relyingParty = ctx.getApplication().getString("providerId").second;\r
+    const char* relyingParty = ctx.getApplication().getString("entityID").second;\r
 \r
 #ifdef HAVE_GOOD_STL\r
     map< pair<xstring,xstring>,pair<const AttributeDecoder*,string> >::const_iterator rule;\r
@@ -452,7 +452,7 @@ void SimpleResolverImpl::resolve(
 \r
         const vector<saml2::EncryptedAttribute*>& encattrs = const_cast<const saml2::AttributeStatement*>(*s)->getEncryptedAttributes();\r
         if (!encattrs.empty()) {\r
-            const XMLCh* recipient = ctx.getApplication().getXMLString("providerId").second;\r
+            const XMLCh* recipient = ctx.getApplication().getXMLString("entityID").second;\r
             CredentialResolver* cr = ctx.getApplication().getCredentialResolver();\r
             if (!cr) {\r
                 Category::getInstance(SHIBSP_LOGCAT".AttributeResolver").warn(\r
@@ -533,7 +533,7 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameIdentifier& nam
             if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
                 continue;\r
             auto_ptr_char loc((*ep)->getLocation());\r
-            auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
+            auto_ptr_XMLCh issuer(ctx.getApplication().getString("entityID").second);\r
             saml1::Subject* subject = saml1::SubjectBuilder::buildSubject();\r
             subject->setNameIdentifier(nameid.cloneNameIdentifier());\r
             saml1p::AttributeQuery* query = saml1p::AttributeQueryBuilder::buildAttributeQuery();\r
@@ -645,7 +645,7 @@ void SimpleResolverImpl::query(ResolutionContext& ctx, const NameID& nameid, con
             if (!XMLString::equals((*ep)->getBinding(),binding.get()))\r
                 continue;\r
             auto_ptr_char loc((*ep)->getLocation());\r
-            auto_ptr_XMLCh issuer(ctx.getApplication().getString("providerId").second);\r
+            auto_ptr_XMLCh issuer(ctx.getApplication().getString("entityID").second);\r
             saml2::Subject* subject = saml2::SubjectBuilder::buildSubject();\r
             subject->setNameID(nameid.cloneNameID());\r
             saml2p::AttributeQuery* query = saml2p::AttributeQueryBuilder::buildAttributeQuery();\r
index 3f85c48..eb6bd20 100644 (file)
@@ -106,7 +106,7 @@ ArtifactResponse* ArtifactResolver::resolve(
             if (!XMLString::equals((*ep)->getBinding(),binding.get()))
                 continue;
             auto_ptr_char loc((*ep)->getLocation());
-            auto_ptr_XMLCh issuer(sppolicy.getApplication().getString("providerId").second);
+            auto_ptr_XMLCh issuer(sppolicy.getApplication().getString("entityID").second);
             ArtifactResolve* request = ArtifactResolveBuilder::buildArtifactResolve();
             Issuer* iss = IssuerBuilder::buildIssuer();
             request->setIssuer(iss);
index 46db17a..31a5cbf 100644 (file)
@@ -32,6 +32,8 @@
 
 namespace shibsp {
 
+    class SHIBSP_API SPRequest;
+
 #if defined (_MSC_VER)
     #pragma warning( push )
     #pragma warning( disable : 4250 )
@@ -68,6 +70,18 @@ namespace shibsp {
         virtual void checkError(const xmltooling::XMLObject* response) const;
         
         /**
+         * Implements various mechanisms to preserve RelayState,
+         * such as cookies or StorageService-backed keys.
+         * 
+         * <p>If a supported mechanism can be identified, the input parameter will be
+         * replaced with a suitable state key, URL-encoded.
+         * 
+         * @param request       the active SPRequest
+         * @param relayState    RelayState token to supply with message
+         */
+        virtual void preserveRelayState(SPRequest& request, std::string& relayState) const;
+
+        /**
          * Implements various mechanisms to recover RelayState,
          * such as cookies or StorageService-backed keys.
          * 
index 99d0750..8ff6475 100644 (file)
@@ -113,15 +113,15 @@ namespace shibsp {
         std::string processMessage(
             const Application& application,
             opensaml::HTTPRequest& httpRequest,
-            std::string& providerId,
+            std::string& entityID,
             std::string& relayState
             ) const;
             
         std::pair<bool,long> sendRedirect(
-            SPRequest& request, const char* key, const char* providerId, const char* relayState
+            SPRequest& request, const char* key, const char* entityID, const char* relayState
             ) const;
         
-        void maintainHistory(SPRequest& request, const char* providerId, const char* cookieProps) const;
+        void maintainHistory(SPRequest& request, const char* entityID, const char* cookieProps) const;
                 
         opensaml::MessageDecoder* m_decoder;
         xmltooling::auto_ptr_char m_configNS;
index 94ccefa..2103d34 100644 (file)
@@ -55,6 +55,12 @@ namespace shibsp {
     
     /** Registers Handler implementations. */
     void SHIBSP_API registerHandlers();
+
+    /** SessionInitiator that iterates through a set of protocol-specific versions. */
+    #define CHAINING_SESSION_INITIATOR "Chaining"
+
+    /** SessionInitiator that supports Shibboleth V1 AuthnRequest/WAYF redirects. */
+    #define SHIB1_SESSION_INITIATOR "Shibboleth"
 };
 
 #endif /* __shibsp_handler_h__ */
index 1bf4593..9048453 100644 (file)
 #include "internal.h"
 #include "Application.h"
 #include "exceptions.h"
+#include "ServiceProvider.h"
 #include "SPRequest.h"
 #include "handler/AbstractHandler.h"
+#include "remoting/ListenerService.h"
 
+#include <log4cpp/Category.hh>
 #include <saml/saml1/core/Protocols.h>
 #include <saml/saml2/core/Protocols.h>
 #include <saml/util/SAMLConstants.h>
 #include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/StorageService.h>
 #include <xmltooling/util/URLEncoder.h>
 
 using namespace shibsp;
 using namespace samlconstants;
 using namespace opensaml;
 using namespace xmltooling;
+using namespace log4cpp;
 using namespace xercesc;
 using namespace std;
 
 namespace shibsp {
     SHIBSP_DLLLOCAL PluginManager<Handler,pair<const DOMElement*,const char*>>::Factory SAML1ConsumerFactory;
     SHIBSP_DLLLOCAL PluginManager<Handler,pair<const DOMElement*,const char*>>::Factory SAML2ConsumerFactory;
+    SHIBSP_DLLLOCAL PluginManager<Handler,pair<const DOMElement*,const char*>>::Factory ChainingSessionInitiatorFactory;
+    SHIBSP_DLLLOCAL PluginManager<Handler,pair<const DOMElement*,const char*>>::Factory Shib1SessionInitiatorFactory;
 };
 
 void SHIBSP_API shibsp::registerHandlers()
 {
     SPConfig& conf=SPConfig::getConfig();
+    
     conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_ARTIFACT, SAML1ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML1_PROFILE_BROWSER_POST, SAML1ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_ARTIFACT, SAML2ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST, SAML2ConsumerFactory);
     conf.AssertionConsumerServiceManager.registerFactory(SAML20_BINDING_HTTP_POST_SIMPLESIGN, SAML2ConsumerFactory);
+
+    conf.SessionInitiatorManager.registerFactory(CHAINING_SESSION_INITIATOR, ChainingSessionInitiatorFactory);
+    conf.SessionInitiatorManager.registerFactory(SHIB1_SESSION_INITIATOR, Shib1SessionInitiatorFactory);
 }
 
 AbstractHandler::AbstractHandler(
@@ -108,15 +119,84 @@ void AbstractHandler::checkError(const XMLObject* response) const
     }
 }
 
+void AbstractHandler::preserveRelayState(SPRequest& request, string& relayState) const
+{
+    pair<bool,const char*> mech=getString("relayState");
+    const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
+
+    // No setting means just pass it by value.
+    if (!mech.first || !mech.second || !*mech.second) {
+        relayState = urlenc->encode(relayState.c_str());
+    }
+    else if (!strcmp(mech.second, "cookie")) {
+        // Here we store the state in a cookie and send a fixed
+        // value so we can recognize it on the way back.
+        pair<string,const char*> shib_cookie=request.getApplication().getCookieNameProps("_shibstate_");
+        string stateval = urlenc->encode(relayState.c_str()) + shib_cookie.second;
+        request.setCookie(shib_cookie.first.c_str(),stateval.c_str());
+        relayState = "cookie";
+    }
+    else if (strstr(mech.second,"ss:")==mech.second) {
+        mech.second+=3;
+        if (*mech.second) {
+            DDF out,in = DDF("set::RelayState").structure();
+            in.addmember("id").string(mech.second);
+            in.addmember("value").string(relayState.c_str());
+            DDFJanitor jin(in),jout(out);
+            out = request.getServiceProvider().getListenerService()->send(in);
+            if (!out.isstring())
+                throw IOException("StorageService-backed RelayState mechanism did not return a state key.");
+            relayState = string(mech.second-3) + ':' + urlenc->encode(out.string());
+        }
+    }
+    else
+        throw ConfigurationException("Unsupported relayState mechanism ($1).", params(1,mech.second));
+}
+
 void AbstractHandler::recoverRelayState(HTTPRequest& httpRequest, string& relayState) const
 {
     SPConfig& conf = SPConfig::getConfig();
-    if (conf.isEnabled(SPConfig::OutOfProcess)) {
-        // Out of process, we look for StorageService-backed state.
-        // TODO: something like ss:SSID:key?
+
+    // Look for StorageService-backed state of the form "ss:SSID:key".
+    const char* state = relayState.c_str();
+    if (strstr(state,"ss:")==state) {
+        state += 3;
+        const char* key = strchr(state,':');
+        if (key) {
+            string ssid = relayState.substr(3, key - state);
+            key++;
+            if (!ssid.empty() && *key) {
+                if (conf.isEnabled(SPConfig::OutOfProcess)) {
+                    StorageService* storage = conf.getServiceProvider()->getStorageService(ssid.c_str());
+                    if (storage) {
+                        if (storage->readString("RelayState",key,&relayState)>0)
+                            storage->deleteString("RelayState",key);
+                        else
+                            relayState = "default";
+                    }
+                    else {
+                        Category::getInstance(SHIBSP_LOGCAT".Handler").error(
+                            "Storage-backed RelayState with invalid StorageService ID (%s)", ssid.c_str()
+                            );
+                        relayState = "default";
+                    }
+                }
+                else if (conf.isEnabled(SPConfig::InProcess)) {
+                    // In process, we should be able to cast down to a full SPRequest.
+                    SPRequest& request = dynamic_cast<SPRequest&>(httpRequest);
+                    DDF out,in = DDF("get::RelayState").structure();
+                    in.addmember("id").string(ssid.c_str());
+                    in.addmember("key").string(key);
+                    DDFJanitor jin(in),jout(out);
+                    out = request.getServiceProvider().getListenerService()->send(in);
+                    if (!out.isstring())
+                        throw IOException("StorageService-backed RelayState mechanism did not return a state value.");
+                    relayState = out.string();
+                }
+            }
+        }
     }
-    
-    if (conf.isEnabled(SPConfig::InProcess)) {
+    else if (conf.isEnabled(SPConfig::InProcess)) {
         // In process, we should be able to cast down to a full SPRequest.
         SPRequest& request = dynamic_cast<SPRequest&>(httpRequest);
         if (relayState.empty() || relayState == "cookie") {
index e664fc9..c5e9080 100644 (file)
@@ -69,9 +69,9 @@ pair<bool,long> AssertionConsumerService::run(SPRequest& request, bool isHandler
         if (conf.isEnabled(SPConfig::OutOfProcess)) {
             // When out of process, we run natively and directly process the message.
             // RelayState will be fully handled during message processing.
-            string providerId;
-            string key = processMessage(request.getApplication(), request, providerId, relayState);
-            return sendRedirect(request, key.c_str(), providerId.c_str(), relayState.c_str());
+            string entityID;
+            string key = processMessage(request.getApplication(), request, entityID, relayState);
+            return sendRedirect(request, key.c_str(), entityID.c_str(), relayState.c_str());
         }
         else {
             // When not out of process, we remote all the message processing.
@@ -105,7 +105,7 @@ pair<bool,long> AssertionConsumerService::run(SPRequest& request, bool isHandler
                 throw FatalProfileException("Remote processing of SSO profile did not return a usable session key.");
             
             // Take care of cookie business and wrap it up.
-            return sendRedirect(request, out["key"].string(), out["provider_id"].string(), relayState.c_str());
+            return sendRedirect(request, out["key"].string(), out["entity_id"].string(), relayState.c_str());
         }
     }
     catch (XMLToolingException& ex) {
@@ -131,16 +131,16 @@ void AssertionConsumerService::receive(DDF& in, ostream& out)
     auto_ptr<HTTPRequest> http(getRequest(in));
     
     // Do the work.
-    string relayState, providerId;
+    string relayState, entityID;
     try {
-        string key = processMessage(*app, *http.get(), providerId, relayState);
+        string key = processMessage(*app, *http.get(), entityID, relayState);
 
         // Repack for return to caller.
         DDF ret=DDF(NULL).structure();
         DDFJanitor jret(ret);
         ret.addmember("key").string(key.c_str());
-        if (!providerId.empty())
-            ret.addmember("provider_id").string(providerId.c_str());
+        if (!entityID.empty())
+            ret.addmember("entity_id").string(entityID.c_str());
         if (!relayState.empty())
             ret.addmember("RelayState").string(relayState.c_str());
         out << ret;
@@ -154,7 +154,7 @@ void AssertionConsumerService::receive(DDF& in, ostream& out)
 }
 
 string AssertionConsumerService::processMessage(
-    const Application& application, HTTPRequest& httpRequest, string& providerId, string& relayState
+    const Application& application, HTTPRequest& httpRequest, string& entityID, string& relayState
     ) const
 {
     // Locate policy key.
@@ -187,13 +187,13 @@ string AssertionConsumerService::processMessage(
 
     auto_ptr_char issuer(policy.getIssuer() ? policy.getIssuer()->getName() : NULL);
     if (issuer.get())
-        providerId = issuer.get();
+        entityID = issuer.get();
     
     return key;
 }
 
 pair<bool,long> AssertionConsumerService::sendRedirect(
-    SPRequest& request, const char* key, const char* providerId, const char* relayState
+    SPRequest& request, const char* key, const char* entityID, const char* relayState
     ) const
 {
     // We've got a good session, so set the session cookie.
@@ -203,7 +203,7 @@ pair<bool,long> AssertionConsumerService::sendRedirect(
     request.setCookie(shib_cookie.first.c_str(), k.c_str());
 
     // History cookie.
-    maintainHistory(request, providerId, shib_cookie.second);
+    maintainHistory(request, entityID, shib_cookie.second);
 
     // Now redirect to the state value. By now, it should be set to *something* usable.
     return make_pair(true, request.sendRedirect(relayState));
@@ -260,9 +260,9 @@ ResolutionContext* AssertionConsumerService::resolveAttributes(
     return NULL;
 }
 
-void AssertionConsumerService::maintainHistory(SPRequest& request, const char* providerId, const char* cookieProps) const
+void AssertionConsumerService::maintainHistory(SPRequest& request, const char* entityID, const char* cookieProps) const
 {
-    if (!providerId)
+    if (!entityID)
         return;
         
     const PropertySet* sessionProps=request.getApplication().getPropertySet("Sessions");
@@ -274,7 +274,7 @@ void AssertionConsumerService::maintainHistory(SPRequest& request, const char* p
         // Either leave in memory or set an expiration.
         pair<bool,unsigned int> days=sessionProps->getUnsignedInt("idpHistoryDays");
         if (!days.first || days.second==0) {
-            string c = string(cdc.set(providerId)) + cookieProps;
+            string c = string(cdc.set(entityID)) + cookieProps;
             request.setCookie(CommonDomainCookie::CDCName, c.c_str());
         }
         else {
@@ -287,7 +287,7 @@ void AssertionConsumerService::maintainHistory(SPRequest& request, const char* p
 #endif
             char timebuf[64];
             strftime(timebuf,64,"%a, %d %b %Y %H:%M:%S GMT",ptime);
-            string c = string(cdc.set(providerId)) + cookieProps + "; expires=" + timebuf;
+            string c = string(cdc.set(entityID)) + cookieProps + "; expires=" + timebuf;
             request.setCookie(CommonDomainCookie::CDCName, c.c_str());
         }
     }
diff --git a/shibsp/handler/impl/ChainingSessionInitiator.cpp b/shibsp/handler/impl/ChainingSessionInitiator.cpp
new file mode 100644 (file)
index 0000000..df52c08
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ *  Copyright 2001-2007 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * ChainingSessionInitiator.cpp
+ * 
+ * Chains together multiple SessionInitiator handlers in sequence.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "handler/AbstractHandler.h"
+#include "util/SPConstants.h"
+
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xmltooling/util/XMLHelper.h>
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
+    class SHIBSP_DLLLOCAL ChainingSessionInitiator : public AbstractHandler
+    {
+    public:
+        ChainingSessionInitiator(const DOMElement* e, const char* appId);
+        virtual ~ChainingSessionInitiator() {
+            for_each(m_handlers.begin(), m_handlers.end(), xmltooling::cleanup<Handler>());
+        }
+        
+        pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+
+    private:
+        vector<Handler*> m_handlers;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    Handler* SHIBSP_DLLLOCAL ChainingSessionInitiatorFactory(const pair<const DOMElement*,const char*>& p)
+    {
+        return new ChainingSessionInitiator(p.first, p.second);
+    }
+
+    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 _type[] =            UNICODE_LITERAL_4(t,y,p,e);
+
+    class SHIBSP_DLLLOCAL SessionInitiatorNodeFilter : public DOMNodeFilter
+    {
+    public:
+        short acceptNode(const DOMNode* node) const {
+            if (XMLHelper::isNodeNamed(node,shibspconstants::SHIB2SPCONFIG_NS,SessionInitiator))
+                return FILTER_REJECT;
+            return FILTER_ACCEPT;
+        }
+    };
+
+    static SHIBSP_DLLLOCAL SessionInitiatorNodeFilter g_SINFilter;
+};
+
+ChainingSessionInitiator::ChainingSessionInitiator(const DOMElement* e, const char* appId)
+    : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator"), &g_SINFilter)
+{
+    SPConfig& conf = SPConfig::getConfig();
+
+    // Load up the chain of handlers.
+    e = e ? XMLHelper::getFirstChildElement(e, SessionInitiator) : NULL;
+    while (e) {
+        auto_ptr_char type(e->getAttributeNS(NULL,_type));
+        if (type.get() && *(type.get())) {
+            m_handlers.push_back(conf.SessionInitiatorManager.newPlugin(type.get(),make_pair(e, appId)));
+            m_handlers.back()->setParent(this);
+        }
+        e = XMLHelper::getNextSiblingElement(e, SessionInitiator);
+    }
+}
+
+pair<bool,long> ChainingSessionInitiator::run(SPRequest& request, bool isHandler) const
+{
+    pair<bool,long> ret;
+    for (vector<Handler*>::const_iterator i = m_handlers.begin(); i!=m_handlers.end(); ++i) {
+        ret = (*i)->run(request, isHandler);
+        if (ret.first)
+            return ret;
+    }
+    throw ConfigurationException("None of the configured SessionInitiators handled the request.");
+}
index 0d79176..6d5cda4 100644 (file)
@@ -201,7 +201,7 @@ string SAML2Consumer::implementProtocol(
         saml2::Assertion* decrypted=NULL;
         try {
             Locker credlocker(cr);
-            auto_ptr<XMLObject> wrapper((*ea)->decrypt(*cr, application.getXMLString("providerId").second, &cc));
+            auto_ptr<XMLObject> wrapper((*ea)->decrypt(*cr, application.getXMLString("entityID").second, &cc));
             decrypted = dynamic_cast<saml2::Assertion*>(wrapper.get());
             if (decrypted) {
                 wrapper.release();
@@ -291,7 +291,7 @@ string SAML2Consumer::implementProtocol(
             else {
                 Locker credlocker(cr);
                 try {
-                    auto_ptr<XMLObject> decryptedID(encname->decrypt(*cr,application.getXMLString("providerId").second,&cc));
+                    auto_ptr<XMLObject> decryptedID(encname->decrypt(*cr,application.getXMLString("entityID").second,&cc));
                     ssoName = dynamic_cast<NameID*>(decryptedID.get());
                     if (ssoName) {
                         ownedName = true;
diff --git a/shibsp/handler/impl/Shib1SessionInitiator.cpp b/shibsp/handler/impl/Shib1SessionInitiator.cpp
new file mode 100644 (file)
index 0000000..aa89402
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ *  Copyright 2001-2007 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Shib1SessionInitiator.cpp
+ * 
+ * Shibboleth 1.x AuthnRequest/WAYF support.
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "exceptions.h"
+#include "SPRequest.h"
+#include "handler/AbstractHandler.h"
+#include "util/SPConstants.h"
+
+#include <saml/saml2/metadata/Metadata.h>
+#include <saml/saml2/metadata/EndpointManager.h>
+#include <xercesc/util/XMLUniDefs.hpp>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/XMLHelper.h>
+#include <xmltooling/util/URLEncoder.h>
+
+using namespace shibsp;
+using namespace opensaml::saml2md;
+using namespace opensaml;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
+    class SHIBSP_DLLLOCAL Shib1SessionInitiator : public AbstractHandler
+    {
+    public:
+        Shib1SessionInitiator(const DOMElement* e, const char* appId)
+            : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".SessionInitiator")) {}
+        virtual ~Shib1SessionInitiator() {}
+        
+        pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+
+    private:
+        pair<bool,long> doAuthnRequest(SPRequest& request, const Handler* shire, const char* dest, string& target) const;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    Handler* SHIBSP_DLLLOCAL Shib1SessionInitiatorFactory(const pair<const DOMElement*,const char*>& p)
+    {
+        return new Shib1SessionInitiator(p.first, p.second);
+    }
+
+};
+
+pair<bool,long> Shib1SessionInitiator::run(SPRequest& request, bool isHandler) const
+{
+    string target;
+    const char* entityID=NULL;
+    const Handler* ACS=NULL;
+    const Application& app=request.getApplication();
+    
+    if (isHandler) {
+        entityID=request.getParameter("acsIndex");
+        if (entityID)
+            ACS=app.getAssertionConsumerServiceByIndex(atoi(entityID));
+
+        entityID = request.getParameter("target");
+        if (entityID)
+            target = entityID;
+        recoverRelayState(request, target);
+
+        // Try and establish which IdP to use.
+        entityID=request.getParameter("entityID");
+        if (!entityID || !*entityID)
+            entityID=request.getParameter("providerId");
+        if (!entityID || !*entityID)
+            entityID=getString("entityID").second;
+    }
+    else {
+        // We're running as a "virtual handler" from within the filter.
+        // The target resource is the current one and everything else is defaulted.
+        entityID=getString("entityID").second;
+        target=request.getRequestURL();
+    }
+        
+    if (entityID && *entityID) {
+        m_log.debug("attempting to initiate session using SAML 1.x with provider (%s)", entityID);
+
+        // Use metadata to invoke the SSO service directly.
+        MetadataProvider* m=app.getMetadataProvider();
+        Locker locker(m);
+        const EntityDescriptor* entity=m->getEntityDescriptor(entityID);
+        if (!entity) {
+            m_log.error("unable to locate metadata for provider (%s)", entityID);
+            return make_pair(false,0);
+        }
+        const IDPSSODescriptor* role=entity->getIDPSSODescriptor(shibspconstants::SHIB1_PROTOCOL_ENUM);
+        if (!role) {
+            m_log.error("unable to locate Shibboleth-aware identity provider role for provider (%s)", entityID);
+            return make_pair(false,0);
+        }
+        const EndpointType* ep=EndpointManager<SingleSignOnService>(role->getSingleSignOnServices()).getByBinding(
+            shibspconstants::SHIB1_AUTHNREQUEST_PROFILE_URI
+            );
+        if (!ep) {
+            m_log.error("unable to locate compatible SSO service for provider (%s)", entityID);
+            return make_pair(false,0);
+        }
+        auto_ptr_char dest(ep->getLocation());
+        return doAuthnRequest(request, ACS ? ACS : app.getDefaultAssertionConsumerService(), dest.get(), target);
+    }
+    
+    // Fall back to optional legacy discovery service.
+    pair<bool,const char*> wayfURL=getString("wayfURL");
+    if (!wayfURL.first)
+        return make_pair(false,0);
+    return doAuthnRequest(request, ACS ? ACS : app.getDefaultAssertionConsumerService(), wayfURL.second, target);
+}
+
+pair<bool,long> Shib1SessionInitiator::doAuthnRequest(SPRequest& request, const Handler* shire, const char* dest, string& target) const
+{
+    // 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=shire ? shire->getString("Location") : pair<bool,const char*>(false,NULL);
+    if (loc.first) ACSloc+=loc.second;
+
+    preserveRelayState(request, target);
+
+    char timebuf[16];
+    sprintf(timebuf,"%u",time(NULL));
+    const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder();
+    string req=string(dest) + "?shire=" + urlenc->encode(ACSloc.c_str()) + "&time=" + timebuf + "&target=" + target +
+        "&providerId=" + urlenc->encode(request.getApplication().getString("entityID").second);
+
+    return make_pair(true, request.sendRedirect(req.c_str()));
+}
index e5c96a6..725970a 100644 (file)
@@ -75,14 +75,6 @@ namespace {
         XMLApplication(const ServiceProvider*, const DOMElement* e, const XMLApplication* base=NULL);\r
         ~XMLApplication() { cleanup(); }\r
     \r
-        // PropertySet\r
-        pair<bool,bool> getBool(const char* name, const char* ns=NULL) const;\r
-        pair<bool,const char*> getString(const char* name, const char* ns=NULL) const;\r
-        pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const;\r
-        pair<bool,unsigned int> getUnsignedInt(const char* name, const char* ns=NULL) const;\r
-        pair<bool,int> getInt(const char* name, const char* ns=NULL) const;\r
-        const PropertySet* getPropertySet(const char* name, const char* ns="urn:mace:shibboleth:sp:config:2.0") const;\r
-\r
         // Application\r
         const ServiceProvider& getServiceProvider() const {return *m_sp;}\r
         const char* getId() const {return getString("id").second;}\r
@@ -214,6 +206,7 @@ namespace {
         }\r
 \r
         // PropertySet\r
+        void setParent(const PropertySet* parent) {return m_impl->setParent(parent);}\r
         pair<bool,bool> getBool(const char* name, const char* ns=NULL) const {return m_impl->getBool(name,ns);}\r
         pair<bool,const char*> getString(const char* name, const char* ns=NULL) const {return m_impl->getString(name,ns);}\r
         pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const {return m_impl->getXMLString(name,ns);}\r
@@ -356,13 +349,15 @@ XMLApplication::XMLApplication(
     try {\r
         // First load any property sets.\r
         load(e,log,this);\r
+        if (base)\r
+            setParent(base);\r
 \r
         SPConfig& conf=SPConfig::getConfig();\r
         SAMLConfig& samlConf=SAMLConfig::getConfig();\r
         XMLToolingConfig& xmlConf=XMLToolingConfig::getConfig();\r
 \r
         m_hash=getId();\r
-        m_hash+=getString("providerId").second;\r
+        m_hash+=getString("entityID").second;\r
         m_hash=samlConf.hashSHA1(m_hash.c_str(), true);\r
 \r
         pair<bool,const char*> attributes = getString("attributeIds");\r
@@ -423,13 +418,13 @@ XMLApplication::XMLApplication(
                     }\r
                 }\r
                 else if (XMLString::equals(child->getLocalName(),SessionInitiator)) {\r
-                    auto_ptr_char bindprop(child->getAttributeNS(NULL,EndpointType::BINDING_ATTRIB_NAME));\r
-                    if (!bindprop.get() || !*(bindprop.get())) {\r
-                        log.warn("SessionInitiator element has no Binding attribute, skipping it...");\r
+                    auto_ptr_char type(child->getAttributeNS(NULL,_type));\r
+                    if (!type.get() || !*(type.get())) {\r
+                        log.warn("SessionInitiator element has no type attribute, skipping it...");\r
                         child = XMLHelper::getNextSiblingElement(child);\r
                         continue;\r
                     }\r
-                    handler=conf.SessionInitiatorManager.newPlugin(bindprop.get(),make_pair(child, getId()));\r
+                    handler=conf.SessionInitiatorManager.newPlugin(type.get(),make_pair(child, getId()));\r
                     pair<bool,const char*> si_id=handler->getString("id");\r
                     if (si_id.first && si_id.second)\r
                         m_sessionInitMap[si_id.second]=handler;\r
@@ -496,8 +491,8 @@ XMLApplication::XMLApplication(
             if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes())\r
                 m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue());\r
 \r
-        // Always include our own providerId as an audience.\r
-        m_audiences.push_back(getXMLString("providerId").second);\r
+        // Always include our own entityID as an audience.\r
+        m_audiences.push_back(getXMLString("entityID").second);\r
 \r
         if (conf.isEnabled(SPConfig::Metadata)) {\r
             child = XMLHelper::getFirstChildElement(e,_MetadataProvider);\r
@@ -627,54 +622,6 @@ short XMLApplication::acceptNode(const DOMNode* node) const
     return FILTER_ACCEPT;\r
 }\r
 \r
-pair<bool,bool> XMLApplication::getBool(const char* name, const char* ns) const\r
-{\r
-    pair<bool,bool> ret=DOMPropertySet::getBool(name,ns);\r
-    if (ret.first)\r
-        return ret;\r
-    return m_base ? m_base->getBool(name,ns) : ret;\r
-}\r
-\r
-pair<bool,const char*> XMLApplication::getString(const char* name, const char* ns) const\r
-{\r
-    pair<bool,const char*> ret=DOMPropertySet::getString(name,ns);\r
-    if (ret.first)\r
-        return ret;\r
-    return m_base ? m_base->getString(name,ns) : ret;\r
-}\r
-\r
-pair<bool,const XMLCh*> XMLApplication::getXMLString(const char* name, const char* ns) const\r
-{\r
-    pair<bool,const XMLCh*> ret=DOMPropertySet::getXMLString(name,ns);\r
-    if (ret.first)\r
-        return ret;\r
-    return m_base ? m_base->getXMLString(name,ns) : ret;\r
-}\r
-\r
-pair<bool,unsigned int> XMLApplication::getUnsignedInt(const char* name, const char* ns) const\r
-{\r
-    pair<bool,unsigned int> ret=DOMPropertySet::getUnsignedInt(name,ns);\r
-    if (ret.first)\r
-        return ret;\r
-    return m_base ? m_base->getUnsignedInt(name,ns) : ret;\r
-}\r
-\r
-pair<bool,int> XMLApplication::getInt(const char* name, const char* ns) const\r
-{\r
-    pair<bool,int> ret=DOMPropertySet::getInt(name,ns);\r
-    if (ret.first)\r
-        return ret;\r
-    return m_base ? m_base->getInt(name,ns) : ret;\r
-}\r
-\r
-const PropertySet* XMLApplication::getPropertySet(const char* name, const char* ns) const\r
-{\r
-    const PropertySet* ret=DOMPropertySet::getPropertySet(name,ns);\r
-    if (ret || !m_base)\r
-        return ret;\r
-    return m_base->getPropertySet(name,ns);\r
-}\r
-\r
 const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provider) const\r
 {\r
     if (!m_partyDefault && m_base)\r
index eceb68d..81b1f08 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\ChainingSessionInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\RemotedHandler.cpp"\r
                                                >\r
                                        </File>\r
                                                RelativePath=".\handler\impl\SAML2Consumer.cpp"\r
                                                >\r
                                        </File>\r
+                                       <File\r
+                                               RelativePath=".\handler\impl\Shib1SessionInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
                                </Filter>\r
                        </Filter>\r
                </Filter>\r
index 58632cb..3f45ad6 100644 (file)
@@ -127,7 +127,6 @@ void DOMPropertySet::load(
 
 pair<bool,bool> DOMPropertySet::getBool(const char* name, const char* ns) const
 {
-    pair<bool,bool> ret(false,false);
     map<string,pair<char*,const XMLCh*> >::const_iterator i;
 
     if (ns)
@@ -135,11 +134,11 @@ pair<bool,bool> DOMPropertySet::getBool(const char* name, const char* ns) const
     else
         i=m_map.find(name);
 
-    if (i!=m_map.end()) {
-        ret.first=true;
-        ret.second=(!strcmp(i->second.first,"true") || !strcmp(i->second.first,"1"));
-    }
-    return ret;
+    if (i!=m_map.end())
+        return make_pair(true,(!strcmp(i->second.first,"true") || !strcmp(i->second.first,"1")));
+    else if (m_parent)
+        return m_parent->getBool(name,ns);
+    return make_pair(false,false);
 }
 
 pair<bool,const char*> DOMPropertySet::getString(const char* name, const char* ns) const
@@ -152,16 +151,15 @@ pair<bool,const char*> DOMPropertySet::getString(const char* name, const char* n
     else
         i=m_map.find(name);
 
-    if (i!=m_map.end()) {
-        ret.first=true;
-        ret.second=i->second.first;
-    }
-    return ret;
+    if (i!=m_map.end())
+        return make_pair(true,i->second.first);
+    else if (m_parent)
+        return m_parent->getString(name,ns);
+    return pair<bool,const char*>(false,NULL);
 }
 
 pair<bool,const XMLCh*> DOMPropertySet::getXMLString(const char* name, const char* ns) const
 {
-    pair<bool,const XMLCh*> ret(false,NULL);
     map<string,pair<char*,const XMLCh*> >::const_iterator i;
 
     if (ns)
@@ -169,16 +167,15 @@ pair<bool,const XMLCh*> DOMPropertySet::getXMLString(const char* name, const cha
     else
         i=m_map.find(name);
 
-    if (i!=m_map.end()) {
-        ret.first=true;
-        ret.second=i->second.second;
-    }
-    return ret;
+    if (i!=m_map.end())
+        return make_pair(true,i->second.second);
+    else if (m_parent)
+        return m_parent->getXMLString(name,ns);
+    return pair<bool,const XMLCh*>(false,NULL);
 }
 
 pair<bool,unsigned int> DOMPropertySet::getUnsignedInt(const char* name, const char* ns) const
 {
-    pair<bool,unsigned int> ret(false,0);
     map<string,pair<char*,const XMLCh*> >::const_iterator i;
 
     if (ns)
@@ -186,16 +183,15 @@ pair<bool,unsigned int> DOMPropertySet::getUnsignedInt(const char* name, const c
     else
         i=m_map.find(name);
 
-    if (i!=m_map.end()) {
-        ret.first=true;
-        ret.second=strtol(i->second.first,NULL,10);
-    }
-    return ret;
+    if (i!=m_map.end())
+        return pair<bool,unsigned int>(true,strtol(i->second.first,NULL,10));
+    else if (m_parent)
+        return m_parent->getUnsignedInt(name,ns);
+    return pair<bool,unsigned int>(false,0);
 }
 
 pair<bool,int> DOMPropertySet::getInt(const char* name, const char* ns) const
 {
-    pair<bool,int> ret(false,0);
     map<string,pair<char*,const XMLCh*> >::const_iterator i;
 
     if (ns)
@@ -203,11 +199,11 @@ pair<bool,int> DOMPropertySet::getInt(const char* name, const char* ns) const
     else
         i=m_map.find(name);
 
-    if (i!=m_map.end()) {
-        ret.first=true;
-        ret.second=atoi(i->second.first);
-    }
-    return ret;
+    if (i!=m_map.end())
+        return pair<bool,int>(true,atoi(i->second.first));
+    else if (m_parent)
+        return m_parent->getInt(name,ns);
+    return pair<bool,int>(false,0);
 }
 
 const PropertySet* DOMPropertySet::getPropertySet(const char* name, const char* ns) const
@@ -219,5 +215,5 @@ const PropertySet* DOMPropertySet::getPropertySet(const char* name, const char*
     else
         i=m_nested.find(name);
 
-    return (i!=m_nested.end()) ? i->second : NULL;
+    return (i!=m_nested.end()) ? i->second : (m_parent ? m_parent->getPropertySet(name,ns) : NULL);
 }
index b29b307..196a287 100644 (file)
@@ -34,10 +34,14 @@ namespace shibsp {
     class SHIBSP_API DOMPropertySet : public virtual PropertySet
     {
     public:
-        DOMPropertySet() : m_root(NULL) {}
+        DOMPropertySet() : m_parent(NULL), m_root(NULL) {}
         
         virtual ~DOMPropertySet();
 
+        void setParent(const PropertySet* parent) {
+            m_parent = parent;
+        }
+
         std::pair<bool,bool> getBool(const char* name, const char* ns=NULL) const;
         std::pair<bool,const char*> getString(const char* name, const char* ns=NULL) const;
         std::pair<bool,const XMLCh*> getXMLString(const char* name, const char* ns=NULL) const;
@@ -65,6 +69,7 @@ namespace shibsp {
             );
 
     private:
+        const PropertySet* m_parent;
         const xercesc::DOMElement* m_root;
         std::map<std::string,std::pair<char*,const XMLCh*> > m_map;
         std::map<std::string,DOMPropertySet*> m_nested;
index cc20b60..019a05d 100644 (file)
@@ -40,6 +40,13 @@ namespace shibsp {
         virtual ~PropertySet() {}
 
         /**
+         * Establishes a "parent" PropertySet to supply inherited settings.
+         *
+         * @param parent    the parent PropertySet to use
+         */
+        virtual void setParent(const PropertySet* parent)=0;
+
+        /**
          * Returns a boolean-valued property.
          * 
          * @param name  property name
index cbc1cdf..08986a9 100644 (file)
@@ -139,7 +139,7 @@ int main(int argc,char* argv[])
         auto_ptr_XMLCh domain(q_param);\r
         auto_ptr_XMLCh name(n_param);\r
         auto_ptr_XMLCh format(f_param);\r
-        auto_ptr_XMLCh issuer(app->getString("providerId").second);\r
+        auto_ptr_XMLCh issuer(app->getString("entityID").second);\r
 \r
         MetadataProvider* m=app->getMetadataProvider();\r
         xmltooling::Locker mlocker(m);\r