X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=shibsp%2Fhandler%2Fimpl%2FLogoutHandler.cpp;h=63e7553661d77620f77ee1cca6340b84c06cb93d;hb=a62c416b6ecfc17d89853a8f14604249c94d2f60;hp=17ee53f484e34ca3f33be9dea10e7b664bfa1cbf;hpb=6d798864578ed02541bcbab1dd38faca8c31080c;p=shibboleth%2Fsp.git diff --git a/shibsp/handler/impl/LogoutHandler.cpp b/shibsp/handler/impl/LogoutHandler.cpp index 17ee53f..63e7553 100644 --- a/shibsp/handler/impl/LogoutHandler.cpp +++ b/shibsp/handler/impl/LogoutHandler.cpp @@ -1,6 +1,6 @@ /* - * Copyright 2001-2007 Internet2 - * + * Copyright 2001-2010 Internet2 + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,7 +16,7 @@ /** * LogoutHandler.cpp - * + * * Base class for logout-related handlers. */ @@ -25,52 +25,88 @@ #include "Application.h" #include "ServiceProvider.h" #include "SessionCache.h" +#include "SPRequest.h" #include "handler/LogoutHandler.h" #include "util/TemplateParameters.h" #include -#include #include +#include #include -#ifndef SHIBSP_LITE -#endif - using namespace shibsp; using namespace xmltooling; using namespace std; +LogoutHandler::LogoutHandler() : m_initiator(true) +{ +} + +LogoutHandler::~LogoutHandler() +{ +} + +pair LogoutHandler::sendLogoutPage( + const Application& application, const HTTPRequest& request, HTTPResponse& response, bool local, const char* status + ) const +{ + return sendLogoutPage(application, request, response, local ? "local" : "global"); +} + +pair LogoutHandler::sendLogoutPage( + const Application& application, const HTTPRequest& request, HTTPResponse& response, const char* type + ) const +{ + string tname = string(type) + "Logout"; + const PropertySet* props = application.getPropertySet("Errors"); + pair prop = props ? props->getString(tname.c_str()) : pair(false,nullptr); + if (!prop.first) { + tname += ".html"; + prop.second = tname.c_str(); + } + response.setContentType("text/html"); + response.setResponseHeader("Expires","01-Jan-1997 12:00:00 GMT"); + response.setResponseHeader("Cache-Control","private,no-store,no-cache"); + string fname(prop.second); + ifstream infile(XMLToolingConfig::getConfig().getPathResolver()->resolve(fname, PathResolver::XMLTOOLING_CFG_FILE).c_str()); + if (!infile) + throw ConfigurationException("Unable to access $1 HTML template.", params(1,prop.second)); + TemplateParameters tp; + tp.m_request = &request; + tp.setPropertySet(props); + tp.m_map["logoutStatus"] = "Logout completed successfully."; // Backward compatibility. + stringstream str; + XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp); + return make_pair(true,response.sendResponse(str)); +} + 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); - + // If we're inside a chain, do nothing. + if (getParent()) + return make_pair(false,0L); + // 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); + return make_pair(false,0L); // 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; - - return make_pair(false,0); + return notifyFrontChannel(request.getApplication(), request, request); } void LogoutHandler::receive(DDF& in, ostream& out) { - DDF ret(NULL); + DDF ret(nullptr); DDFJanitor jout(ret); if (in["notify"].integer() != 1) throw ListenerException("Unsupported operation."); // Find application. const char* aid=in["application_id"].string(); - const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL; + const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr; if (!app) { // Something's horribly wrong. - log4cpp::Category::getInstance(SHIBSP_LOGCAT".Logout").error("couldn't find application (%s) for logout", aid ? aid : "(missing)"); + Category::getInstance(SHIBSP_LOGCAT".Logout").error("couldn't find application (%s) for logout", aid ? aid : "(missing)"); throw ConfigurationException("Unable to locate application for logout, deleted?"); } @@ -80,7 +116,7 @@ void LogoutHandler::receive(DDF& in, ostream& out) while (temp.isstring()) { sessions.push_back(temp.string()); temp = s.next(); - if (notifyBackChannel(*app, sessions)) + if (notifyBackChannel(*app, in["url"].string(), sessions, in["local"].integer()==1)) ret.integer(1); } @@ -100,10 +136,13 @@ pair LogoutHandler::notifyFrontChannel( if (param) index = atoi(param); + // "return" is a backwards-compatible "eventual destination" to go back to after logout completes. + param = request.getParameter("return"); + // Fetch the next front notification URL and bump the index for the next round trip. - string loc = application.getNotificationURL(request, true, index++); + string loc = application.getNotificationURL(request.getRequestURL(), true, index++); if (loc.empty()) - return make_pair(false,0); + return make_pair(false,0L); const URLEncoder* encoder = XMLToolingConfig::getConfig().getURLEncoder(); @@ -111,34 +150,114 @@ pair LogoutHandler::notifyFrontChannel( loc = loc + (strchr(loc.c_str(),'?') ? '&' : '?') + "action=logout"; // Now we create a second URL representing the return location back to us. + ostringstream locstr; const char* start = request.getRequestURL(); const char* end = strchr(start,'?'); string tempstr(start, end ? end-start : strlen(start)); - ostringstream locstr(tempstr); // Add a signal that we're coming back from notification and the next index. - locstr << "?notifying=1&index=" << index; + locstr << tempstr << "?notifying=1&index=" << index; - // Now we preserve anything we're instructed to (or populate the initial values from the map). + // Add return if set. + if (param) + locstr << "&return=" << encoder->encode(param); + + // We preserve anything we're instructed to directly. if (params) { - for (map::const_iterator p = params->begin(); p!=params->end(); ++p) { - if (!p->second.empty()) - locstr << '&' << p->first << '=' << encoder->encode(p->second.c_str()); - else if (param = request.getParameter(p->first.c_str())) - locstr << '&' << p->first << '=' << encoder->encode(param); + for (map::const_iterator p = params->begin(); p!=params->end(); ++p) + locstr << '&' << p->first << '=' << encoder->encode(p->second.c_str()); + } + else { + for (vector::const_iterator q = m_preserve.begin(); q!=m_preserve.end(); ++q) { + param = request.getParameter(q->c_str()); + if (param) + locstr << '&' << *q << '=' << encoder->encode(param); } } - // Add the return parameter to the destination location and redirect. + // Add the notifier's return parameter to the destination location and redirect. + // This is NOT the same as the return parameter that might be embedded inside it ;-) loc = loc + "&return=" + encoder->encode(locstr.str().c_str()); return make_pair(true,response.sendRedirect(loc.c_str())); } -bool LogoutHandler::notifyBackChannel(const Application& application, const vector& sessions) const +#ifndef SHIBSP_LITE +#include "util/SPConstants.h" +#include +#include +#include +#include +using namespace soap11; +namespace { + static const XMLCh LogoutNotification[] = UNICODE_LITERAL_18(L,o,g,o,u,t,N,o,t,i,f,i,c,a,t,i,o,n); + static const XMLCh SessionID[] = UNICODE_LITERAL_9(S,e,s,s,i,o,n,I,D); + static const XMLCh _type[] = UNICODE_LITERAL_4(t,y,p,e); + static const XMLCh _local[] = UNICODE_LITERAL_5(l,o,c,a,l); + static const XMLCh _global[] = UNICODE_LITERAL_6(g,l,o,b,a,l); + + class SHIBSP_DLLLOCAL SOAPNotifier : public soap11::SOAPClient + { + public: + SOAPNotifier() {} + virtual ~SOAPNotifier() {} + private: + void prepareTransport(SOAPTransport& transport) { + transport.setVerifyHost(false); + HTTPSOAPTransport* http = dynamic_cast(&transport); + if (http) { + http->useChunkedEncoding(false); + http->setRequestHeader("User-Agent", PACKAGE_NAME); + http->setRequestHeader(PACKAGE_NAME, PACKAGE_VERSION); + } + } + }; +}; +#endif + +bool LogoutHandler::notifyBackChannel( + const Application& application, const char* requestURL, const vector& sessions, bool local + ) const { + if (sessions.empty()) { + Category::getInstance(SHIBSP_LOGCAT".Logout").error("no sessions supplied to back channel notification method"); + return false; + } + + unsigned int index = 0; + string endpoint = application.getNotificationURL(requestURL, false, index++); + if (endpoint.empty()) + return true; + if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { #ifndef SHIBSP_LITE - return true; + auto_ptr env(EnvelopeBuilder::buildEnvelope()); + Body* body = BodyBuilder::buildBody(); + env->setBody(body); + ElementProxy* msg = new AnyElementImpl(shibspconstants::SHIB2SPNOTIFY_NS, LogoutNotification); + body->getUnknownXMLObjects().push_back(msg); + msg->setAttribute(xmltooling::QName(nullptr, _type), local ? _local : _global); + for (vector::const_iterator s = sessions.begin(); s!=sessions.end(); ++s) { + auto_ptr_XMLCh temp(s->c_str()); + ElementProxy* child = new AnyElementImpl(shibspconstants::SHIB2SPNOTIFY_NS, SessionID); + child->setTextContent(temp.get()); + msg->getUnknownXMLObjects().push_back(child); + } + + bool result = true; + SOAPNotifier soaper; + while (!endpoint.empty()) { + try { + soaper.send(*env.get(), SOAPTransport::Address(application.getId(), application.getId(), endpoint.c_str())); + delete soaper.receive(); + } + catch (exception& ex) { + Category::getInstance(SHIBSP_LOGCAT".Logout").error("error notifying application of logout event: %s", ex.what()); + result = false; + } + soaper.reset(); + endpoint = application.getNotificationURL(requestURL, false, index++); + } + return result; #else return false; #endif @@ -149,35 +268,14 @@ bool LogoutHandler::notifyBackChannel(const Application& application, const vect DDFJanitor jin(in), jout(out); in.addmember("notify").integer(1); in.addmember("application_id").string(application.getId()); + in.addmember("url").string(requestURL); + if (local) + in.addmember("local").integer(1); 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()); + DDF temp = DDF(nullptr).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 -{ - pair prop = application.getString(local ? "localLogout" : "globalLogout"); - if (prop.first) { - response.setContentType("text/html"); - response.setResponseHeader("Expires","01-Jan-1997 12:00:00 GMT"); - response.setResponseHeader("Cache-Control","private,no-store,no-cache"); - ifstream infile(prop.second); - if (!infile) - throw ConfigurationException("Unable to access $1 HTML template.", params(1,local ? "localLogout" : "globalLogout")); - TemplateParameters tp; - tp.setPropertySet(application.getPropertySet("Errors")); - if (status) - tp.m_map["logoutStatus"] = status; - stringstream str; - XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp); - return make_pair(true,response.sendResponse(str)); - } - prop = application.getString("homeURL"); - if (!prop.first) - prop.second = "/"; - return make_pair(true,response.sendRedirect(prop.second)); -}