From 6d798864578ed02541bcbab1dd38faca8c31080c Mon Sep 17 00:00:00 2001 From: cantor Date: Mon, 9 Jul 2007 19:22:44 +0000 Subject: [PATCH] Move back channel notify loop out of base class for better control. git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2343 cb58f699-b61c-0410-a6fe-9272a202ed29 --- shibsp/handler/LogoutHandler.h | 12 ++--- shibsp/handler/impl/LocalLogoutInitiator.cpp | 9 +++- shibsp/handler/impl/LogoutHandler.cpp | 76 +++++++++------------------- shibsp/handler/impl/SAML2LogoutInitiator.cpp | 49 +++++++++++------- 4 files changed, 68 insertions(+), 78 deletions(-) diff --git a/shibsp/handler/LogoutHandler.h b/shibsp/handler/LogoutHandler.h index 1c97ba3..da10672 100644 --- a/shibsp/handler/LogoutHandler.h +++ b/shibsp/handler/LogoutHandler.h @@ -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 notifyFrontChannel( const Application& application, const xmltooling::HTTPRequest& request, xmltooling::HTTPResponse& response, - const std::map* params=NULL, - const std::vector* sessions=NULL + const std::map* params=NULL ) const; /** diff --git a/shibsp/handler/impl/LocalLogoutInitiator.cpp b/shibsp/handler/impl/LocalLogoutInitiator.cpp index 004af9f..50ccc2f 100644 --- a/shibsp/handler/impl/LocalLogoutInitiator.cpp +++ b/shibsp/handler/impl/LocalLogoutInitiator.cpp @@ -81,8 +81,15 @@ pair LocalLogoutInitiator::run(SPRequest& request, bool isHandler) co // Get session ID from cookie. pair 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 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."); } diff --git a/shibsp/handler/impl/LogoutHandler.cpp b/shibsp/handler/impl/LogoutHandler.cpp index f2a85d0..17ee53f 100644 --- a/shibsp/handler/impl/LogoutHandler.cpp +++ b/shibsp/handler/impl/LogoutHandler.cpp @@ -45,45 +45,16 @@ pair 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 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 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 LogoutHandler::notifyFrontChannel( const Application& application, const HTTPRequest& request, HTTPResponse& response, - const map* params, - const vector* sessions + const map* params ) const { // Index of notification point starts at 0. @@ -139,20 +110,6 @@ pair 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::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 LogoutHandler::notifyFrontChannel( bool LogoutHandler::notifyBackChannel(const Application& application, const vector& 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::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 LogoutHandler::sendLogoutPage(const Application& application, HTTPResponse& response, bool local, const char* status) const diff --git a/shibsp/handler/impl/SAML2LogoutInitiator.cpp b/shibsp/handler/impl/SAML2LogoutInitiator.cpp index c60ad96..4a0f630 100644 --- a/shibsp/handler/impl/SAML2LogoutInitiator.cpp +++ b/shibsp/handler/impl/SAML2LogoutInitiator.cpp @@ -166,11 +166,14 @@ void SAML2LogoutInitiator::setParent(const PropertySet* parent) pair SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) const { - // Defer to base class first. + // Defer to base class for front-channel loop first. pair 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 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 SAML2LogoutInitiator::doRequest(const Application& application, Session* session, HTTPResponse& response) const { + // Do back channel notification. + vector 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 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(); - msg->setSignature(sig); - pair alg = relyingParty->getXMLString("signatureAlg"); - if (alg.first) - sig->setSignatureAlgorithm(alg.second); - alg = relyingParty->getXMLString("digestAlg"); - if (alg.first) { - ContentReference* cr = dynamic_cast(sig->getContentReference()); - if (cr) - cr->setDigestAlgorithm(alg.second); - } - - // Sign response while marshalling. - vector sigs(1,sig); - msg->marshall((DOMDocument*)NULL,&sigs,cred); + xmlsignature::Signature* sig = xmlsignature::SignatureBuilder::buildSignature(); + msg->setSignature(sig); + pair alg = relyingParty->getXMLString("signatureAlg"); + if (alg.first) + sig->setSignatureAlgorithm(alg.second); + alg = relyingParty->getXMLString("digestAlg"); + if (alg.first) { + ContentReference* cr = dynamic_cast(sig->getContentReference()); + if (cr) + cr->setDigestAlgorithm(alg.second); + } + + // Sign response while marshalling. + vector sigs(1,sig); + msg->marshall((DOMDocument*)NULL,&sigs,cred); } else { m_log.warn("no signing credential resolved, leaving message unsigned"); -- 2.1.4