Add logout notifications to config.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Sun, 1 Jul 2007 22:26:36 +0000 (22:26 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Sun, 1 Jul 2007 22:26:36 +0000 (22:26 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2334 cb58f699-b61c-0410-a6fe-9272a202ed29

schemas/shibboleth-2.0-native-sp-config.xsd
shibsp/Application.h
shibsp/impl/XMLServiceProvider.cpp

index 8073aff..cde7881 100644 (file)
                                <element ref="conf:Sessions" minOccurs="0"/>\r
                                <element ref="conf:Errors" minOccurs="0"/>\r
                                <element ref="conf:DefaultRelyingParty" minOccurs="0"/>\r
+                               <element ref="conf:NotifyOnLogout" minOccurs="0" maxOccurs="unbounded"/>\r
                                <element ref="saml:Audience" minOccurs="0" maxOccurs="unbounded"/>\r
                                <element name="MetadataProvider" type="conf:PluggableType" minOccurs="0"/>\r
                                <element name="TrustEngine" type="conf:PluggableType" minOccurs="0"/>\r
                </complexType>\r
        </element>\r
 \r
+       <element name="NotifyOnLogout">\r
+               <annotation>\r
+                       <documentation>Used to specify locations to receive application notifications of logout</documentation>\r
+               </annotation>\r
+               <complexType>\r
+                       <sequence/>\r
+                       <attribute name="Channel" use="required">\r
+                               <simpleType>\r
+                                       <restriction base="string">\r
+                                               <enumeration value="front"/>\r
+                                               <enumeration value="back"/>\r
+                                       </restriction>\r
+                               </simpleType>\r
+                       </attribute>\r
+                       <attribute name="Location" type="anyURI" use="required"/>\r
+                       <anyAttribute namespace="##any" processContents="lax"/>\r
+               </complexType>\r
+       </element>\r
+       \r
        <element name="DefaultRelyingParty">\r
                <annotation>\r
                        <documentation>Container for specifying security methods to use with particular peers</documentation>\r
index 352fca6..d32f76a 100644 (file)
@@ -32,6 +32,7 @@
 # include <xmltooling/security/CredentialResolver.h>
 # include <xmltooling/security/TrustEngine.h>
 #endif
+#include <xmltooling/io/HTTPRequest.h>
 
 namespace shibsp {
     
@@ -153,6 +154,16 @@ namespace shibsp {
 #endif
 
         /**
+         * Returns the designated logout notification URL, or an empty string if no more locations are specified.
+         *
+         * @param request   SP request to use to fill in missing pieces of URL
+         * @param front     true iff front channel notification is desired, false iff back channel is desired
+         * @param index     zero-based index of URL to return
+         * @return  the designated URL, or an empty string
+         */
+        virtual std::string getLogoutNotification(const xmltooling::HTTPRequest& request, bool front, unsigned int index) const=0;
+
+        /**
          * Returns a set of attribute IDs to use as a REMOTE_USER value.
          * <p>The first attribute with a value (and only a single value) will be used.
          *
index 9cd852c..2c1e147 100644 (file)
@@ -129,6 +129,8 @@ namespace {
             return (m_audiences.empty() && m_base) ? m_base->getAudiences() : m_audiences;
         }
 #endif
+        string getLogoutNotification(const HTTPRequest& request, bool front, unsigned int index) const;
+
         const set<string>& getRemoteUserAttributeIds() const {
             return (m_remoteUsers.empty() && m_base) ? m_base->getRemoteUserAttributeIds() : m_remoteUsers;
         }
@@ -178,6 +180,7 @@ namespace {
         map<const XMLCh*,PropertySet*> m_partyMap;
 #endif
 #endif
+        vector<string> m_frontLogout,m_backLogout;
         set<string> m_remoteUsers;
         mutable vector< pair<string,string> > m_unsetHeaders;
         RWLock* m_unsetLock;
@@ -371,6 +374,7 @@ namespace {
     static const XMLCh _ArtifactResolutionService[] =UNICODE_LITERAL_25(A,r,t,i,f,a,c,t,R,e,s,o,l,u,t,i,o,n,S,e,r,v,i,c,e);
     static const XMLCh _Audience[] =            UNICODE_LITERAL_8(A,u,d,i,e,n,c,e);
     static const XMLCh Binding[] =              UNICODE_LITERAL_7(B,i,n,d,i,n,g);
+    static const XMLCh Channel[]=               UNICODE_LITERAL_7(C,h,a,n,n,e,l);
     static const XMLCh _CredentialResolver[] =  UNICODE_LITERAL_18(C,r,e,d,e,n,t,i,a,l,R,e,s,o,l,v,e,r);
     static const XMLCh DefaultRelyingParty[] =  UNICODE_LITERAL_19(D,e,f,a,u,l,t,R,e,l,y,i,n,g,P,a,r,t,y);
     static const XMLCh _Extensions[] =          UNICODE_LITERAL_10(E,x,t,e,n,s,i,o,n,s);
@@ -381,10 +385,12 @@ namespace {
     static const XMLCh InProcess[] =            UNICODE_LITERAL_9(I,n,P,r,o,c,e,s,s);
     static const XMLCh Library[] =              UNICODE_LITERAL_7(L,i,b,r,a,r,y);
     static const XMLCh Listener[] =             UNICODE_LITERAL_8(L,i,s,t,e,n,e,r);
+    static const XMLCh Location[] =             UNICODE_LITERAL_8(L,o,c,a,t,i,o,n);
     static const XMLCh logger[] =               UNICODE_LITERAL_6(l,o,g,g,e,r);
     static const XMLCh _ManageNameIDService[] = UNICODE_LITERAL_19(M,a,n,a,g,e,N,a,m,e,I,D,S,e,r,v,i,c,e);
     static const XMLCh MemoryListener[] =       UNICODE_LITERAL_14(M,e,m,o,r,y,L,i,s,t,e,n,e,r);
     static const XMLCh _MetadataProvider[] =    UNICODE_LITERAL_16(M,e,t,a,d,a,t,a,P,r,o,v,i,d,e,r);
+    static const XMLCh NotifyOnLogout[] =       UNICODE_LITERAL_14(N,o,t,i,f,y,O,n,L,o,g,o,u,t);
     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 Policy[] =               UNICODE_LITERAL_6(P,o,l,i,c,y);
@@ -402,6 +408,7 @@ namespace {
     static const XMLCh _type[] =                UNICODE_LITERAL_4(t,y,p,e);
     static const XMLCh UnixListener[] =         UNICODE_LITERAL_12(U,n,i,x,L,i,s,t,e,n,e,r);
 
+
     class SHIBSP_DLLLOCAL PolicyNodeFilter : public DOMNodeFilter
     {
     public:
@@ -653,8 +660,23 @@ XMLApplication::XMLApplication(
             child = XMLHelper::getNextSiblingElement(child);
         }
 
+        // Logout notification.
+        DOMNodeList* nlist=e->getElementsByTagNameNS(shibspconstants::SHIB2SPCONFIG_NS,NotifyOnLogout);
+        for (XMLSize_t i=0; nlist && i<nlist->getLength(); i++) {
+            if (nlist->item(i)->getParentNode()->isSameNode(e)) {
+                const XMLCh* channel = static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,Channel);
+                auto_ptr_char loc(static_cast<DOMElement*>(nlist->item(i))->getAttributeNS(NULL,Location));
+                if (loc.get() && *loc.get()) {
+                    if (channel && *channel == chLatin_f)
+                        m_frontLogout.push_back(loc.get());
+                    else
+                        m_backLogout.push_back(loc.get());
+                }
+            }
+        }
+
 #ifndef SHIBSP_LITE
-        DOMNodeList* nlist=e->getElementsByTagNameNS(samlconstants::SAML20_NS,Audience::LOCAL_NAME);
+        nlist=e->getElementsByTagNameNS(samlconstants::SAML20_NS,Audience::LOCAL_NAME);
         for (XMLSize_t i=0; nlist && i<nlist->getLength(); i++)
             if (nlist->item(i)->getParentNode()->isSameNode(e) && nlist->item(i)->hasChildNodes())
                 m_audiences.push_back(nlist->item(i)->getFirstChild()->getNodeValue());
@@ -856,6 +878,7 @@ short XMLApplication::acceptNode(const DOMNode* node) const
     const XMLCh* name=node->getLocalName();
     if (XMLString::equals(name,_Application) ||
         XMLString::equals(name,_Audience) ||
+        XMLString::equals(name,NotifyOnLogout) ||
         XMLString::equals(name,_AssertionConsumerService) ||
         XMLString::equals(name,_ArtifactResolutionService) ||
         XMLString::equals(name,_SingleLogoutService) ||
@@ -914,6 +937,79 @@ const PropertySet* XMLApplication::getRelyingParty(const EntityDescriptor* provi
 
 #endif
 
+string XMLApplication::getLogoutNotification(const HTTPRequest& request, bool front, unsigned int index) const
+{
+    const vector<string>& locs = front ? m_frontLogout : m_backLogout;
+    if (locs.empty())
+        return m_base ? m_base->getLogoutNotification(request, front, index) : string();
+    else if (index >= locs.size())
+        return string();
+
+    const char* resource = request.getRequestURL();
+#ifdef HAVE_STRCASECMP
+    if (!resource || (strncasecmp(resource,"http://",7) && strncasecmp(resource,"https://",8)))
+#else
+    if (!resource || (strnicmp(resource,"http://",7) && strnicmp(resource,"https://",8)))
+#endif
+        throw ConfigurationException("Request URL was not absolute.");
+
+    const char* handler=locs[index].c_str();
+    
+    // Should never happen...
+    if (!handler || (*handler!='/' && strncmp(handler,"http:",5) && strncmp(handler,"https:",6)))
+        throw ConfigurationException(
+            "Invalid Location property ($1) in logout notification for Application ($2)",
+            params(2, handler ? handler : "null", getId())
+            );
+
+    // The "Location" property can be in one of three formats:
+    //
+    // 1) a full URI:       http://host/foo/bar
+    // 2) a hostless URI:   http:///foo/bar
+    // 3) a relative path:  /foo/bar
+    //
+    // #  Protocol  Host        Path
+    // 1  handler   handler     handler
+    // 2  handler   resource    handler
+    // 3  resource  resource    handler
+
+    const char* path = NULL;
+
+    // Decide whether to use the handler or the resource for the "protocol"
+    const char* prot;
+    if (*handler != '/') {
+        prot = handler;
+    }
+    else {
+        prot = resource;
+        path = handler;
+    }
+
+    // break apart the "protocol" string into protocol, host, and "the rest"
+    const char* colon=strchr(prot,':');
+    colon += 3;
+    const char* slash=strchr(colon,'/');
+    if (!path)
+        path = slash;
+
+    // Compute the actual protocol and store.
+    string notifyURL(prot, colon-prot);
+
+    // create the "host" from either the colon/slash or from the target string
+    // If prot == handler then we're in either #1 or #2, else #3.
+    // If slash == colon then we're in #2.
+    if (prot != handler || slash == colon) {
+        colon = strchr(resource, ':');
+        colon += 3;      // Get past the ://
+        slash = strchr(colon, '/');
+    }
+    string host(colon, (slash ? slash-colon : strlen(colon)));
+
+    // Build the URL
+    notifyURL += host + path;
+    return notifyURL;
+}
+
 const SessionInitiator* XMLApplication::getDefaultSessionInitiator() const
 {
     if (m_sessionInitDefault) return m_sessionInitDefault;