[cpp-sp] branch main updated: Remove old SAML logout code.

Codeberg noreply at shibboleth.net
Tue May 19 13:47:07 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/7f2d57b946b8e25526ce0fb488f23e1cc7befde7

The following commit(s) were added to refs/heads/main by this push:
     new 7f2d57b9 Remove old SAML logout code.
7f2d57b9 is described below

commit 7f2d57b946b8e25526ce0fb488f23e1cc7befde7
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Tue May 19 09:46:51 2026 -0400

    Remove old SAML logout code.
---
 shibsp/handler/impl/SAML2Logout.cpp          | 546 ---------------------------
 shibsp/handler/impl/SAML2LogoutInitiator.cpp | 476 -----------------------
 2 files changed, 1022 deletions(-)

diff --git a/shibsp/handler/impl/SAML2Logout.cpp b/shibsp/handler/impl/SAML2Logout.cpp
deleted file mode 100644
index 485c0474..00000000
--- a/shibsp/handler/impl/SAML2Logout.cpp
+++ /dev/null
@@ -1,546 +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/SAML2Logout.cpp
- *
- * Handles SAML 2.0 single logout protocol messages.
- */
-
-#include "internal.h"
-#include "exceptions.h"
-#include "SPRequest.h"
-#include "handler/AbstractHandler.h"
-#include "handler/LogoutHandler.h"
-#include "util/SPConstants.h"
-
-#include <boost/scoped_ptr.hpp>
-
-using namespace shibsp;
-using namespace boost;
-using namespace xercesc;
-using namespace std;
-
-namespace shibsp {
-
-#if defined (_MSC_VER)
-    #pragma warning( push )
-    #pragma warning( disable : 4250 )
-#endif
-
-    class SHIBSP_DLLLOCAL SAML2Logout : public AbstractHandler, public LogoutHandler
-    {
-    public:
-        SAML2Logout(const DOMElement* e, const char* appId, bool deprecationSupport=true);
-        virtual ~SAML2Logout() {}
-
-        void receive(DDF& in, ostream& out);
-        pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
-
-    private:
-        pair<bool,long> doRequest(SPRequest& request) const;
-    };
-
-#if defined (_MSC_VER)
-    #pragma warning( pop )
-#endif
-
-    Handler* SHIBSP_DLLLOCAL SAML2LogoutFactory(const pair<const DOMElement*,const char*>& p, bool deprecationSupport)
-    {
-        return new SAML2Logout(p.first, p.second, deprecationSupport);
-    }
-};
-
-SAML2Logout::SAML2Logout(const DOMElement* e, const char* appId, bool deprecationSupport)
-    : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT ".Logout.SAML2"))
-{
-    m_initiator = false;
-#ifndef SHIBSP_LITE
-    m_preserve.push_back("ID");
-    m_preserve.push_back("entityID");
-    m_preserve.push_back("RelayState");
-
-    pair<bool, bool> flag = getBool("notifyWithoutSession", shibspconstants::ASCII_SHIBSPCONFIG_NS);
-    if (!flag.first)
-        flag = getBool("notifyWithoutSession");
-    m_notifyWithoutSession = flag.first && flag.second;
-
-    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
-        SAMLConfig& conf = SAMLConfig::getConfig();
-
-        // Handle incoming binding.
-        m_decoder.reset(conf.MessageDecoderManager.newPlugin(getString("Binding").second, e, deprecationSupport));
-        m_decoder->setArtifactResolver(SPConfig::getConfig().getArtifactResolver());
-
-        if (m_decoder->isUserAgentPresent()) {
-            // Handle front-channel binding setup.
-            string dupBindings;
-            pair<bool,const char*> outgoing = getString("outgoingBindings", shibspconstants::ASCII_SHIBSPCONFIG_NS);
-            if (outgoing.first) {
-                dupBindings = outgoing.second;
-                trim(dupBindings);
-            }
-            else {
-                // No override, so we'll install a default binding precedence.
-                dupBindings = string(samlconstants::SAML20_BINDING_HTTP_REDIRECT) + ' ' + samlconstants::SAML20_BINDING_HTTP_POST + ' ' +
-                    samlconstants::SAML20_BINDING_HTTP_POST_SIMPLESIGN + ' ' + samlconstants::SAML20_BINDING_HTTP_ARTIFACT;
-            }
-
-            split(m_bindings, dupBindings, is_space(), algorithm::token_compress_on);
-            for (vector<string>::const_iterator b = m_bindings.begin(); b != m_bindings.end(); ++b) {
-                try {
-                    boost::shared_ptr<MessageEncoder> encoder(conf.MessageEncoderManager.newPlugin(*b, e, deprecationSupport));
-                    if (encoder->isUserAgentPresent() && XMLString::equals(getProtocolFamily(), encoder->getProtocolFamily())) {
-                        m_encoders[*b] = encoder;
-                        m_log.debug("supporting outgoing binding (%s)", b->c_str());
-                    }
-                    else {
-                        m_log.warn("skipping outgoing binding (%s), not a SAML 2.0 front-channel mechanism", b->c_str());
-                    }
-                }
-                catch (const std::exception& ex) {
-                    m_log.error("error building MessageEncoder: %s", ex.what());
-                }
-            }
-        }
-        else {
-            pair<bool,const char*> b = getString("Binding");
-            boost::shared_ptr<MessageEncoder> encoder(conf.MessageEncoderManager.newPlugin(b.second, e, deprecationSupport));
-            m_encoders[b.second] = encoder;
-        }
-    }
-#endif
-
-    string address(appId);
-    address += getString("Location").second;
-    setAddress(address.c_str());
-}
-
-pair<bool,long> SAML2Logout::run(SPRequest& request, bool isHandler) const
-{
-    // Defer to base class for front-channel loop first.
-    // This won't initiate the loop, only continue/end it.
-    pair<bool,long> ret = LogoutHandler::run(request, isHandler);
-    if (ret.first)
-        return ret;
-
-    if (false) {
-        // When out of process, we run natively and directly process the message.
-        return doRequest(request);
-    }
-    else {
-        // When not out of process, we remote all the message processing.
-        vector<string> headers(1,"Cookie");
-        headers.push_back("User-Agent");
-        DDF out,in = wrap(request, &headers, true);
-        DDFJanitor jin(in), jout(out);
-        out = send(request, in);
-        return unwrap(request, out);
-    }
-}
-
-void SAML2Logout::receive(DDF& in, ostream& out)
-{
-    /*
-    // 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(*app, in));
-
-    // Wrap a response shim.
-    DDF ret(nullptr);
-    DDFJanitor jout(ret);
-    scoped_ptr<HTTPResponse> resp(getResponse(*app, 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;
-    */
-}
-
-pair<bool,long> SAML2Logout::doRequest(SPRequest& request) const
-{
-#ifndef SHIBSP_LITE
-    // First capture the active session ID, if any.
-    SessionCache* cache = application.getServiceProvider().getSessionCache();
-    string session_id = cache->active(application, request);
-
-    if (!strcmp(request.getMethod(),"GET") && request.getParameter("notifying")) {
-        // This is returning from a front-channel notification, so we have to do the back-channel and then
-        // respond. To do that, we need state from the original request.
-        if (!request.getParameter("entityID")) {
-            cache->remove(application, request, &response);
-            throw FatalProfileException("Application notification loop did not return entityID for LogoutResponse.");
-        }
-
-        // Best effort on back channel and to remove the user agent's session.
-        bool worked1 = false,worked2 = false;
-        if (!session_id.empty()) {
-            vector<string> sessions(1,session_id);
-            worked1 = notifyBackChannel(application, request.getRequestURL(), sessions, false);
-            try {
-                cache->remove(application, request, &response);
-                worked2 = true;
-            }
-            catch (const std::exception& ex) {
-                m_log.error("error removing session (%s): %s", session_id.c_str(), ex.what());
-            }
-        }
-        else {
-            worked1 = worked2 = true;
-        }
-
-        // We need metadata to issue a response.
-        MetadataProvider* m = application.getMetadataProvider();
-        Locker metadataLocker(m);
-        MetadataProviderCriteria mc(
-            application, request.getParameter("entityID"), &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", request.getParameter("entityID"))
-                );
-        }
-        else if (!entity.second) {
-            throw MetadataException(
-                "Unable to locate SAML 2.0 IdP role for identity provider ($entityID).",
-                namedparams(1, "entityID", request.getParameter("entityID"))
-                );
-        }
-
-        auto_ptr_XMLCh reqid(request.getParameter("ID"));
-        if (worked1 && worked2) {
-            // Successful LogoutResponse. Has to be front-channel or we couldn't be here.
-            return sendResponse(
-                reqid.get(),
-                StatusCode::SUCCESS, nullptr, nullptr,
-                request.getParameter("RelayState"),
-                entity.second,
-                application,
-                response,
-                true
-                );
-        }
-
-        return sendResponse(
-            reqid.get(),
-            StatusCode::RESPONDER, nullptr, "Unable to fully destroy principal's session.",
-            request.getParameter("RelayState"),
-            entity.second,
-            application,
-            response,
-            true
-            );
-    }
-
-    // If we get here, it's an external protocol message to decode.
-
-    // Locate policy key.
-    pair<bool,const char*> policyId = getString("policyId", shibspconstants::ASCII_SHIBSPCONFIG_NS);  // may be namespace-qualified inside handler element
-    if (!policyId.first)
-        policyId = getString("policyId");   // try unqualified
-    if (!policyId.first)
-        policyId = application.getString("policyId");   // unqualified in Application(s) element
-
-    // Lock metadata for use by policy.
-    Locker metadataLocker(application.getMetadataProvider());
-
-    // Create the policy.
-    scoped_ptr<SecurityPolicy> policy(
-        application.getServiceProvider().getSecurityPolicyProvider()->createSecurityPolicy(
-            samlconstants::SAML20_PROFILE_SSO_LOGOUT, application, &IDPSSODescriptor::ELEMENT_QNAME, policyId.second
-            )
-        );
-
-    // Decode the message.
-    string relayState;
-    scoped_ptr<XMLObject> msg(m_decoder->decode(relayState, request, &response, *policy));
-    const LogoutRequest* logoutRequest = dynamic_cast<LogoutRequest*>(msg.get());
-    if (logoutRequest) {
-        if (!policy->isAuthenticated())
-            throw SecurityPolicyException("Security of LogoutRequest not established.");
-
-        // Message from IdP to logout one or more sessions.
-        // Extract the NameID from the request, decrypting it if needed.
-
-        scoped_ptr<XMLObject> decryptedID;
-        NameID* nameid = logoutRequest->getNameID();
-        if (!nameid) {
-            // Check for EncryptedID.
-            EncryptedID* encname = logoutRequest->getEncryptedID();
-            if (encname) {
-                CredentialResolver* cr=application.getCredentialResolver();
-                if (!cr)
-                    m_log.warn("found encrypted NameID, but no decryption credential was available");
-                else {
-                    Locker credlocker(cr);
-                    scoped_ptr<MetadataCredentialCriteria> mcc(
-                        policy->getIssuerMetadata() ? new MetadataCredentialCriteria(*policy->getIssuerMetadata()) : nullptr
-                        );
-                    try {
-                        decryptedID.reset(
-                            encname->decrypt(
-                                *cr,
-                                application.getRelyingParty(
-                                    policy->getIssuerMetadata() ?
-                                        dynamic_cast<EntityDescriptor*>(policy->getIssuerMetadata()->getParent()) :
-                                            nullptr)->getXMLString("entityID").second,
-                                mcc.get()
-                                )
-                            );
-                        nameid = dynamic_cast<NameID*>(decryptedID.get());
-                    }
-                    catch (const std::exception& ex) {
-                        m_log.error(ex.what());
-                    }
-                }
-            }
-        }
-        if (!nameid) {
-            // No NameID, so must respond with an error.
-            m_log.error("NameID not found in request");
-            return sendResponse(
-                logoutRequest->getID(),
-                StatusCode::REQUESTER, StatusCode::UNKNOWN_PRINCIPAL, "NameID not found in request.",
-                relayState.c_str(),
-                policy->getIssuerMetadata(),
-                application,
-                response,
-                m_decoder->isUserAgentPresent()
-                );
-        }
-
-        // Suck indexes out of the request for next steps.
-        set<string> indexes;
-        EntityDescriptor* entity =
-            policy->getIssuerMetadata() ? dynamic_cast<EntityDescriptor*>(policy->getIssuerMetadata()->getParent()) : nullptr;
-        const vector<SessionIndex*> sindexes = logoutRequest->getSessionIndexs();
-        for (indirect_iterator<vector<SessionIndex*>::const_iterator> i = make_indirect_iterator(sindexes.begin());
-                i != make_indirect_iterator(sindexes.end()); ++i) {
-            auto_ptr_char sindex(i->getSessionIndex());
-            indexes.insert(sindex.get());
-        }
-
-        // For a front-channel non-admin LogoutRequest, we have to match the information in the request
-        // against the current session, if one is known/available.
-        if (!session_id.empty()) {
-            if (!XMLString::equals(logoutRequest->getReason(),LogoutRequest::REASON_ADMIN)
-                    && !cache->matches(application, request, entity, *nameid, &indexes)) {
-                return sendResponse(
-                    logoutRequest->getID(),
-                    StatusCode::REQUESTER, StatusCode::REQUEST_DENIED, "Active session did not match logout request.",
-                    relayState.c_str(),
-                    policy->getIssuerMetadata(),
-                    application,
-                    response,
-                    true
-                    );
-            }
-        }
-        else if (m_decoder->isUserAgentPresent()) {
-            m_log.info("processing front channel logout request with no active session");
-        }
-
-        // Now we perform "logout" by finding the matching sessions.
-        vector<string> sessions;
-        try {
-            time_t expires = logoutRequest->getNotOnOrAfter() ? logoutRequest->getNotOnOrAfterEpoch() : 0;
-            cache->logout(application, entity, *nameid, &indexes, expires, sessions);
-            m_log.debug("session cache returned %d sessions bound to NameID in logout request", sessions.size());
-
-            // Now we actually terminate everything except for the active session,
-            // if this is front-channel, for notification purposes.
-            for (vector<string>::const_iterator sit = sessions.begin(); sit != sessions.end(); ++sit)
-                if (*sit != session_id)
-                    cache->remove(application, sit->c_str()); // using the ID-based removal operation
-        }
-        catch (const std::exception& ex) {
-            m_log.error("error while logging out matching sessions: %s", ex.what());
-            return sendResponse(
-                logoutRequest->getID(),
-                StatusCode::RESPONDER, nullptr, ex.what(),
-                relayState.c_str(),
-                policy->getIssuerMetadata(),
-                application,
-                response,
-                m_decoder->isUserAgentPresent()
-                );
-        }
-
-        if (m_decoder->isUserAgentPresent()) {
-            if (!session_id.empty() || m_notifyWithoutSession) {
-                // Pass control to the first front channel notification point, if any.
-                map<string,string> parammap;
-                if (!relayState.empty())
-                    parammap["RelayState"] = relayState;
-                auto_ptr_char entityID(entity ? entity->getEntityID() : nullptr);
-                if (entityID.get())
-                    parammap["entityID"] = entityID.get();
-                auto_ptr_char reqID(logoutRequest->getID());
-                if (reqID.get())
-                    parammap["ID"] = reqID.get();
-                pair<bool,long> result = notifyFrontChannel(application, request, response, &parammap);
-                if (result.first)
-                    return result;
-            }
-            else {
-                m_log.info("client's session isn't available, skipping front-channel notifications");
-            }
-        }
-
-        // For back-channel requests, or if no front-channel notification is needed or possible...
-        bool worked1 = notifyBackChannel(application, request.getRequestURL(), sessions, false);
-        bool worked2 = true;
-        if (!session_id.empty()) {
-            // One last session to yoink...
-            try {
-                cache->remove(application, request, &response);
-            }
-            catch (std::exception& ex) {
-                worked2 = false;
-                m_log.error("error removing active session (%s): %s", session_id.c_str(), ex.what());
-            }
-        }
-
-        return sendResponse(
-            logoutRequest->getID(),
-            (worked1 && worked2) ? StatusCode::SUCCESS : StatusCode::RESPONDER,
-            (worked1 && worked2) ? nullptr : StatusCode::PARTIAL_LOGOUT,
-            nullptr,
-            relayState.c_str(),
-            policy->getIssuerMetadata(),
-            application,
-            response,
-            m_decoder->isUserAgentPresent()
-            );
-    }
-
-    // A LogoutResponse completes an SP-initiated logout sequence.
-    const LogoutResponse* logoutResponse = dynamic_cast<LogoutResponse*>(msg.get());
-    if (logoutResponse) {
-        if (!policy->isAuthenticated()) {
-            SecurityPolicyException ex("Security of LogoutResponse not established.");
-            annotateException(&ex, policy->getIssuerMetadata()); // throws it
-        }
- 
-        checkError(logoutResponse, policy->getIssuerMetadata()); // throws if Status doesn't look good...
-
-        // If relay state is set, recover the original return URL.
-        if (!relayState.empty()) {
-            recoverRelayState(application, request, response, relayState);
-        }
-
-        // Check for partial logout.
-        bool wasPartial = false;
-        const StatusCode* sc = logoutResponse->getStatus() ? logoutResponse->getStatus()->getStatusCode() : nullptr;
-        sc = sc ? sc->getStatusCode() : nullptr;
-        if (sc && XMLString::equals(sc->getValue(), StatusCode::PARTIAL_LOGOUT)) {
-            wasPartial = true;
-        }
-
-        if (!relayState.empty()) {
-            application.limitRedirect(request, relayState.c_str());
-            return make_pair(true, response.sendRedirect(relayState.c_str()));
-        }
-
-        // Return template for completion of logout.
-        return sendLogoutPage(application, request, response, wasPartial ? "partial" : "global");
-    }
-
-    FatalProfileException ex("Incoming message was not a samlp:LogoutRequest or samlp:LogoutResponse.");
-    if (policy->getIssuerMetadata())
-        annotateException(&ex, policy->getIssuerMetadata()); // throws it
-    ex.raise();
-    return make_pair(false,0L);  // never happen, satisfies compiler
-#else
-    throw ConfigurationException("Cannot process logout message using lite version of shibsp library.");
-#endif
-}
-
-#ifndef SHIBSP_LITE
-
-pair<bool,long> SAML2Logout::sendResponse(
-    const XMLCh* requestID,
-    const XMLCh* code,
-    const XMLCh* subcode,
-    const char* msg,
-    const char* relayState,
-    const RoleDescriptor* role,
-    const Application& application,
-    HTTPResponse& httpResponse,
-    bool front
-    ) const
-{
-    // Get endpoint and encoder to use.
-    const EndpointType* ep = nullptr;
-    const MessageEncoder* encoder = nullptr;
-    if (front) {
-        const IDPSSODescriptor* idp = dynamic_cast<const IDPSSODescriptor*>(role);
-        for (vector<string>::const_iterator b = m_bindings.begin(); idp && b != m_bindings.end(); ++b) {
-            auto_ptr_XMLCh wideb(b->c_str());
-            if ((ep = EndpointManager<SingleLogoutService>(idp->getSingleLogoutServices()).getByBinding(wideb.get()))) {
-                map< string,boost::shared_ptr<MessageEncoder> >::const_iterator enc = m_encoders.find(*b);
-                if (enc != m_encoders.end())
-                    encoder = enc->second.get();
-                break;
-            }
-        }
-        if (!ep || !encoder) {
-            auto_ptr_char id(role ? dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID() : nullptr);
-            m_log.error("unable to locate compatible SLO service for provider (%s)", id.get() ? id.get() : "unknown");
-            MetadataException ex("Unable to locate endpoint at IdP ($entityID) to send LogoutResponse.");
-            annotateException(&ex, role);   // throws it
-        }
-    }
-    else {
-        encoder = m_encoders.begin()->second.get();
-    }
-
-    // Prepare response.
-    auto_ptr<LogoutResponse> logout(LogoutResponseBuilder::buildLogoutResponse());
-    logout->setInResponseTo(requestID);
-    if (ep) {
-        const XMLCh* loc = ep->getResponseLocation();
-        if (!loc || !*loc)
-            loc = ep->getLocation();
-        logout->setDestination(loc);
-    }
-    Issuer* issuer = IssuerBuilder::buildIssuer();
-    logout->setIssuer(issuer);
-    issuer->setName(application.getRelyingParty(role ? dynamic_cast<EntityDescriptor*>(role->getParent()) :
-            nullptr)->getXMLString("entityID").second);
-    fillStatus(*logout, code, subcode, msg);
-    XMLCh* msgid = SAMLConfig::getConfig().generateIdentifier();
-    logout->setID(msgid);
-    XMLString::release(&msgid);
-    logout->setIssueInstant(time(nullptr));
-
-    auto_ptr_char dest(logout->getDestination());
-    long ret = sendMessage(*encoder, logout.get(), relayState, dest.get(), role, application, httpResponse, "conditional");
-    logout.release();  // freed by encoder
-    return make_pair(true, ret);
-}
-
-#endif
diff --git a/shibsp/handler/impl/SAML2LogoutInitiator.cpp b/shibsp/handler/impl/SAML2LogoutInitiator.cpp
deleted file mode 100644
index dc7bc388..00000000
--- a/shibsp/handler/impl/SAML2LogoutInitiator.cpp
+++ /dev/null
@@ -1,476 +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/SAML2LogoutInitiator.cpp
- *
- * Triggers SP-initiated logout for SAML 2.0 sessions.
- */
-
-#include "internal.h"
-#include "exceptions.h"
-#include "Agent.h"
-#include "handler/AbstractHandler.h"
-#include "handler/LogoutInitiator.h"
-#include "session/SessionCache.h"
-
-#include <mutex>
-
-using namespace shibsp;
-using namespace xercesc;
-using namespace std;
-
-namespace shibsp {
-
-#if defined (_MSC_VER)
-    #pragma warning( push )
-    #pragma warning( disable : 4250 )
-#endif
-
-    class SHIBSP_DLLLOCAL SAML2LogoutInitiator : public AbstractHandler, public LogoutInitiator
-    {
-    public:
-        SAML2LogoutInitiator(const DOMElement* e, const char* appId, bool deprecationSupport=true);
-        virtual ~SAML2LogoutInitiator() {}
-
-        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(SPRequest& request, Session* session) const;
-
-        string m_appId;
-        bool m_deprecationSupport;
-    };
-
-#if defined (_MSC_VER)
-    #pragma warning( pop )
-#endif
-
-    Handler* SHIBSP_DLLLOCAL SAML2LogoutInitiatorFactory(const pair<const DOMElement*,const char*>& p, bool deprecationSupport)
-    {
-        return new SAML2LogoutInitiator(p.first, p.second, deprecationSupport);
-    }
-};
-
-SAML2LogoutInitiator::SAML2LogoutInitiator(const DOMElement* e, const char* appId, bool deprecationSupport)
-    : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT ".LogoutInitiator.SAML2")),
-        m_appId(appId), m_deprecationSupport(deprecationSupport)
-{
-    // 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 SAML2LogoutInitiator::setParent(const PropertySet* parent)
-{
-    DOMPropertySet::setParent(parent);
-    pair<bool,const char*> loc = getString("Location");
-    init(loc.second);
-}
-
-void SAML2LogoutInitiator::init(const char* location)
-{
-    if (location) {
-        string address = m_appId + location + "::run::SAML2LI";
-        setAddress(address.c_str());
-    }
-    else {
-        m_log.warn("no Location property in SAML2 LogoutInitiator (or parent), can't register as remoted handler");
-    }
-
-#ifndef SHIBSP_LITE
-    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
-        pair<bool,bool> async = getBool("asynchronous");
-        m_async = !async.first || async.second;
-
-        string dupBindings;
-        pair<bool,const char*> outgoing = getString("outgoingBindings");
-        if (outgoing.first) {
-            dupBindings = outgoing.second;
-            trim(dupBindings);
-        }
-        else {
-            // No override, so we'll install a default binding precedence.
-            dupBindings = string(samlconstants::SAML20_BINDING_HTTP_REDIRECT) + ' ' + samlconstants::SAML20_BINDING_HTTP_POST + ' ' +
-                samlconstants::SAML20_BINDING_HTTP_POST_SIMPLESIGN + ' ' + samlconstants::SAML20_BINDING_HTTP_ARTIFACT;
-        }
-        split(m_bindings, dupBindings, is_space(), algorithm::token_compress_on);
-        for (vector<string>::const_iterator b = m_bindings.begin(); b != m_bindings.end(); ++b) {
-            try {
-                boost::shared_ptr<MessageEncoder> encoder(SAMLConfig::getConfig().MessageEncoderManager.newPlugin(*b, getElement(), m_deprecationSupport));
-                if (encoder->isUserAgentPresent() && XMLString::equals(getProtocolFamily(), encoder->getProtocolFamily())) {
-                    m_encoders[*b] = encoder;
-                    m_log.debug("supporting outgoing binding (%s)", b->c_str());
-                }
-                else {
-                    m_log.warn("skipping outgoing binding (%s), not a SAML 2.0 front-channel mechanism", b->c_str());
-                }
-            }
-            catch (const std::exception& ex) {
-                m_log.error("error building MessageEncoder: %s", ex.what());
-            }
-        }
-    }
-#endif
-}
-
-
-pair<bool,long> SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) const
-{
-    // 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 = nullptr;
-    try {
-        session = request.getSession(false, true, false);  // don't cache it and ignore all checks
-        if (!session)
-            return make_pair(false, 0L);
-
-        // We only handle SAML 2.0 sessions.
-        //if (!XMLString::equals(session->getProtocol(), m_protocol.get())) {
-        //    session->unlock();
-        //    return make_pair(false, 0L);
-        //}
-    }
-    catch (const std::exception& ex) {
-        m_log.error("error accessing current session: %s", ex.what());
-        return make_pair(false, 0L);
-    }
-
-    if (false) {
-        // When out of process, we run natively.
-        return doRequest(request, session);
-    }
-    else {
-        // When not out of process, we remote the request.
-        session->unlock();
-        vector<string> headers(1,"Cookie");
-        DDF out,in = wrap(request,&headers);
-        DDFJanitor jin(in), jout(out);
-        out = send(request, in);
-        return unwrap(request, out);
-    }
-}
-
-void SAML2LogoutInitiator::receive(DDF& in, ostream& out)
-{
-#ifndef SHIBSP_LITE
-    // Defer to base class for notifications
-    if (in["notify"].integer() == 1)
-        return LogoutHandler::receive(in, out);
-
-    // 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(*app, in));
-
-    // Set up a response shim.
-    DDF ret(nullptr);
-    DDFJanitor jout(ret);
-    scoped_ptr<HTTPResponse> resp(getResponse(*app, ret));
-
-    Session* session = nullptr;
-    try {
-         session = app->getServiceProvider().getSessionCache()->find(*app, *req, nullptr, nullptr);
-    }
-    catch (std::exception& ex) {
-        m_log.error("error accessing current session: %s", ex.what());
-    }
-
-    // With no session, we just skip the request and let it fall through to an empty struct return.
-    if (session) {
-        if (session->getNameID() && session->getEntityID()) {
-            // 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, 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(), revocationExp);
-        }
-    }
-    out << ret;
-#else
-    throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
-#endif
-}
-
-pair<bool,long> SAML2LogoutInitiator::doRequest(SPRequest& request, Session* session) const
-{
-    unique_lock<Session> sessionLocker(*session, adopt_lock);
-
-    // Do back channel notification.
-    vector<string> sessions(1, session->getID());
-    if (!notifyBackChannel(request, sessions, false)) {
-        time_t revocationExp = session->getExpiration();
-        sessionLocker.unlock();
-        session = nullptr;
-        request.getAgent().getSessionCache()->remove(request, revocationExp);
-        //return sendLogoutPage(application, httpRequest, httpResponse, "partial");
-    }
-
-#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 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())
-                );
-        }
-
-        const EndpointType* ep = nullptr;
-        const MessageEncoder* encoder = nullptr;
-        for (vector<string>::const_iterator b = m_bindings.begin(); b != m_bindings.end(); ++b) {
-            auto_ptr_XMLCh wideb(b->c_str());
-            ep = EndpointManager<SingleLogoutService>(role->getSingleLogoutServices()).getByBinding(wideb.get());
-            if (ep) {
-                map< string,boost::shared_ptr<MessageEncoder> >::const_iterator enc = m_encoders.find(*b);
-                if (enc != m_encoders.end())
-                    encoder = enc->second.get();
-                break;
-            }
-        }
-        if (!ep || !encoder) {
-            m_log.debug("no compatible front channel SingleLogoutService, trying back channel...");
-            shibsp::SecurityPolicy policy(application);
-            shibsp::SOAPClient soaper(policy);
-            MetadataCredentialCriteria mcc(*role);
-
-            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;
-                    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 (endpoints.empty())
-                    m_log.info("IdP doesn't support single logout protocol over a compatible binding");
-                else
-                    m_log.warn("IdP didn't respond to logout request");
-
-                ret = sendLogoutPage(application, httpRequest, httpResponse, "partial");
-            }
-            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)
-                    ret = sendLogoutPage(application, httpRequest, httpResponse, "partial");
-                else {
-                    const char* returnloc = httpRequest.getParameter("return");
-                    if (returnloc) {
-                        // Relative URLs get promoted, absolutes get validated.
-                        if (*returnloc == '/') {
-                            string loc(returnloc);
-                            httpRequest.absolutize(loc);
-                            ret.second = httpResponse.sendRedirect(loc.c_str());
-                        }
-                        else {
-                            application.limitRedirect(httpRequest, returnloc);
-                            ret.second = httpResponse.sendRedirect(returnloc);
-                        }
-                        ret.first = true;
-                    }
-                    else {
-                        ret = sendLogoutPage(application, httpRequest, httpResponse, "global");
-                    }
-                }
-            }
-
-            if (session) {
-                time_t revocationExp = session->getExpiration();
-                sessionLocker.assign();
-                session = nullptr;
-                application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse, revocationExp);
-            }
-
-            return ret;
-        }
-
-        // Save off return location as RelayState.
-        string relayState;
-        const char* returnloc = httpRequest.getParameter("return");
-        if (returnloc) {
-            application.limitRedirect(httpRequest, returnloc);
-            relayState = returnloc;
-            httpRequest.absolutize(relayState);
-        }
-        cleanRelayState(application, httpRequest, httpResponse);
-        preserveRelayState(application, httpResponse, relayState);
-
-        auto_ptr<LogoutRequest> msg(buildRequest(application, *session, *role, ep->getLocation(), encoder));
-        msg->setDestination(ep->getLocation());
-
-        auto_ptr_char dest(ep->getLocation());
-        ret.second = sendMessage(*encoder, msg.get(), relayState.c_str(), dest.get(), role, application, httpResponse, "true");
-        ret.first = true;
-        msg.release();  // freed by encoder
-
-        if (session) {
-            time_t revocationExp = session->getExpiration();
-            sessionLocker.assign();
-            session = nullptr;
-            application.getServiceProvider().getSessionCache()->remove(application, httpRequest, &httpResponse, revocationExp);
-        }
-    }
-    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 (const std::exception& ex) {
-        m_log.error("error issuing SAML 2.0 logout request: %s", ex.what());
-    }
-
-    return ret;
-#else
-    throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
-#endif
-}
-
-#ifndef SHIBSP_LITE
-
-auto_ptr<LogoutRequest> SAML2LogoutInitiator::buildRequest(
-    const Application& application,
-    const Session& session,
-    const RoleDescriptor& role,
-    const XMLCh* endpoint,
-    const MessageEncoder* encoder) 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(), encoder != nullptr)) {
-        try {
-            auto_ptr<EncryptedID> encrypted(EncryptedIDBuilder::buildEncryptedID());
-            MetadataCredentialCriteria mcc(role);
-            encrypted->encrypt(
-                *nameid,
-                *(application.getMetadataProvider()),
-                mcc,
-                encoder ? encoder->isCompact() : false,
-                relyingParty->getXMLString("encryptionAlg").second
-            );
-            msg->setEncryptedID(encrypted.get());
-            encrypted.release();
-        }
-        catch (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));
-
-    if (m_async && encoder) {
-        msg->setExtensions(saml2p::ExtensionsBuilder::buildExtensions());
-        msg->getExtensions()->getUnknownXMLObjects().push_back(AsynchronousBuilder::buildAsynchronous());
-    }
-
-    return msg;
-}
-
-#endif

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list