[cpp-sp] branch main updated: Initial sketch of admin logout handler.
Codeberg
noreply at shibboleth.net
Thu Oct 8 19:47:32 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository cpp-sp.
View the commit online:
https://codeberg.org/Shibboleth/cpp-sp/commit/f08c41751020237cfce583bb14833e84a81d8d2c
The following commit(s) were added to refs/heads/main by this push:
new f08c4175 Initial sketch of admin logout handler.
f08c4175 is described below
commit f08c41751020237cfce583bb14833e84a81d8d2c
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Thu Oct 8 15:47:14 2026 -0400
Initial sketch of admin logout handler.
---
Projects/vc22/shibsp.vcxproj | 1 +
Projects/vc22/shibsp.vcxproj.filters | 21 +--
configs/handlers.ini | 7 +
shibsp/Makefile.am | 1 +
shibsp/handler/Handler.h | 2 +-
shibsp/handler/impl/AbstractHandler.cpp | 2 +
shibsp/handler/impl/AdminLogoutHandler.cpp | 112 +++++++++++++
shibsp/handler/impl/AdminLogoutInitiator.cpp | 228 ---------------------------
shibsp/session/impl/CookieSessionCache.cpp | 4 +-
9 files changed, 138 insertions(+), 240 deletions(-)
diff --git a/Projects/vc22/shibsp.vcxproj b/Projects/vc22/shibsp.vcxproj
index cc02517d..93278457 100644
--- a/Projects/vc22/shibsp.vcxproj
+++ b/Projects/vc22/shibsp.vcxproj
@@ -93,6 +93,7 @@
</ClCompile>
<ClCompile Include="..\..\shibsp\handler\impl\AttributeCheckerHandler.cpp" />
<ClCompile Include="..\..\shibsp\handler\impl\DefaultHandlerConfiguration.cpp" />
+ <ClCompile Include="..\..\shibsp\handler\impl\AdminLogoutHandler.cpp" />
<ClCompile Include="..\..\shibsp\handler\impl\LogoutConsumer.cpp" />
<ClCompile Include="..\..\shibsp\handler\impl\LogoutHandler.cpp" />
<ClCompile Include="..\..\shibsp\handler\impl\LogoutInitiator.cpp" />
diff --git a/Projects/vc22/shibsp.vcxproj.filters b/Projects/vc22/shibsp.vcxproj.filters
index ccd33cce..c2fe848e 100644
--- a/Projects/vc22/shibsp.vcxproj.filters
+++ b/Projects/vc22/shibsp.vcxproj.filters
@@ -230,6 +230,12 @@
</ClInclude>
</ItemGroup>
<ItemGroup>
+ <ClCompile Include="..\..\shibsp\handler\impl\DefaultHandlerConfiguration.cpp">
+ <Filter>Source Files\Handler</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\shibsp\handler\impl\AdminLogoutHandler.cpp">
+ <Filter>Source Files\Handler</Filter>
+ </ClCompile>
<ClCompile Include="..\..\shibsp\handler\impl\AbstractHandler.cpp">
<Filter>Source Files\Handler</Filter>
</ClCompile>
@@ -242,6 +248,9 @@
<ClCompile Include="..\..\shibsp\handler\impl\LogoutInitiator.cpp">
<Filter>Source Files\Handler</Filter>
</ClCompile>
+ <ClCompile Include="..\..\shibsp\handler\impl\LogoutConsumer.cpp">
+ <Filter>Source Files\Handler</Filter>
+ </ClCompile>
<ClCompile Include="..\..\shibsp\handler\impl\Passthrough.cpp">
<Filter>Source Files\Handler</Filter>
</ClCompile>
@@ -257,6 +266,9 @@
<ClCompile Include="..\..\shibsp\handler\impl\StatusHandler.cpp">
<Filter>Source Files\Handler</Filter>
</ClCompile>
+ <ClCompile Include="..\..\shibsp\handler\impl\TokenConsumer.cpp">
+ <Filter>Source Files\Handler</Filter>
+ </ClCompile>
<ClCompile Include="..\..\shibsp\impl\AgentConfig.cpp">
<Filter>Source Files\Impl</Filter>
</ClCompile>
@@ -356,9 +368,6 @@
<ClCompile Include="..\..\shibsp\logging\impl\WindowsLoggingService.cpp">
<Filter>Source Files\Logging</Filter>
</ClCompile>
- <ClCompile Include="..\..\shibsp\handler\impl\DefaultHandlerConfiguration.cpp">
- <Filter>Source Files\Handler</Filter>
- </ClCompile>
<ClCompile Include="..\..\shibsp\session\impl\FilesystemSessionCache.cpp">
<Filter>Source Files\Session</Filter>
</ClCompile>
@@ -377,18 +386,12 @@
<ClCompile Include="..\..\shibsp\remoting\impl\CurlHTTPRemotingService.cpp">
<Filter>Source Files\Remoting</Filter>
</ClCompile>
- <ClCompile Include="..\..\shibsp\handler\impl\TokenConsumer.cpp">
- <Filter>Source Files\Handler</Filter>
- </ClCompile>
<ClCompile Include="..\..\shibsp\remoting\impl\WinHTTPRemotingService.cpp">
<Filter>Source Files\Remoting</Filter>
</ClCompile>
<ClCompile Include="..\..\shibsp\attribute\impl\DefaultAttributeConfiguration.cpp">
<Filter>Source Files\Attribute</Filter>
</ClCompile>
- <ClCompile Include="..\..\shibsp\handler\impl\LogoutConsumer.cpp">
- <Filter>Source Files\Handler</Filter>
- </ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\shibsp\shibsp.rc">
diff --git a/configs/handlers.ini b/configs/handlers.ini
index 02eb1709..4890d6e3 100644
--- a/configs/handlers.ini
+++ b/configs/handlers.ini
@@ -22,6 +22,13 @@ type = LogoutConsumer
# true requires valid logout request matching active session
#matchRequired = false
+[Logout/Admin]
+type = AdminLogout
+acl = 127.0.0.1 ::1
+# true suppresses upstream signaling via Hub
+#localOnly = true
+
+
# Legacy SSO/SLO endpoints for SP3 compatibility
#[SAML2/POST]
diff --git a/shibsp/Makefile.am b/shibsp/Makefile.am
index 434519fe..63bcfcb4 100644
--- a/shibsp/Makefile.am
+++ b/shibsp/Makefile.am
@@ -100,6 +100,7 @@ libshibsp_la_SOURCES = \
version.cpp \
attribute/impl/DefaultAttributeConfiguration.cpp \
handler/impl/AbstractHandler.cpp \
+ handler/impl/AdminLogoutHandler.cpp \
handler/impl/AttributeCheckerHandler.cpp \
handler/impl/DefaultHandlerConfiguration.cpp \
handler/impl/LogoutConsumer.cpp \
diff --git a/shibsp/handler/Handler.h b/shibsp/handler/Handler.h
index f59eba61..e3a8af26 100644
--- a/shibsp/handler/Handler.h
+++ b/shibsp/handler/Handler.h
@@ -77,7 +77,7 @@ namespace shibsp {
/** Handler for logout. */
#define LOGOUT_CONSUMER_HANDLER "LogoutConsumer"
- /** LogoutInitiator that supports administrative logout. */
+ /** Handler that supports administrative logout. */
#define ADMIN_LOGOUT_HANDLER "AdminLogout"
/** Handler for hooking new sessions with attribute checking. */
diff --git a/shibsp/handler/impl/AbstractHandler.cpp b/shibsp/handler/impl/AbstractHandler.cpp
index 59bafd8b..eef09d98 100644
--- a/shibsp/handler/impl/AbstractHandler.cpp
+++ b/shibsp/handler/impl/AbstractHandler.cpp
@@ -57,6 +57,7 @@ namespace shibsp {
extern SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<ptree&,const char*> >::Factory TokenConsumerFactory;
extern SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<ptree&,const char*> >::Factory LogoutInitiatorFactory;
extern SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<ptree&,const char*> >::Factory LogoutConsumerFactory;
+ extern SHIBSP_DLLLOCAL PluginManager< Handler,string,pair<ptree&,const char*> >::Factory AdminLogoutHandlerFactory;
};
void SHIBSP_API shibsp::registerHandlers()
@@ -73,6 +74,7 @@ void SHIBSP_API shibsp::registerHandlers()
conf.HandlerManager.registerFactory(TOKEN_CONSUMER_HANDLER, TokenConsumerFactory);
conf.HandlerManager.registerFactory(LOGOUT_INITIATOR_HANDLER, LogoutInitiatorFactory);
conf.HandlerManager.registerFactory(LOGOUT_CONSUMER_HANDLER, LogoutConsumerFactory);
+ conf.HandlerManager.registerFactory(ADMIN_LOGOUT_HANDLER, AdminLogoutHandlerFactory);
}
Handler::Handler()
diff --git a/shibsp/handler/impl/AdminLogoutHandler.cpp b/shibsp/handler/impl/AdminLogoutHandler.cpp
new file mode 100644
index 00000000..5a21ee24
--- /dev/null
+++ b/shibsp/handler/impl/AdminLogoutHandler.cpp
@@ -0,0 +1,112 @@
+/**
+ * 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
+ *
+ * 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.
+ */
+
+/**
+ * handler/impl/AdminLogoutHandler.cpp
+ *
+ * Handles administrative logout of a session.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "Agent.h"
+#include "AgentConfig.h"
+#include "handler/SecuredHandler.h"
+#include "logging/Category.h"
+#include "session/SessionCache.h"
+
+#include <sstream>
+#ifdef HAVE_CXX14
+# include <shared_mutex>
+#endif
+
+using namespace shibsp;
+using namespace boost::property_tree;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+ #pragma warning( push )
+ #pragma warning( disable : 4250 )
+#endif
+
+ class SHIBSP_DLLLOCAL AdminLogoutHandler : public SecuredHandler
+ {
+ public:
+ AdminLogoutHandler(const ptree& pt);
+ virtual ~AdminLogoutHandler() {}
+
+ pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+ };
+
+#if defined (_MSC_VER)
+ #pragma warning( pop )
+#endif
+
+ Handler* SHIBSP_DLLLOCAL AdminLogoutHandlerFactory(const pair<ptree&,const char*> p, bool)
+ {
+ return new AdminLogoutHandler(p.first);
+ }
+};
+
+AdminLogoutHandler::AdminLogoutHandler(const ptree& pt) : AbstractHandler(pt), SecuredHandler(pt)
+{
+}
+
+pair<bool,long> AdminLogoutHandler::run(SPRequest& request, bool isHandler) const
+{
+ // No front-channel notifications, so skip calling logout base class.
+
+ // Check ACL in base class.
+ pair<bool,long> ret = SecuredHandler::run(request, isHandler);
+ if (ret.first)
+ return ret;
+
+ const char* sessionId = request.getParameter("session");
+ if (!sessionId || !*sessionId) {
+ request.error("no session parameter supplied for request");
+ istringstream msg("NO SESSION PARAMETER");
+ return make_pair(true, request.sendResponse(msg, HTTPResponse::SHIBSP_HTTP_STATUS_BADREQUEST));
+ }
+
+ const char* applicationId = request.getRequestSettings().first->getString("applicationId", "default");
+
+ unique_lock<Session> session;
+ try {
+ session = AgentConfig::getConfig().getAgent().getSessionCache()->find(applicationId, sessionId);
+ }
+ catch (const std::exception& ex) {
+ request.error("error locating session: %s", ex.what());
+ }
+
+ // With no session, we return a 404 after "revoking" the session just to be safe.
+ if (!session) {
+ AgentConfig::getConfig().getAgent().getSessionCache()->remove(sessionId);
+ istringstream msg("NOT FOUND");
+ return make_pair(true, request.sendResponse(msg, HTTPResponse::SHIBSP_HTTP_STATUS_NOTFOUND));
+ }
+
+ bool localOnly = getBool("localOnly", true);
+
+ if (localOnly) {
+ session.unlock();
+ AgentConfig::getConfig().getAgent().getSessionCache()->remove(sessionId);
+
+ istringstream msg("OK");
+ return make_pair(true, request.sendResponse(msg, HTTPResponse::SHIBSP_HTTP_STATUS_OK));
+ }
+
+ return make_pair(false, 0L);
+}
diff --git a/shibsp/handler/impl/AdminLogoutInitiator.cpp b/shibsp/handler/impl/AdminLogoutInitiator.cpp
deleted file mode 100644
index feea7053..00000000
--- a/shibsp/handler/impl/AdminLogoutInitiator.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-/**
- * 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
- *
- * 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.
- */
-
-/**
- * handler/impl/AdminLogoutInitiator.cpp
- *
- * Triggers administrative logout of a session.
- */
-
-#include "internal.h"
-#include "exceptions.h"
-#include "Agent.h"
-#include "AgentConfig.h"
-#include "handler/SecuredHandler.h"
-#include "handler/LogoutInitiator.h"
-#include "logging/Category.h"
-#include "session/SessionCache.h"
-
-#include <sstream>
-#ifdef HAVE_CXX14
-# include <shared_mutex>
-#endif
-
-using namespace shibsp;
-using namespace boost::property_tree;
-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 ptree& pt);
- virtual ~AdminLogoutInitiator() {}
-
- pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
- };
-
-#if defined (_MSC_VER)
- #pragma warning( pop )
-#endif
-
- Handler* SHIBSP_DLLLOCAL AdminLogoutInitiatorFactory(const pair<ptree&,const char*> p, bool)
- {
- return new AdminLogoutInitiator(p.first);
- }
-};
-
-AdminLogoutInitiator::AdminLogoutInitiator(const ptree& pt) : SecuredHandler(pt)
-{
-}
-
-pair<bool,long> AdminLogoutInitiator::run(SPRequest& request, bool isHandler) const
-{
- // No front-channel notifications, so skip calling logout base class.
-
- // Check ACL in base class.
- pair<bool,long> ret = SecuredHandler::run(request, isHandler);
- if (ret.first)
- return ret;
-
- const char* sessionId = request.getParameter("session");
- if (!sessionId || !*sessionId) {
- // Something's horribly wrong.
- request.error("no session parameter supplied for request");
- istringstream msg("NO SESSION PARAMETER");
- return make_pair(true, request.sendResponse(msg, HTTPResponse::SHIBSP_HTTP_STATUS_BADREQUEST));
- }
-
- const char* applicationId = request.getRequestSettings().first->getString("applicationId", "default");
-
- unique_lock<Session> session;
- try {
- session = AgentConfig::getConfig().getAgent().getSessionCache()->find(applicationId, sessionId);
- }
- catch (const std::exception& ex) {
- request.error(string("error accessing designated session: ") + ex.what());
- }
-
- // With no session, we return a 404 after "revoking" the session just to be safe.
- if (!session) {
- AgentConfig::getConfig().getAgent().getSessionCache()->remove(sessionId);
- istringstream msg("NOT FOUND");
- return make_pair(true, request.sendResponse(msg, HTTPResponse::SHIBSP_HTTP_STATUS_NOTFOUND));
- }
-
- bool doSAML = false;
-
- // Do back channel notification.
- vector<string> sessions(1, session.mutex()->getID());
- if (!notifyBackChannel(request, sessions, true)) {
- session.unlock();
- AgentConfig::getConfig().getAgent().getSessionCache()->remove(sessionId);
-
- istringstream msg("PARTIAL");
- return make_pair(true, request.sendResponse(msg, 206)); // misuse of an HTTP code, but whatever
- }
-
- if (!doSAML) {
- session.unlock();
- AgentConfig::getConfig().getAgent().getSessionCache()->remove(sessionId);
-
- istringstream msg("OK");
- return make_pair(true, request.sendResponse(msg, HTTPResponse::SHIBSP_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()));
-
- 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());
- }
- 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);
- }
-
- 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());
- }
- catch (const std::exception& ex) {
- m_log.error("error issuing SAML 2.0 logout request: %s", ex.what());
- }
-
- 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
-}
-
diff --git a/shibsp/session/impl/CookieSessionCache.cpp b/shibsp/session/impl/CookieSessionCache.cpp
index 40066536..7a2a10ca 100644
--- a/shibsp/session/impl/CookieSessionCache.cpp
+++ b/shibsp/session/impl/CookieSessionCache.cpp
@@ -180,7 +180,7 @@ DDF CookieSessionCache::cache_read(
)
{
if (!request) {
- throw SessionException("Access to request object required to read cookie-backed session.");
+ throw SessionException("Access to request object required to locate cookie-backed session.");
}
string encryptedSession = m_chunkedCookieManager->getChunkedCookieValue(*request);
@@ -515,7 +515,7 @@ bool CookieSessionCache::cache_touch(SPRequest* request, const char* key, unsign
void CookieSessionCache::cache_remove(SPRequest* request, const char* key)
{
if (!request) {
- throw SessionException("Access to request object required to remove cookie-backed session.");
+ throw SessionException("Cookie-backed session cache does not support admninistrative logout.");
}
m_chunkedCookieManager->unsetCookie(*request, true);
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list