Move back channel notify loop out of base class for better control.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 9 Jul 2007 19:22:44 +0000 (19:22 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 9 Jul 2007 19:22:44 +0000 (19:22 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2343 cb58f699-b61c-0410-a6fe-9272a202ed29

shibsp/handler/LogoutHandler.h
shibsp/handler/impl/LocalLogoutInitiator.cpp
shibsp/handler/impl/LogoutHandler.cpp
shibsp/handler/impl/SAML2LogoutInitiator.cpp

index 1c97ba3..da10672 100644 (file)
@@ -33,9 +33,6 @@ namespace shibsp {
      */
     class SHIBSP_API LogoutHandler : public RemotedHandler
     {
-    protected:
-        LogoutHandler() {}
-
     public:
         virtual ~LogoutHandler() {}
 
@@ -67,6 +64,11 @@ namespace shibsp {
         void receive(DDF& in, std::ostream& out);
 
     protected:
+        LogoutHandler() : m_initiator(true) {}
+        
+        /** Flag indicating whether the subclass is acting as a LogoutInitiator. */
+        bool m_initiator;
+
         /**
          * Perform front-channel logout notifications for an Application.
          *
@@ -77,15 +79,13 @@ namespace shibsp {
          * @param request       last request from browser
          * @param response      response to use for next notification
          * @param params        map of query string parameters to preserve across notifications, optionally with initial values
-         * @param sessions      optional array of session keys being logged out
          * @return  indicator of a completed response along with the status code to return from the handler
          */
         std::pair<bool,long> notifyFrontChannel(
             const Application& application,
             const xmltooling::HTTPRequest& request,
             xmltooling::HTTPResponse& response,
-            const std::map<std::string,std::string>* params=NULL,
-            const std::vector<std::string>* sessions=NULL
+            const std::map<std::string,std::string>* params=NULL
             ) const;
 
         /**
index 004af9f..50ccc2f 100644 (file)
@@ -81,8 +81,15 @@ pair<bool,long> LocalLogoutInitiator::run(SPRequest& request, bool isHandler) co
     // Get session ID from cookie.
     pair<string,const char*> shib_cookie = request.getApplication().getCookieNameProps("_shibsession_");
     const char* session_id = request.getCookie(shib_cookie.first.c_str());
-    if (session_id)
+    if (session_id) {
+        // Do back channel notification.
+        vector<string> sessions(1, session_id);
+        if (!notifyBackChannel(request.getApplication(), sessions)) {
+            request.getApplication().getServiceProvider().getSessionCache()->remove(session_id, request.getApplication());
+            return sendLogoutPage(request.getApplication(), request, true, "Partial logout failure.");
+        }
         request.getServiceProvider().getSessionCache()->remove(session_id, request.getApplication());
+    }
 
     return sendLogoutPage(request.getApplication(), request, true, "Logout was successful.");
 }
index f2a85d0..17ee53f 100644 (file)
@@ -45,45 +45,16 @@ pair<bool,long> LogoutHandler::run(SPRequest& request, bool isHandler) const
     // If no location for this handler, we're inside a chain, so do nothing.
     if (!getString("Location").first)
         return make_pair(false,0);
-
-    string session_id;
-    try {
-        // Get the session, ignoring most checks and don't cache the lock.
-        Session* session = request.getSession(false,true,false);
-        if (session)
-            session_id = session->getID();
-        session->unlock();
-    }
-    catch (exception& ex) {
-        log4cpp::Category::getInstance(SHIBSP_LOGCAT".Logout").error("error accessing current session: %s", ex.what());
-    }
-
-    if (session_id.empty())
-        return sendLogoutPage(request.getApplication(), request, true, "The local logout process was only partially completed.");
+    
+    // If this isn't a LogoutInitiator, we only "continue" a notification loop, rather than starting one.
+    if (!m_initiator && !request.getParameter("notifying"))
+        return make_pair(false,0);
 
     // Try another front-channel notification. No extra parameters and the session is implicit.
     pair<bool,long> ret = notifyFrontChannel(request.getApplication(), request, request);
     if (ret.first)
         return ret;
 
-    // Now we complete with back-channel notification, which has to be out of process.
-
-    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
-        // When out of process, we run natively.
-        vector<string> sessions(1,session_id);
-        notifyBackChannel(request.getApplication(), sessions);
-    }
-    else {
-        // When not out of process, we remote the back channel work.
-        DDF out,in(m_address.c_str());
-        DDFJanitor jin(in), jout(out);
-        in.addmember("notify").integer(1);
-        in.addmember("application_id").string(request.getApplication().getId());
-        DDF s = DDF(NULL).string(session_id.c_str());
-        in.addmember("sessions").list().add(s);
-        out=request.getServiceProvider().getListenerService()->send(in);
-    }
-
     return make_pair(false,0);
 }
 
@@ -109,7 +80,8 @@ void LogoutHandler::receive(DDF& in, ostream& out)
     while (temp.isstring()) {
         sessions.push_back(temp.string());
         temp = s.next();
-        notifyBackChannel(*app, sessions);
+        if (notifyBackChannel(*app, sessions))
+            ret.integer(1);
     }
 
     out << ret;
@@ -119,8 +91,7 @@ pair<bool,long> LogoutHandler::notifyFrontChannel(
     const Application& application,
     const HTTPRequest& request,
     HTTPResponse& response,
-    const map<string,string>* params,
-    const vector<string>* sessions
+    const map<string,string>* params
     ) const
 {
     // Index of notification point starts at 0.
@@ -139,20 +110,6 @@ pair<bool,long> LogoutHandler::notifyFrontChannel(
     // Start with an "action" telling the application what this is about.
     loc = loc + (strchr(loc.c_str(),'?') ? '&' : '?') + "action=logout";
 
-    // Attach the "sessions" parameter, if any.
-    if (sessions) {
-        string keys;
-        for (vector<string>::const_iterator k = sessions->begin(); k!=sessions->end(); ++k) {
-            if (!keys.empty())
-                keys += ',';
-            keys += *k;
-        }
-        loc = loc + "&sessions=" + keys;
-    }
-    else if (param = request.getParameter("sessions")) {
-        loc = loc + "&sessions=" + request.getParameter("sessions");
-    }
-
     // Now we create a second URL representing the return location back to us.
     const char* start = request.getRequestURL();
     const char* end = strchr(start,'?');
@@ -179,11 +136,26 @@ pair<bool,long> LogoutHandler::notifyFrontChannel(
 
 bool LogoutHandler::notifyBackChannel(const Application& application, const vector<string>& sessions) const
 {
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
 #ifndef SHIBSP_LITE
-    return false;
+        return true;
 #else
-    throw ConfigurationException("Cannot perform back channel notification using lite version of shibsp library.");
+        return false;
 #endif
+    }
+
+    // When not out of process, we remote the back channel work.
+    DDF out,in(m_address.c_str());
+    DDFJanitor jin(in), jout(out);
+    in.addmember("notify").integer(1);
+    in.addmember("application_id").string(application.getId());
+    DDF s = in.addmember("sessions").list();
+    for (vector<string>::const_iterator i = sessions.begin(); i!=sessions.end(); ++i) {
+        DDF temp = DDF(NULL).string(i->c_str());
+        s.add(temp);
+    }
+    out=application.getServiceProvider().getListenerService()->send(in);
+    return (out.integer() == 1);
 }
 
 pair<bool,long> LogoutHandler::sendLogoutPage(const Application& application, HTTPResponse& response, bool local, const char* status) const
index c60ad96..4a0f630 100644 (file)
@@ -166,11 +166,14 @@ void SAML2LogoutInitiator::setParent(const PropertySet* parent)
 
 pair<bool,long> SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) const
 {
-    // Defer to base class first.
+    // Defer to base class for front-channel loop first.
     pair<bool,long> ret = LogoutHandler::run(request, isHandler);
     if (ret.first)
         return ret;
 
+    // At this point we know the front-channel is handled.
+    // We need the session to do any other work.
+
     Session* session = NULL;
     try {
         session = request.getSession(false, true, false);  // don't cache it and ignore all checks
@@ -178,16 +181,16 @@ pair<bool,long> SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) co
             return make_pair(false,0);
 
         // We only handle SAML 2.0 sessions.
-        if (!XMLString::equals(session->getProtocol(), m_protocol.get()))
+        if (!XMLString::equals(session->getProtocol(), m_protocol.get())) {
+            session->unlock();
             return make_pair(false,0);
+        }
     }
     catch (exception& ex) {
         m_log.error("error accessing current session: %s", ex.what());
         return make_pair(false,0);
     }
 
-    // At this point, notification is completed, and we're ready to issue a LogoutRequest.
-
     if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
         // When out of process, we run natively.
         return doRequest(request.getApplication(), session, request);
@@ -256,6 +259,14 @@ void SAML2LogoutInitiator::receive(DDF& in, ostream& out)
 
 pair<bool,long> SAML2LogoutInitiator::doRequest(const Application& application, Session* session, HTTPResponse& response) const
 {
+    // Do back channel notification.
+    vector<string> sessions(1, session->getID());
+    if (!notifyBackChannel(application, sessions)) {
+        session->unlock();
+        application.getServiceProvider().getSessionCache()->remove(sessions.front().c_str(), application);
+        return sendLogoutPage(application, response, true, "Partial logout failure.");
+    }
+
 #ifndef SHIBSP_LITE
     pair<bool,long> ret = make_pair(false,0);
     try {
@@ -408,21 +419,21 @@ LogoutRequest* SAML2LogoutInitiator::buildRequest(
                     mcc.setXMLAlgorithm(sigalg.second);
                 const Credential* cred = credResolver->resolve(&mcc);
                 if (cred) {
-                    xmlsignature::Signature* sig = xmlsignature::SignatureBuilder::buildSignature();\r
-                    msg->setSignature(sig);\r
-                    pair<bool, const XMLCh*> alg = relyingParty->getXMLString("signatureAlg");\r
-                    if (alg.first)\r
-                        sig->setSignatureAlgorithm(alg.second);\r
-                    alg = relyingParty->getXMLString("digestAlg");\r
-                    if (alg.first) {\r
-                        ContentReference* cr = dynamic_cast<ContentReference*>(sig->getContentReference());\r
-                        if (cr)\r
-                            cr->setDigestAlgorithm(alg.second);\r
-                    }\r
-            \r
-                    // Sign response while marshalling.\r
-                    vector<xmlsignature::Signature*> sigs(1,sig);\r
-                    msg->marshall((DOMDocument*)NULL,&sigs,cred);\r
+                    xmlsignature::Signature* sig = xmlsignature::SignatureBuilder::buildSignature();
+                    msg->setSignature(sig);
+                    pair<bool, const XMLCh*> alg = relyingParty->getXMLString("signatureAlg");
+                    if (alg.first)
+                        sig->setSignatureAlgorithm(alg.second);
+                    alg = relyingParty->getXMLString("digestAlg");
+                    if (alg.first) {
+                        ContentReference* cr = dynamic_cast<ContentReference*>(sig->getContentReference());
+                        if (cr)
+                            cr->setDigestAlgorithm(alg.second);
+                    }
+            
+                    // Sign response while marshalling.
+                    vector<xmlsignature::Signature*> sigs(1,sig);
+                    msg->marshall((DOMDocument*)NULL,&sigs,cred);
                 }
                 else {
                     m_log.warn("no signing credential resolved, leaving message unsigned");