*/
class SHIBSP_API LogoutHandler : public RemotedHandler
{
- protected:
- LogoutHandler() {}
-
public:
virtual ~LogoutHandler() {}
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.
*
* @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<bool,long> notifyFrontChannel(
const Application& application,
const xmltooling::HTTPRequest& request,
xmltooling::HTTPResponse& response,
- const std::map<std::string,std::string>* params=NULL,
- const std::vector<std::string>* sessions=NULL
+ const std::map<std::string,std::string>* params=NULL
) const;
/**
// Get session ID from cookie.
pair<string,const char*> 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<string> 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.");
}
// 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<bool,long> 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<string> 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);
}
while (temp.isstring()) {
sessions.push_back(temp.string());
temp = s.next();
- notifyBackChannel(*app, sessions);
+ if (notifyBackChannel(*app, sessions))
+ ret.integer(1);
}
out << ret;
const Application& application,
const HTTPRequest& request,
HTTPResponse& response,
- const map<string,string>* params,
- const vector<string>* sessions
+ const map<string,string>* params
) const
{
// Index of notification point starts at 0.
// 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<string>::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,'?');
bool LogoutHandler::notifyBackChannel(const Application& application, const vector<string>& 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<string>::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<bool,long> LogoutHandler::sendLogoutPage(const Application& application, HTTPResponse& response, bool local, const char* status) const
pair<bool,long> SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) const
{
- // Defer to base class first.
+ // Defer to base class for front-channel loop first.
pair<bool,long> 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
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);
pair<bool,long> SAML2LogoutInitiator::doRequest(const Application& application, Session* session, HTTPResponse& response) const
{
+ // Do back channel notification.
+ vector<string> 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<bool,long> ret = make_pair(false,0);
try {
mcc.setXMLAlgorithm(sigalg.second);
const Credential* cred = credResolver->resolve(&mcc);
if (cred) {
- xmlsignature::Signature* sig = xmlsignature::SignatureBuilder::buildSignature();\r
- msg->setSignature(sig);\r
- pair<bool, const XMLCh*> alg = relyingParty->getXMLString("signatureAlg");\r
- if (alg.first)\r
- sig->setSignatureAlgorithm(alg.second);\r
- alg = relyingParty->getXMLString("digestAlg");\r
- if (alg.first) {\r
- ContentReference* cr = dynamic_cast<ContentReference*>(sig->getContentReference());\r
- if (cr)\r
- cr->setDigestAlgorithm(alg.second);\r
- }\r
- \r
- // Sign response while marshalling.\r
- vector<xmlsignature::Signature*> sigs(1,sig);\r
- msg->marshall((DOMDocument*)NULL,&sigs,cred);\r
+ xmlsignature::Signature* sig = xmlsignature::SignatureBuilder::buildSignature();
+ msg->setSignature(sig);
+ pair<bool, const XMLCh*> alg = relyingParty->getXMLString("signatureAlg");
+ if (alg.first)
+ sig->setSignatureAlgorithm(alg.second);
+ alg = relyingParty->getXMLString("digestAlg");
+ if (alg.first) {
+ ContentReference* cr = dynamic_cast<ContentReference*>(sig->getContentReference());
+ if (cr)
+ cr->setDigestAlgorithm(alg.second);
+ }
+
+ // Sign response while marshalling.
+ vector<xmlsignature::Signature*> sigs(1,sig);
+ msg->marshall((DOMDocument*)NULL,&sigs,cred);
}
else {
m_log.warn("no signing credential resolved, leaving message unsigned");