[cpp-sp] branch master updated: SSPCPP-798 - Administrative logout
Scott Cantor
cantor.2 at osu.edu
Wed Apr 25 20:02:22 EDT 2018
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch master
in repository cpp-sp.
View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=dd51ee26c29d29304288bde6b7d13ae45f26355b
The following commit(s) were added to refs/heads/master by this push:
new dd51ee2 SSPCPP-798 - Administrative logout
dd51ee2 is described below
commit dd51ee26c29d29304288bde6b7d13ae45f26355b
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Apr 25 20:01:24 2018 -0400
SSPCPP-798 - Administrative logout
https://issues.shibboleth.net/jira/browse/SSPCPP-798
Missing file, plus revocation features to prevent recovery.
---
adfs/adfs.cpp | 15 +-
shibsp/Makefile.am | 22 +-
shibsp/SessionCache.h | 11 +-
shibsp/handler/impl/AdminLogoutInitiator.cpp | 467 ++++++++++++++++++++++++
shibsp/handler/impl/AttributeCheckerHandler.cpp | 18 +-
shibsp/handler/impl/LocalLogoutInitiator.cpp | 7 +-
shibsp/handler/impl/SAML2Logout.cpp | 10 +-
shibsp/handler/impl/SAML2LogoutInitiator.cpp | 22 +-
shibsp/impl/StorageServiceSessionCache.cpp | 113 ++++--
shibsp/impl/StorageServiceSessionCache.h | 20 +-
10 files changed, 620 insertions(+), 85 deletions(-)
diff --git a/adfs/adfs.cpp b/adfs/adfs.cpp
index ed3d681..0158b03 100644
--- a/adfs/adfs.cpp
+++ b/adfs/adfs.cpp
@@ -918,8 +918,9 @@ void ADFSLogoutInitiator::receive(DDF& in, ostream& out)
}
else {
m_log.error("no issuing entityID found in session");
+ time_t revocationExp = session->getExpiration();
session->unlock();
- app->getServiceProvider().getSessionCache()->remove(*app, *req, resp.get());
+ app->getServiceProvider().getSessionCache()->remove(*app, *req, resp.get(), revocationExp);
}
}
out << ret;
@@ -944,9 +945,10 @@ pair<bool,long> ADFSLogoutInitiator::doRequest(
application.getServiceProvider().getTransactionLog()->write(*logout_event);
}
#endif
+ time_t revocationExp = session->getExpiration();
sessionLocker.assign();
session = nullptr;
- application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
+ application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse, revocationExp);
return sendLogoutPage(application, httpRequest, httpResponse, "partial");
}
@@ -1008,16 +1010,17 @@ pair<bool,long> ADFSLogoutInitiator::doRequest(
ret.first = true;
if (session) {
+ time_t revocationExp = session->getExpiration();
sessionLocker.assign();
session = nullptr;
- application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
+ application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse, revocationExp);
}
}
- catch (MetadataException& mex) {
+ catch (const MetadataException& mex) {
// Less noise for IdPs that don't support logout
m_log.info("unable to issue ADFS logout request: %s", mex.what());
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error issuing ADFS logout request: %s", ex.what());
}
@@ -1070,7 +1073,7 @@ pair<bool,long> ADFSLogout::run(SPRequest& request, bool isHandler) const
try {
app.getServiceProvider().getSessionCache()->remove(app, request, &request);
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error removing session (%s): %s", session_id.c_str(), ex.what());
}
}
diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am
index 5a19269..f35f86b 100644
--- a/shibsp/Makefile.am
+++ b/shibsp/Makefile.am
@@ -75,7 +75,7 @@ handinclude_HEADERS = \
handler/LogoutHandler.h \
handler/LogoutInitiator.h \
handler/RemotedHandler.h \
- handler/SecuredHandler.h \
+ handler/SecuredHandler.h \
handler/SessionInitiator.h
liteinclude_HEADERS = \
@@ -105,8 +105,8 @@ utilinclude_HEADERS = \
noinst_HEADERS = \
internal.h \
- impl/StoredSession.h \
- impl/StorageServiceSessionCache.h \
+ impl/StoredSession.h \
+ impl/StorageServiceSessionCache.h \
remoting/impl/SocketListener.h
common_sources = \
@@ -114,7 +114,7 @@ common_sources = \
Application.cpp \
ServiceProvider.cpp \
SPConfig.cpp \
- version.cpp \
+ version.cpp \
attribute/Attribute.cpp \
attribute/BinaryAttribute.cpp \
attribute/ExtensibleAttribute.cpp \
@@ -124,15 +124,15 @@ common_sources = \
attribute/XMLAttribute.cpp \
binding/impl/XMLProtocolProvider.cpp \
handler/impl/AbstractHandler.cpp \
- handler/impl/AdminLogout.cpp \
+ handler/impl/AdminLogoutInitiator.cpp \
handler/impl/AssertionConsumerService.cpp \
handler/impl/AssertionLookup.cpp \
- handler/impl/AttributeCheckerHandler.cpp \
+ handler/impl/AttributeCheckerHandler.cpp \
handler/impl/ChainingLogoutInitiator.cpp \
handler/impl/ChainingSessionInitiator.cpp \
handler/impl/CookieSessionInitiator.cpp \
handler/impl/DiscoveryFeed.cpp \
- handler/impl/ExternalAuthHandler.cpp \
+ handler/impl/ExternalAuthHandler.cpp \
handler/impl/FormSessionInitiator.cpp \
handler/impl/LocalLogoutInitiator.cpp \
handler/impl/LogoutHandler.cpp \
@@ -147,7 +147,7 @@ common_sources = \
handler/impl/SAML2NameIDMgmt.cpp \
handler/impl/SAML2SessionInitiator.cpp \
handler/impl/SAMLDSSessionInitiator.cpp \
- handler/impl/SecuredHandler.cpp \
+ handler/impl/SecuredHandler.cpp \
handler/impl/SessionHandler.cpp \
handler/impl/SessionInitiator.cpp \
handler/impl/Shib1SessionInitiator.cpp \
@@ -155,8 +155,8 @@ common_sources = \
handler/impl/TransformSessionInitiator.cpp \
handler/impl/WAYFSessionInitiator.cpp \
impl/ChainingAccessControl.cpp \
- impl/StoredSession.cpp \
- impl/StorageServiceSessionCache.cpp \
+ impl/StoredSession.cpp \
+ impl/StorageServiceSessionCache.cpp \
impl/XMLAccessControl.cpp \
impl/XMLRequestMapper.cpp \
impl/XMLServiceProvider.cpp \
@@ -215,7 +215,7 @@ libshibsp_la_SOURCES = \
attribute/filtering/impl/AttributeIssuerEntityMatcherFunctor.cpp \
attribute/filtering/impl/AttributeRequesterEntityMatcherFunctor.cpp \
attribute/filtering/impl/AttributeMatchesShibMDScopeFunctor.cpp \
- attribute/filtering/impl/RegistrationAuthorityFunctor.cpp \
+ attribute/filtering/impl/RegistrationAuthorityFunctor.cpp \
attribute/resolver/impl/ChainingAttributeResolver.cpp \
attribute/resolver/impl/QueryAttributeResolver.cpp \
attribute/resolver/impl/SimpleAggregationAttributeResolver.cpp \
diff --git a/shibsp/SessionCache.h b/shibsp/SessionCache.h
index 4977dba..4ca7cb8 100644
--- a/shibsp/SessionCache.h
+++ b/shibsp/SessionCache.h
@@ -355,14 +355,18 @@ namespace shibsp {
/**
* Deletes an existing session bound to a request.
*
+ * <p>Revocation may be supported by some implementations.</p>
+ *
* @param application reference to Application that owns the Session
* @param request request from client containing session, or a reference to it
* @param response optional response to client enabling removal of session or reference
+ * @param revocationExp optional indicator for length of time to track revocation of this session
*/
virtual void remove(
const Application& application,
const xmltooling::HTTPRequest& request,
- xmltooling::HTTPResponse* response=nullptr
+ xmltooling::HTTPResponse* response=nullptr,
+ time_t revocationExp=0
)=0;
/**
@@ -377,10 +381,13 @@ namespace shibsp {
/**
* Deletes an existing session.
*
+ * <p>Revocation may be supported by some implementations.</p>
+ *
* @param application reference to Application that owns the Session
* @param key session key
+ * @param revocationExp optional indicator for length of time to track revocation of this session
*/
- virtual void remove(const Application& application, const char* key)=0;
+ virtual void remove(const Application& application, const char* key, time_t revocationExp=0)=0;
};
/** SessionCache implementation backed by a StorageService. */
diff --git a/shibsp/handler/impl/AdminLogoutInitiator.cpp b/shibsp/handler/impl/AdminLogoutInitiator.cpp
new file mode 100644
index 0000000..4b9da9b
--- /dev/null
+++ b/shibsp/handler/impl/AdminLogoutInitiator.cpp
@@ -0,0 +1,467 @@
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+
+/**
+ * AdminLogoutInitiator.cpp
+ *
+ * Triggers administrative logout of a session.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "Application.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "handler/SecuredHandler.h"
+#include "handler/LogoutInitiator.h"
+
+#ifndef SHIBSP_LITE
+# include "binding/SOAPClient.h"
+# include "metadata/MetadataProviderCriteria.h"
+# include "security/SecurityPolicy.h"
+# include <boost/algorithm/string.hpp>
+# include <boost/iterator/indirect_iterator.hpp>
+# include <saml/exceptions.h>
+# include <saml/SAMLConfig.h>
+# include <saml/saml2/core/Protocols.h>
+# include <saml/saml2/binding/SAML2SOAPClient.h>
+# include <saml/saml2/metadata/EndpointManager.h>
+# include <saml/saml2/metadata/Metadata.h>
+# include <saml/saml2/metadata/MetadataCredentialCriteria.h>
+using namespace opensaml::saml2;
+using namespace opensaml::saml2p;
+using namespace opensaml::saml2md;
+using namespace opensaml;
+#else
+# include "lite/SAMLConstants.h"
+#endif
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace boost;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+ #pragma warning( push )
+ #pragma warning( disable : 4250 )
+#endif
+
+ class SHIBSP_DLLLOCAL AdminLogoutInitiator : public SecuredHandler, public LogoutInitiator
+ {
+ public:
+ AdminLogoutInitiator(const DOMElement* e, const char* appId);
+ virtual ~AdminLogoutInitiator() {}
+
+ void init(const char* location); // encapsulates actions that need to run either in the c'tor or setParent
+
+ void setParent(const PropertySet* parent);
+ void receive(DDF& in, ostream& out);
+ pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+
+ private:
+ pair<bool,long> doRequest(const Application& application, const HTTPRequest& request, HTTPResponse& httpResponse) const;
+
+ string m_appId;
+#ifndef SHIBSP_LITE
+ auto_ptr_char m_protocol;
+ auto_ptr<LogoutRequest> buildRequest(
+ const Application& application,
+ const Session& session,
+ const RoleDescriptor& role,
+ const XMLCh* endpoint
+ ) const;
+#endif
+ };
+
+#if defined (_MSC_VER)
+ #pragma warning( pop )
+#endif
+
+ Handler* SHIBSP_DLLLOCAL AdminLogoutInitiatorFactory(const pair<const DOMElement*,const char*>& p)
+ {
+ return new AdminLogoutInitiator(p.first, p.second);
+ }
+};
+
+AdminLogoutInitiator::AdminLogoutInitiator(const DOMElement* e, const char* appId)
+ : SecuredHandler(e, Category::getInstance(SHIBSP_LOGCAT ".LogoutInitiator.Admin")), m_appId(appId)
+#ifndef SHIBSP_LITE
+ ,m_protocol(samlconstants::SAML20P_NS)
+#endif
+{
+ // If Location isn't set, defer initialization until the setParent call.
+ pair<bool,const char*> loc = getString("Location");
+ if (loc.first) {
+ init(loc.second);
+ }
+}
+
+void AdminLogoutInitiator::setParent(const PropertySet* parent)
+{
+ DOMPropertySet::setParent(parent);
+ pair<bool,const char*> loc = getString("Location");
+ init(loc.second);
+}
+
+void AdminLogoutInitiator::init(const char* location)
+{
+ if (location) {
+ string address = m_appId + location + "::run::AdminLI";
+ setAddress(address.c_str());
+ }
+ else {
+ m_log.warn("no Location property in Admin LogoutInitiator (or parent), can't register as remoted handler");
+ }
+}
+
+
+pair<bool,long> AdminLogoutInitiator::run(SPRequest& request, bool isHandler) const
+{
+ // No front-channel notifications, so skip calling base class.
+
+ if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+ // When out of process, we run natively.
+ return doRequest(request.getApplication(), request, request);
+ }
+ else {
+ // When not out of process, we remote the request.
+ vector<string> headers(1, "User-Agent");
+ DDF out, in = wrap(request, &headers);
+ DDFJanitor jin(in), jout(out);
+ out = send(request, in);
+ return unwrap(request, out);
+ }
+}
+
+void AdminLogoutInitiator::receive(DDF& in, ostream& out)
+{
+#ifndef SHIBSP_LITE
+ // Find application.
+ const char* aid=in["application_id"].string();
+ const Application* app=aid ? SPConfig::getConfig().getServiceProvider()->getApplication(aid) : nullptr;
+ if (!app) {
+ // Something's horribly wrong.
+ m_log.error("couldn't find application (%s) for logout", aid ? aid : "(missing)");
+ throw ConfigurationException("Unable to locate application for logout, deleted?");
+ }
+
+ // Unpack the request.
+ scoped_ptr<HTTPRequest> req(getRequest(in));
+
+ // Set up a response shim.
+ DDF ret(nullptr);
+ DDFJanitor jout(ret);
+ scoped_ptr<HTTPResponse> 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, *req, *resp);
+
+ out << ret;
+#else
+ throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
+#endif
+}
+
+pair<bool,long> AdminLogoutInitiator::doRequest(const Application& application, const HTTPRequest& httpRequest, HTTPResponse& httpResponse) const
+{
+ const char* sessionId = httpRequest.getParameter("session");
+ if (!sessionId || !*sessionId) {
+ // Something's horribly wrong.
+ m_log.error("no session parameter supplied for request");
+ istringstream msg("NO SESSION PARAMETER");
+ return make_pair(true, httpResponse.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_BADREQUEST));
+ }
+
+ Session* session = nullptr;
+ try {
+ session = application.getServiceProvider().getSessionCache()->find(application, sessionId);
+ }
+ catch (const std::exception& ex) {
+ m_log.error("error accessing designated session: %s", ex.what());
+ }
+
+ // With no session, we return a 404 after "revoking" the session just to be safe.
+ if (!session) {
+ application.getServiceProvider().getSessionCache()->remove(application, sessionId);
+ istringstream msg("NOT FOUND");
+ return make_pair(true, httpResponse.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTFOUND));
+ }
+
+ time_t revocationExp = session->getExpiration();
+
+ Locker sessionLocker(session, false);
+#ifndef SHIBSP_LITE
+ scoped_ptr<LogoutEvent> logout_event(newLogoutEvent(application, &httpRequest, session));
+#endif
+
+ bool doSAML = false;
+
+#ifndef SHIBSP_LITE
+ if (XMLString::equals(session->getProtocol(), m_protocol.get())) {
+ if (!session->getEntityID() || !session->getNameID()) {
+ m_log.info("skipping SAML 2.0 logout attempt, no NameID or issuing entityID found in session");
+ }
+ else {
+ doSAML = true;
+ }
+ }
+ else {
+ m_log.info("skipping global logout for non-SAML2 session");
+ }
+#endif
+
+ // Do back channel notification.
+ vector<string> sessions(1, session->getID());
+ if (!notifyBackChannel(application, httpRequest.getRequestURL(), sessions, true)) {
+#ifndef SHIBSP_LITE
+ if (logout_event) {
+ logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_PARTIAL;
+ application.getServiceProvider().getTransactionLog()->write(*logout_event);
+ }
+#endif
+ sessionLocker.assign();
+ session = nullptr;
+ application.getServiceProvider().getSessionCache()->remove(application, sessionId, revocationExp);
+
+ istringstream msg("PARTIAL");
+ return make_pair(true, httpResponse.sendResponse(msg, 206)); // misuse of an HTTP code, but whatever
+ }
+
+ if (!doSAML) {
+#ifndef SHIBSP_LITE
+ if (logout_event) {
+ logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_LOCAL;
+ application.getServiceProvider().getTransactionLog()->write(*logout_event);
+ }
+#endif
+ sessionLocker.assign();
+ session = nullptr;
+ application.getServiceProvider().getSessionCache()->remove(application, sessionId, revocationExp);
+
+ istringstream msg("OK");
+ return make_pair(true, httpResponse.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_OK));
+ }
+
+#ifndef SHIBSP_LITE
+ pair<bool,long> ret = make_pair(false, 0L);
+ try {
+ // With a session in hand, we can create a LogoutRequest message, if we can find a compatible SOAP endpoint.
+ MetadataProvider* m = application.getMetadataProvider();
+ Locker metadataLocker(m);
+ MetadataProviderCriteria mc(application, session->getEntityID(), &IDPSSODescriptor::ELEMENT_QNAME, samlconstants::SAML20P_NS);
+ pair<const EntityDescriptor*,const RoleDescriptor*> entity = m->getEntityDescriptor(mc);
+ 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 SAML 2.0 IdP role for identity provider ($entityID).", namedparams(1, "entityID", session->getEntityID())
+ );
+ }
+
+ const IDPSSODescriptor* role = dynamic_cast<const IDPSSODescriptor*>(entity.second);
+ if (role->getSingleLogoutServices().empty()) {
+ throw MetadataException(
+ "No SingleLogoutService endpoints in metadata for identity provider ($entityID).", namedparams(1, "entityID", session->getEntityID())
+ );
+ }
+
+ shibsp::SecurityPolicy policy(application);
+ shibsp::SOAPClient soaper(policy);
+ MetadataCredentialCriteria mcc(*role);
+
+ bool requestSent = false;
+ LogoutResponse* logoutResponse = nullptr;
+ scoped_ptr<StatusResponseType> srt;
+ auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);
+ const vector<SingleLogoutService*>& endpoints = role->getSingleLogoutServices();
+ for (indirect_iterator<vector<SingleLogoutService*>::const_iterator> epit = make_indirect_iterator(endpoints.begin());
+ !logoutResponse && epit != make_indirect_iterator(endpoints.end()); ++epit) {
+ try {
+ if (!XMLString::equals(epit->getBinding(), binding.get()))
+ continue;
+
+ requestSent = true;
+ auto_ptr<LogoutRequest> msg(buildRequest(application, *session, *role, epit->getLocation()));
+
+ // Log the request.
+ if (logout_event) {
+ logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_UNKNOWN;
+ logout_event->m_saml2Request = msg.get();
+ application.getServiceProvider().getTransactionLog()->write(*logout_event);
+ logout_event->m_saml2Request = nullptr;
+ }
+
+ SAML2SOAPClient client(soaper, false);
+ auto_ptr_char dest(epit->getLocation());
+ client.sendSAML(msg.release(), application.getId(), mcc, dest.get());
+ srt.reset(client.receiveSAML());
+ if (!(logoutResponse = dynamic_cast<LogoutResponse*>(srt.get()))) {
+ break;
+ }
+ }
+ catch (const std::exception& ex) {
+ m_log.error("error sending LogoutRequest message: %s", ex.what());
+ soaper.reset();
+ }
+ }
+
+ // No answer at all?
+ if (!logoutResponse) {
+ if (!requestSent)
+ m_log.info("IdP (%s) doesn't support SOAP-based single logout protocol", session->getEntityID());
+
+ // Log the end result.
+ if (logout_event) {
+ logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_PARTIAL;
+ application.getServiceProvider().getTransactionLog()->write(*logout_event);
+ }
+ }
+ else {
+ // Check the status, looking for non-success or a partial logout code.
+ const StatusCode* sc = logoutResponse->getStatus() ? logoutResponse->getStatus()->getStatusCode() : nullptr;
+ bool partial = (!sc || !XMLString::equals(sc->getValue(), StatusCode::SUCCESS));
+ if (!partial && sc->getStatusCode()) {
+ // Success, but still need to check for partial.
+ partial = XMLString::equals(sc->getStatusCode()->getValue(), StatusCode::PARTIAL_LOGOUT);
+ }
+
+ // Log the end result.
+ if (logout_event) {
+ logout_event->m_logoutType = partial ? LogoutEvent::LOGOUT_EVENT_PARTIAL : LogoutEvent::LOGOUT_EVENT_GLOBAL;
+ logout_event->m_saml2Response = logoutResponse;
+ application.getServiceProvider().getTransactionLog()->write(*logout_event);
+ }
+
+ if (!partial) {
+ sessionLocker.assign();
+ session = nullptr;
+ application.getServiceProvider().getSessionCache()->remove(application, sessionId, revocationExp);
+ istringstream msg("OK");
+ ret = make_pair(true, httpResponse.sendResponse(msg, HTTPResponse::XMLTOOLING_HTTP_STATUS_OK));
+ }
+ }
+ }
+ catch (const MetadataException& mex) {
+ // Less noise for IdPs that don't support logout (i.e. most)
+ m_log.info("unable to attempt SAML 2.0 logout: %s", mex.what());
+ if (logout_event) {
+ logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_PARTIAL;
+ application.getServiceProvider().getTransactionLog()->write(*logout_event);
+ }
+ }
+ catch (const std::exception& ex) {
+ m_log.error("error issuing SAML 2.0 logout request: %s", ex.what());
+ if (logout_event) {
+ logout_event->m_logoutType = LogoutEvent::LOGOUT_EVENT_PARTIAL;
+ application.getServiceProvider().getTransactionLog()->write(*logout_event);
+ }
+ }
+
+ if (session) {
+ sessionLocker.assign();
+ session = nullptr;
+ application.getServiceProvider().getSessionCache()->remove(application, sessionId, revocationExp);
+ }
+
+ if (ret.first)
+ return ret;
+
+ istringstream msg("PARTIAL");
+ return make_pair(true, httpResponse.sendResponse(msg, 206)); // misuse of an HTTP code, but whatever
+
+#else
+ throw ConfigurationException("Cannot perform SAML logout using lite version of shibsp library.");
+#endif
+}
+
+#ifndef SHIBSP_LITE
+
+auto_ptr<LogoutRequest> AdminLogoutInitiator::buildRequest(
+ const Application& application,
+ const Session& session,
+ const RoleDescriptor& role,
+ const XMLCh* endpoint) const
+{
+ const PropertySet* relyingParty = application.getRelyingParty(dynamic_cast<EntityDescriptor*>(role.getParent()));
+
+ auto_ptr<LogoutRequest> msg(LogoutRequestBuilder::buildLogoutRequest());
+ Issuer* issuer = IssuerBuilder::buildIssuer();
+ msg->setIssuer(issuer);
+ issuer->setName(relyingParty->getXMLString("entityID").second);
+ auto_ptr_XMLCh index(session.getSessionIndex());
+ if (index.get() && *index.get()) {
+ SessionIndex* si = SessionIndexBuilder::buildSessionIndex();
+ msg->getSessionIndexs().push_back(si);
+ si->setSessionIndex(index.get());
+ }
+
+ const NameID* nameid = session.getNameID();
+ pair<bool, const char*> flag = getString("encryption");
+ if (!flag.first)
+ flag = relyingParty->getString("encryption");
+ auto_ptr_char dest(endpoint);
+ if (SPConfig::shouldSignOrEncrypt(flag.first ? flag.second : "conditional", dest.get(), false)) {
+ try {
+ auto_ptr<EncryptedID> encrypted(EncryptedIDBuilder::buildEncryptedID());
+ MetadataCredentialCriteria mcc(role);
+ encrypted->encrypt(
+ *nameid,
+ *(application.getMetadataProvider()),
+ mcc,
+ false,
+ relyingParty->getXMLString("encryptionAlg").second
+ );
+ msg->setEncryptedID(encrypted.get());
+ encrypted.release();
+ }
+ catch (const std::exception& ex) {
+ // If we're encrypting deliberately, failure should be fatal.
+ if (flag.first && strcmp(flag.second, "conditional")) {
+ throw;
+ }
+ // If opportunistically, just log and move on.
+ m_log.info("Conditional encryption of NameID in LogoutRequest failed: %s", ex.what());
+ auto_ptr<NameID> namewrapper(nameid->cloneNameID());
+ msg->setNameID(namewrapper.get());
+ namewrapper.release();
+ }
+ }
+ else {
+ auto_ptr<NameID> namewrapper(nameid->cloneNameID());
+ msg->setNameID(namewrapper.get());
+ namewrapper.release();
+ }
+
+ XMLCh* msgid = SAMLConfig::getConfig().generateIdentifier();
+ msg->setID(msgid);
+ XMLString::release(&msgid);
+ msg->setIssueInstant(time(nullptr));
+
+ return msg;
+}
+
+#endif
diff --git a/shibsp/handler/impl/AttributeCheckerHandler.cpp b/shibsp/handler/impl/AttributeCheckerHandler.cpp
index fb61caa..085de02 100644
--- a/shibsp/handler/impl/AttributeCheckerHandler.cpp
+++ b/shibsp/handler/impl/AttributeCheckerHandler.cpp
@@ -76,11 +76,11 @@ namespace shibsp {
pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
private:
- void flushSession(SPRequest& request) const {
+ void flushSession(SPRequest& request, time_t exp) const {
try {
- request.getApplication().getServiceProvider().getSessionCache()->remove(request.getApplication(), request, &request);
+ request.getApplication().getServiceProvider().getSessionCache()->remove(request.getApplication(), request, &request, exp);
}
- catch (std::exception&) {
+ catch (const std::exception&) {
}
}
@@ -151,7 +151,7 @@ pair<bool,long> AttributeCheckerHandler::run(SPRequest& request, bool isHandler)
if (!session)
request.log(SPRequest::SPWarn, "AttributeChecker found session unavailable immediately after creation");
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
request.log(SPRequest::SPWarn, string("AttributeChecker caught exception accessing session immediately after creation: ") + ex.what());
}
@@ -191,16 +191,18 @@ pair<bool,long> AttributeCheckerHandler::run(SPRequest& request, bool isHandler)
tp.m_request = &request;
stringstream str;
XMLToolingConfig::getConfig().getTemplateEngine()->run(infile, str, tp);
- if (m_flushSession) {
+ if (m_flushSession && session) {
+ time_t revocationExp = session->getExpiration();
sessionLocker.assign(); // unlock the session
- flushSession(request);
+ flushSession(request, revocationExp);
}
return make_pair(true, request.sendError(str));
}
- if (m_flushSession) {
+ if (m_flushSession && session) {
+ time_t revocationExp = session->getExpiration();
sessionLocker.assign(); // unlock the session
- flushSession(request);
+ flushSession(request, revocationExp);
}
m_log.error("could not process error template (%s)", m_template.c_str());
istringstream msg("Internal Server Error. Please contact the site administrator.");
diff --git a/shibsp/handler/impl/LocalLogoutInitiator.cpp b/shibsp/handler/impl/LocalLogoutInitiator.cpp
index fe8146f..4c8b304 100644
--- a/shibsp/handler/impl/LocalLogoutInitiator.cpp
+++ b/shibsp/handler/impl/LocalLogoutInitiator.cpp
@@ -112,7 +112,7 @@ pair<bool,long> LocalLogoutInitiator::run(SPRequest& request, bool isHandler) co
try {
session = request.getSession(false, true, false); // don't cache it and ignore all checks
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error accessing current session: %s", ex.what());
}
return doRequest(request.getApplication(), request, request, session);
@@ -156,7 +156,7 @@ void LocalLogoutInitiator::receive(DDF& in, ostream& out)
try {
session = app->getServiceProvider().getSessionCache()->find(*app, *req, nullptr, nullptr);
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error accessing current session: %s", ex.what());
}
@@ -188,8 +188,9 @@ pair<bool,long> LocalLogoutInitiator::doRequest(
application.getServiceProvider().getTransactionLog()->write(*logout_event);
}
#endif
+ time_t revocationExp = session->getExpiration();
locker.assign(); // unlock the session
- application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
+ application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse, revocationExp);
if (!result)
return sendLogoutPage(application, httpRequest, httpResponse, "partial");
}
diff --git a/shibsp/handler/impl/SAML2Logout.cpp b/shibsp/handler/impl/SAML2Logout.cpp
index 5d649e5..fe763d3 100644
--- a/shibsp/handler/impl/SAML2Logout.cpp
+++ b/shibsp/handler/impl/SAML2Logout.cpp
@@ -190,7 +190,7 @@ SAML2Logout::SAML2Logout(const DOMElement* e, const char* appId)
m_log.warn("skipping outgoing binding (%s), not a SAML 2.0 front-channel mechanism", b->c_str());
}
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error building MessageEncoder: %s", ex.what());
}
}
@@ -286,7 +286,7 @@ pair<bool,long> SAML2Logout::doRequest(const Application& application, HTTPReque
cache->remove(application, request, &response);
worked2 = true;
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error removing session (%s): %s", session_id.c_str(), ex.what());
}
}
@@ -406,7 +406,7 @@ pair<bool,long> SAML2Logout::doRequest(const Application& application, HTTPReque
);
nameid = dynamic_cast<NameID*>(decryptedID.get());
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error(ex.what());
}
}
@@ -471,7 +471,7 @@ pair<bool,long> SAML2Logout::doRequest(const Application& application, HTTPReque
if (*sit != session_id)
cache->remove(application, sit->c_str()); // using the ID-based removal operation
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error while logging out matching sessions: %s", ex.what());
if (logout_event) {
logout_event->m_nameID = nameid;
@@ -562,7 +562,7 @@ pair<bool,long> SAML2Logout::doRequest(const Application& application, HTTPReque
try {
checkError(logoutResponse, policy->getIssuerMetadata()); // throws if Status doesn't look good...
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
if (logout_event) {
logout_event->m_exception = &ex;
application.getServiceProvider().getTransactionLog()->write(*logout_event);
diff --git a/shibsp/handler/impl/SAML2LogoutInitiator.cpp b/shibsp/handler/impl/SAML2LogoutInitiator.cpp
index 04fac01..c315f1f 100644
--- a/shibsp/handler/impl/SAML2LogoutInitiator.cpp
+++ b/shibsp/handler/impl/SAML2LogoutInitiator.cpp
@@ -180,7 +180,7 @@ void SAML2LogoutInitiator::init(const char* location)
m_log.warn("skipping outgoing binding (%s), not a SAML 2.0 front-channel mechanism", b->c_str());
}
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error building MessageEncoder: %s", ex.what());
}
}
@@ -211,7 +211,7 @@ pair<bool,long> SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) co
return make_pair(false, 0L);
}
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error accessing current session: %s", ex.what());
return make_pair(false, 0L);
}
@@ -272,9 +272,10 @@ void SAML2LogoutInitiator::receive(DDF& in, ostream& out)
doRequest(*app, *req, *resp, session);
}
else {
+ time_t revocationExp = session->getExpiration();
session->unlock();
m_log.log(getParent() ? Priority::WARN : Priority::ERROR, "bypassing SAML 2.0 logout, no NameID or issuing entityID found in session");
- app->getServiceProvider().getSessionCache()->remove(*app, *req, resp.get());
+ app->getServiceProvider().getSessionCache()->remove(*app, *req, resp.get(), revocationExp);
}
}
out << ret;
@@ -301,9 +302,10 @@ pair<bool,long> SAML2LogoutInitiator::doRequest(
application.getServiceProvider().getTransactionLog()->write(*logout_event);
}
#endif
+ time_t revocationExp = session->getExpiration();
sessionLocker.assign();
session = nullptr;
- application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
+ application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse, revocationExp);
return sendLogoutPage(application, httpRequest, httpResponse, "partial");
}
@@ -378,7 +380,7 @@ pair<bool,long> SAML2LogoutInitiator::doRequest(
break;
}
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error sending LogoutRequest message: %s", ex.what());
soaper.reset();
}
@@ -439,9 +441,10 @@ pair<bool,long> SAML2LogoutInitiator::doRequest(
}
if (session) {
+ time_t revocationExp = session->getExpiration();
sessionLocker.assign();
session = nullptr;
- application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
+ application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse, revocationExp);
}
return ret;
@@ -474,16 +477,17 @@ pair<bool,long> SAML2LogoutInitiator::doRequest(
msg.release(); // freed by encoder
if (session) {
+ time_t revocationExp = session->getExpiration();
sessionLocker.assign();
session = nullptr;
- application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse);
+ application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse, revocationExp);
}
}
- catch (MetadataException& mex) {
+ catch (const MetadataException& mex) {
// Less noise for IdPs that don't support logout (i.e. most)
m_log.info("unable to issue SAML 2.0 logout request: %s", mex.what());
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error issuing SAML 2.0 logout request: %s", ex.what());
}
diff --git a/shibsp/impl/StorageServiceSessionCache.cpp b/shibsp/impl/StorageServiceSessionCache.cpp
index bda1f1b..3d926d8 100644
--- a/shibsp/impl/StorageServiceSessionCache.cpp
+++ b/shibsp/impl/StorageServiceSessionCache.cpp
@@ -94,11 +94,12 @@ SessionCache::~SessionCache()
}
SSCache::SSCache(const DOMElement* e)
- : m_log(Category::getInstance(SHIBSP_LOGCAT ".SessionCache")), inproc(true),
+ :
#ifndef SHIBSP_LITE
- m_storage(nullptr), m_storage_lite(nullptr), m_cacheAssertions(true), m_reverseIndex(true),
+ m_storage(nullptr), m_storage_lite(nullptr), m_cacheAssertions(true), m_reverseIndex(true), m_softRevocation(true), m_reverseIndexMaxSize(0),
#endif
- m_root(e), m_inprocTimeout(900), m_cacheTimeout(0), m_cacheAllowance(0), shutdown(false)
+ m_root(e), m_inprocTimeout(900), m_cacheTimeout(0), m_cacheAllowance(0),
+ m_log(Category::getInstance(SHIBSP_LOGCAT ".SessionCache")), inproc(true), shutdown(false)
{
SPConfig& conf = SPConfig::getConfig();
inproc = conf.isEnabled(SPConfig::InProcess);
@@ -111,6 +112,8 @@ SSCache::SSCache(const DOMElement* e)
static const XMLCh inprocTimeout[] = UNICODE_LITERAL_13(i,n,p,r,o,c,T,i,m,e,o,u,t);
static const XMLCh inboundHeader[] = UNICODE_LITERAL_13(i,n,b,o,u,n,d,H,e,a,d,e,r);
static const XMLCh maintainReverseIndex[] = UNICODE_LITERAL_20(m,a,i,n,t,a,i,n,R,e,v,e,r,s,e,I,n,d,e,x);
+ static const XMLCh reverseIndexMaxSize[] = UNICODE_LITERAL_19(r,e,v,e,r,s,e,I,n,d,e,x,M,a,x,S,i,z,e);
+ static const XMLCh softRevocation[] = UNICODE_LITERAL_14(s,o,f,t,R,e,v,o,c,a,t,i,o,n);
static const XMLCh outboundHeader[] = UNICODE_LITERAL_14(o,u,t,b,o,u,n,d,H,e,a,d,e,r);
static const XMLCh _StorageService[] = UNICODE_LITERAL_14(S,t,o,r,a,g,e,S,e,r,v,i,c,e);
static const XMLCh _StorageServiceLite[] = UNICODE_LITERAL_18(S,t,o,r,a,g,e,S,e,r,v,i,c,e,L,i,t,e);
@@ -158,8 +161,10 @@ SSCache::SSCache(const DOMElement* e)
m_storage_lite = m_storage;
}
+ m_softRevocation = XMLHelper::getAttrBool(e, true, softRevocation);
m_cacheAssertions = XMLHelper::getAttrBool(e, true, cacheAssertions);
m_reverseIndex = XMLHelper::getAttrBool(e, true, maintainReverseIndex);
+ m_reverseIndexMaxSize = XMLHelper::getAttrInt(e, 0, reverseIndexMaxSize);
const XMLCh* excludedNames = e ? e->getAttributeNS(nullptr, excludeReverseIndex) : nullptr;
if (excludedNames && *excludedNames) {
XMLStringTokenizer toks(excludedNames);
@@ -324,10 +329,11 @@ void SSCache::insert(const char* key, time_t expires, const char* name, const ch
if (!index || !*index)
index = "_shibnull";
DDF sessions = obj.addmember(index);
- if (!sessions.islist())
- sessions.list();
- DDF session = DDF(nullptr).string(key);
- sessions.add(session);
+ if (!sessions.isstruct())
+ sessions.structure();
+ else if (sessions.integer() == m_reverseIndexMaxSize)
+ sessions.first().destroy();
+ sessions.addmember(key);
// Remarshall the record.
ostringstream out;
@@ -492,7 +498,7 @@ void SSCache::insert(
try {
insert(key.get(), expires, name.get(), index.get());
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error storing back mapping of NameID for logout: %s", ex.what());
}
}
@@ -509,7 +515,7 @@ void SSCache::insert(
throw IOException("Duplicate assertion ID ($1)", params(1, tokenid.get()));
}
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error storing assertion along with session: %s", ex.what());
}
}
@@ -603,7 +609,7 @@ void SSCache::persist(const Application& app, HTTPResponse& httpResponse, DDF& s
}
httpResponse.setCookie(shib_cookie.first.c_str(), sealed.c_str());
}
- catch (std::exception& e) {
+ catch (const std::exception& e) {
m_log.error("failed to wrap session (%s) with DataSealer: %s", session.name(), e.what());
}
}
@@ -627,7 +633,7 @@ bool SSCache::matches(
}
}
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error while matching session: %s", ex.what());
}
return false;
@@ -733,20 +739,20 @@ vector<string>::size_type SSCache::_logout(
istringstream in(record);
in >> obj;
- // The record contains child lists for each known session index.
+ // The record contains child structs for each known session index.
DDF key;
DDF sessions = obj.first();
- while (sessions.islist()) {
+ while (sessions.isstruct()) {
if (!indexes || indexes->empty() || indexes->count(sessions.name())) {
key = sessions.first();
- while (key.isstring()) {
+ while (!key.isnull()) {
// Fetch the session for comparison.
Session* session = nullptr;
try {
- session = find(app, key.string());
+ session = find(app, key.name());
}
- catch (std::exception& ex) {
- m_log.error("error locating session (%s): %s", key.string(), ex.what());
+ catch (const std::exception& ex) {
+ m_log.error("error locating session (%s): %s", key.name(), ex.what());
}
if (session) {
@@ -755,15 +761,15 @@ vector<string>::size_type SSCache::_logout(
if (XMLString::equals(session->getEntityID(), entityID.get())) {
// Same NameID?
if (stronglyMatches(issuer->getEntityID(), app.getRelyingParty(issuer)->getXMLString("entityID").second, nameid, *session->getNameID())) {
- sessionsKilled.push_back(key.string());
+ sessionsKilled.push_back(key.name());
key.destroy();
}
else {
- m_log.debug("session (%s) contained a non-matching NameID, leaving it alone", key.string());
+ m_log.debug("session (%s) contained a non-matching NameID, leaving it alone", key.name());
}
}
else {
- m_log.debug("session (%s) established by different IdP, leaving it alone", key.string());
+ m_log.debug("session (%s) established by different IdP, leaving it alone", key.name());
}
}
else {
@@ -771,7 +777,7 @@ vector<string>::size_type SSCache::_logout(
// To be conservative, we'll leave it alone. This isn't really increasing our security
// risk, because if we can't lookup the session, it's unlikely the calling logout code
// can either, so there's no chance of removing the session anyway.
- m_log.warn("session (%s) not accessible for logout, may be gone, or associated with a different application", key.string());
+ m_log.warn("session (%s) not accessible for logout, may be gone, or associated with a different application", key.name());
}
key = sessions.next();
}
@@ -798,7 +804,7 @@ vector<string>::size_type SSCache::_logout(
m_log.warn("logout mapping record changed behind us, leaving it alone");
}
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error updating logout mapping record: %s", ex.what());
}
@@ -856,7 +862,7 @@ LogoutEvent* SSCache::newLogoutEvent(const Application& app) const
m_log.warn("unable to audit event, log event object was of an incorrect type");
}
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.warn("exception auditing event: %s", ex.what());
}
return nullptr;
@@ -990,7 +996,7 @@ Session* SSCache::_find(const Application& app, const char* key, const char* rec
try {
m_storage->updateContext(key, now + cacheTimeout);
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("failed to update session expiration: %s", ex.what());
}
}
@@ -1078,7 +1084,7 @@ Session* SSCache::find(const Application& app, HTTPRequest& request, const char*
response->setCookie(shib_cookie.first.c_str(), exp.c_str());
}
}
- catch (std::exception&) {
+ catch (const std::exception&) {
HTTPResponse* response = dynamic_cast<HTTPResponse*>(&request);
if (response) {
if (!m_outboundHeader.empty())
@@ -1124,6 +1130,22 @@ bool SSCache::recover(const Application& app, const char* key, const char* data)
else {
// We're out of process, so we can recover the session.
#ifndef SHIBSP_LITE
+ m_log.debug("checking for revocation of session (%s)", key);
+ try {
+ if (m_storage_lite->readString("Revoked", key) > 0) {
+ m_log.warn("blocked recovery of revoked session (%s)", key);
+ return false;
+ }
+ }
+ catch (const std::exception& ex) {
+ if (m_softRevocation)
+ m_log.warn("ignoring failed check for revocation of session (%s): %s", ex.what());
+ else {
+ m_log.warn("check for revocation of session (%s) failed, treating as revoked: %s", ex.what());
+ return false;
+ }
+ }
+
m_log.debug("attempting recovery of session (%s)", key);
DDF obj;
@@ -1140,7 +1162,7 @@ bool SSCache::recover(const Application& app, const char* key, const char* data)
stringstream str(unwrapped);
str >> obj;
}
- catch (std::exception& e) {
+ catch (const std::exception& e) {
if (dup)
free(dup);
m_log.error("failed to unwrap sealed session data with DataSealer: %s", e.what());
@@ -1183,7 +1205,7 @@ bool SSCache::recover(const Application& app, const char* key, const char* data)
insert(key, iso.getEpoch(), name.get(), obj["session_index"].string());
}
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("error storing back mapping of NameID for logout: %s", ex.what());
}
}
@@ -1200,7 +1222,7 @@ bool SSCache::recover(const Application& app, const char* key, const char* data)
return true;
}
-void SSCache::remove(const Application& app, const HTTPRequest& request, HTTPResponse* response)
+void SSCache::remove(const Application& app, const HTTPRequest& request, HTTPResponse* response, time_t revocationExp)
{
#ifdef _DEBUG
xmltooling::NDC ndc("remove");
@@ -1227,11 +1249,11 @@ void SSCache::remove(const Application& app, const HTTPRequest& request, HTTPRes
shib_cookie = app.getCookieNameProps("_shibsealed_");
response->setCookie(shib_cookie.first.c_str(), exp.c_str());
}
- remove(app, session_id.c_str());
+ remove(app, session_id.c_str(), revocationExp);
}
}
-void SSCache::remove(const Application& app, const char* key)
+void SSCache::remove(const Application& app, const char* key, time_t revocationExp)
{
#ifdef _DEBUG
xmltooling::NDC ndc("remove");
@@ -1245,6 +1267,24 @@ void SSCache::remove(const Application& app, const char* key)
#ifndef SHIBSP_LITE
m_storage->deleteContext(key);
m_log.info("removed session (%s)", key);
+
+ if (!m_persistedAttributeIds.empty()) {
+ if (!revocationExp) {
+ const PropertySet* props = app.getPropertySet("Sessions");
+ if (props)
+ revocationExp = props->getUnsignedInt("lifetime").second;
+ if (!revocationExp)
+ revocationExp = 28800;
+ revocationExp += time(nullptr);
+ }
+ try {
+ if (!m_storage_lite->createString("Revoked", key, "1", revocationExp))
+ m_log.warn("duplicate insertion of revocation for session (%s)", key);
+ }
+ catch (const std::exception& ex) {
+ m_log.warn("error recording revocation of session (%s): %s", key, ex.what());
+ }
+ }
#else
throw ConfigurationException("SessionCache removal requires a StorageService.");
#endif
@@ -1445,7 +1485,7 @@ void SSCache::receive(DDF& in, ostream& out)
try {
m_storage->updateContext(key, now + cacheTimeout);
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("failed to update session expiration: %s", ex.what());
}
}
@@ -1496,7 +1536,7 @@ void SSCache::receive(DDF& in, ostream& out)
try {
m_storage->updateContext(key, now + cacheTimeout);
}
- catch (std::exception& ex) {
+ catch (const std::exception& ex) {
m_log.error("failed to update session expiration: %s", ex.what());
}
@@ -1573,8 +1613,15 @@ void SSCache::receive(DDF& in, ostream& out)
const char* key=in["key"].string();
if (!key)
throw ListenerException("Required parameter missing for session removal.");
+ time_t revocationExp = 0;
+ auto_ptr_XMLCh dt(in["revocationExp"].string());
+ if (dt.get()) {
+ XMLDateTime dtobj(dt.get());
+ dtobj.parseDateTime();
+ revocationExp = dtobj.getEpoch();
+ }
- remove(*app, key);
+ remove(*app, key, revocationExp);
DDF ret(nullptr);
DDFJanitor jan(ret);
out << ret;
diff --git a/shibsp/impl/StorageServiceSessionCache.h b/shibsp/impl/StorageServiceSessionCache.h
index d2411ba..5e18062 100644
--- a/shibsp/impl/StorageServiceSessionCache.h
+++ b/shibsp/impl/StorageServiceSessionCache.h
@@ -104,12 +104,18 @@ namespace shibsp {
#endif
std::string active(const Application& app, const xmltooling::HTTPRequest& request);
Session* find(const Application& app, xmltooling::HTTPRequest& request, const char* client_addr=nullptr, time_t* timeout=nullptr);
- void remove(const Application& app, const xmltooling::HTTPRequest& request, xmltooling::HTTPResponse* response=nullptr);
+
+ void remove(
+ const Application& app,
+ const xmltooling::HTTPRequest& request,
+ xmltooling::HTTPResponse* response=nullptr,
+ time_t revocationExp=0
+ );
Session* find(const Application& app, const char* key) {
return _find(app, key, nullptr, nullptr, nullptr);
}
- void remove(const Application& app, const char* key);
+ void remove(const Application& app, const char* key, time_t revocationExp=0);
void test();
unsigned long getCacheTimeout(const Application& app) const;
@@ -138,7 +144,10 @@ namespace shibsp {
bool stronglyMatches(const XMLCh* idp, const XMLCh* sp, const opensaml::saml2::NameID& n1, const opensaml::saml2::NameID& n2) const;
LogoutEvent* newLogoutEvent(const Application& app) const;
- bool m_cacheAssertions,m_reverseIndex;
+ xmltooling::StorageService* m_storage;
+ xmltooling::StorageService* m_storage_lite;
+ bool m_cacheAssertions,m_reverseIndex,m_softRevocation;
+ unsigned long m_reverseIndexMaxSize;
std::set<xmltooling::xstring> m_excludedNames;
std::set<std::string> m_persistedAttributeIds;
#endif
@@ -162,11 +171,6 @@ namespace shibsp {
xmltooling::logging::Category& m_log;
bool inproc;
-#ifndef SHIBSP_LITE
- xmltooling::StorageService* m_storage;
- xmltooling::StorageService* m_storage_lite;
-#endif
-
bool shutdown;
boost::scoped_ptr<xmltooling::CondWait> shutdown_wait;
boost::scoped_ptr<xmltooling::Thread> cleanup_thread;
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list