Sync logout initiator to SAML 2 functionality and remove session before sending to...
authorScott Cantor <cantor.2@osu.edu>
Sun, 6 Apr 2008 20:19:12 +0000 (20:19 +0000)
committerScott Cantor <cantor.2@osu.edu>
Sun, 6 Apr 2008 20:19:12 +0000 (20:19 +0000)
adfs/adfs.cpp

index 4c12f10..a631512 100644 (file)
@@ -178,7 +178,7 @@ namespace {
 #endif
     };
 
-    class SHIBSP_DLLLOCAL ADFSLogoutInitiator : public AbstractHandler, public RemotedHandler
+    class SHIBSP_DLLLOCAL ADFSLogoutInitiator : public AbstractHandler, public LogoutHandler
     {
     public:
         ADFSLogoutInitiator(const DOMElement* e, const char* appId)
@@ -214,7 +214,7 @@ namespace {
 #endif
 
     private:
-        pair<bool,long> doRequest(const Application& application, const char* entityID, HTTPResponse& httpResponse) const;
+        pair<bool,long> doRequest(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse, Session* session) const;
 
         string m_appId;
         auto_ptr_XMLCh m_binding;
@@ -771,15 +771,14 @@ pair<bool,long> ADFSLogoutInitiator::run(SPRequest& request, bool isHandler) con
 
     if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
         // When out of process, we run natively.
-        return doRequest(request.getApplication(), entityID.c_str(), request);
+        return doRequest(request.getApplication(), request, request, session);
     }
     else {
         // When not out of process, we remote the request.
-        Locker locker(session, false);
-        DDF out,in(m_address.c_str());
+        session->unlock();
+        vector<string> headers(1,"Cookie");
+        DDF out,in = wrap(request,&headers);
         DDFJanitor jin(in), jout(out);
-        in.addmember("application_id").string(request.getApplication().getId());
-        in.addmember("entity_id").string(entityID.c_str());
         out=request.getServiceProvider().getListenerService()->send(in);
         return unwrap(request, out);
     }
@@ -788,6 +787,10 @@ pair<bool,long> ADFSLogoutInitiator::run(SPRequest& request, bool isHandler) con
 void ADFSLogoutInitiator::receive(DDF& in, ostream& out)
 {
 #ifndef SHIBSP_LITE
+    // Defer to base class for notifications
+    if (in["notify"].integer() == 1)
+        return LogoutHandler::receive(in, out);
+
     // Find application.
     const char* aid=in["application_id"].string();
     const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
@@ -797,59 +800,109 @@ void ADFSLogoutInitiator::receive(DDF& in, ostream& out)
         throw ConfigurationException("Unable to locate application for logout, deleted?");
     }
     
+    // Unpack the request.
+    auto_ptr<HTTPRequest> req(getRequest(in));
+
     // Set up a response shim.
     DDF ret(NULL);
     DDFJanitor jout(ret);
     auto_ptr<HTTPResponse> resp(getResponse(ret));
     
-    // Since we're remoted, the result should either be a throw, which we pass on,
-    // a false/0 return, which we just return as an empty structure, or a response/redirect,
-    // which we capture in the facade and send back.
-    doRequest(*app, in["entity_id"].string(), *resp.get());
+    Session* session = NULL;
+    try {
+         session = app->getServiceProvider().getSessionCache()->find(*app, *req.get(), NULL, NULL);
+    }
+    catch (exception& ex) {
+        m_log.error("error accessing current session: %s", ex.what());
+    }
 
+    // With no session, we just skip the request and let it fall through to an empty struct return.
+    if (session) {
+        if (session->getEntityID()) {
+            // Since we're remoted, the result should either be a throw, which we pass on,
+            // a false/0 return, which we just return as an empty structure, or a response/redirect,
+            // which we capture in the facade and send back.
+            doRequest(*app, *req.get(), *resp.get(), session);
+        }
+        else {
+             m_log.error("no issuing entityID found in session");
+             session->unlock();
+             app->getServiceProvider().getSessionCache()->remove(*app, *req.get(), resp.get());
+        }
+    }
     out << ret;
 #else
     throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
 #endif
 }
 
