From: Scott Cantor Date: Sun, 6 Apr 2008 20:19:12 +0000 (+0000) Subject: Sync logout initiator to SAML 2 functionality and remove session before sending to... X-Git-Tag: 2.1.0~67 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fcpp-sp.git;a=commitdiff_plain;h=54c8a5c50713c1ed5e5d4a2f26a8a00585e74475 Sync logout initiator to SAML 2 functionality and remove session before sending to IP/STS. --- diff --git a/adfs/adfs.cpp b/adfs/adfs.cpp index 4c12f10..a631512 100644 --- a/adfs/adfs.cpp +++ b/adfs/adfs.cpp @@ -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 doRequest(const Application& application, const char* entityID, HTTPResponse& httpResponse) const; + pair 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 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 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 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 req(getRequest(in)); + // Set up a response shim. DDF ret(NULL); DDFJanitor jout(ret); auto_ptr 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 ADFSLogoutInitiator::doRequest(const Application& application, const char* entityID, HTTPResponse& response) const +pair ADFSLogoutInitiator::doRequest( + const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse, Session* session + ) const { + // Do back channel notification. + vector 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 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 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( dynamic_cast(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