#include "util/TemplateParameters.h"
#include <fstream>
-#include <log4cpp/Category.hh>
#include <xmltooling/XMLToolingConfig.h>
#include <xmltooling/util/URLEncoder.h>
-#ifndef SHIBSP_LITE
-#endif
-
using namespace shibsp;
using namespace xmltooling;
using namespace std;
+pair<bool,long> LogoutHandler::sendLogoutPage(
+ const Application& application, const HTTPRequest& request, HTTPResponse& response, bool local, const char* status
+ ) const
+{
+ const PropertySet* props = application.getPropertySet("Errors");
+ pair<bool,const char*> prop = props ? props->getString(local ? "localLogout" : "globalLogout") : pair<bool,const char*>(false,NULL);
+ 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.m_request = &request;
+ tp.setPropertySet(props);
+ 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));
+}
+
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);
+ // 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<bool,long> 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)
const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : NULL;
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?");
}
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);
}
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();
// Add a signal that we're coming back from notification and the next index.
locstr << "?notifying=1&index=" << index;
+ // Add return if set.
+ if (param)
+ locstr << "&return=" << encoder->encode(param);
+
// We preserve anything we're instructed to directly.
if (params) {
for (map<string,string>::const_iterator p = params->begin(); p!=params->end(); ++p)
}
}
- // 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<string>& sessions) const
+#ifndef SHIBSP_LITE
+#include "util/SPConstants.h"
+#include <xmltooling/impl/AnyElement.h>
+#include <xmltooling/soap/SOAP.h>
+#include <xmltooling/soap/SOAPClient.h>
+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);
+ }
+ };
+};
+#endif
+
+bool LogoutHandler::notifyBackChannel(
+ const Application& application, const char* requestURL, const vector<string>& sessions, bool local
+ ) const
{
+ 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<Envelope> 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(QName(NULL, _type), local ? _local : _global);
+ for (vector<string>::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
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<string>::const_iterator i = sessions.begin(); i!=sessions.end(); ++i) {
DDF temp = DDF(NULL).string(i->c_str());
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
-{
- pair<bool,const char*> 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));
-}