-pair<bool,long> ADFSLogoutInitiator::doRequest(const Application& application, const char* entityID, HTTPResponse& response) const
+pair<bool,long> ADFSLogoutInitiator::doRequest(
+    const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse, Session* session
+    ) const
 {
+    // Do back channel notification.
+    vector<string> sessions(1, session->getID());
+    if (!notifyBackChannel(application, httpRequest.getRequestURL(), sessions, false)) {
+        session->unlock();
+        application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
+        return sendLogoutPage(application, httpRequest, httpResponse, true, "Partial logout failure.");
+    }
+
 #ifndef SHIBSP_LITE
-    try {
-        if (!entityID)
-            throw ConfigurationException("Missing entityID parameter.");
+    pair<bool,long> ret = make_pair(false,0L);
 
+    try {
         // With a session in hand, we can create a request message, if we can find a compatible endpoint.
         MetadataProvider* m=application.getMetadataProvider();
-        Locker locker(m);
-        MetadataProvider::Criteria mc(entityID, &IDPSSODescriptor::ELEMENT_QNAME, m_binding.get());
+        Locker metadataLocker(m);
+        MetadataProvider::Criteria mc(session->getEntityID(), &IDPSSODescriptor::ELEMENT_QNAME, m_binding.get());
         pair<const EntityDescriptor*,const RoleDescriptor*> entity=m->getEntityDescriptor(mc);
-        if (!entity.first)
-            throw MetadataException("Unable to locate metadata for identity provider ($entityID)", namedparams(1, "entityID", entityID));
-        else if (!entity.second)
-            throw MetadataException("Unable to locate ADFS IdP role for identity provider ($entityID).", namedparams(1, "entityID", entityID));
-
+        if (!entity.first) {
+            throw MetadataException(
+                "Unable to locate metadata for identity provider ($entityID)", namedparams(1, "entityID", session->getEntityID())
+                );
+        }
+        else if (!entity.second) {
+            throw MetadataException(
+                "Unable to locate ADFS IdP role for identity provider ($entityID).", namedparams(1, "entityID", session->getEntityID())
+                );
+        }
+        
         const EndpointType* ep = EndpointManager<SingleLogoutService>(
             dynamic_cast<const IDPSSODescriptor*>(entity.second)->getSingleLogoutServices()
             ).getByBinding(m_binding.get());
         if (!ep) {
             throw MetadataException(
-                "Unable to locate ADFS single logout service for identity provider ($entityID).",
-                namedparams(1, "entityID", entityID)
+                "Unable to locate ADFS single logout service for identity provider ($entityID).", namedparams(1, "entityID", session->getEntityID())
                 );
         }
 
+        // Save off return location as RelayState.
+        string relayState;
+        const char* returnloc = httpRequest.getParameter("return");
+        if (returnloc) {
+            relayState = returnloc;
+            preserveRelayState(application, httpResponse, relayState);
+        }
+        
         auto_ptr_char dest(ep->getLocation());
-
         string req=string(dest.get()) + (strchr(dest.get(),'?') ? '&' : '?') + "wa=wsignout1.0";
-        return make_pair(true,response.sendRedirect(req.c_str()));
+        if (!relayState.empty())
+            req += "&wreply=" + relayState;
+        ret.second = httpResponse.sendRedirect(req.c_str());
+        ret.first = true;
     }
     catch (exception& ex) {
         m_log.error("error issuing ADFS logout request: %s", ex.what());
     }
 
-    return make_pair(false,0L);
+    if (session) {
+        session->unlock();
+        session = NULL;
+        application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
+    }
+
+    return ret;
 #else
     throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
 #endif