Local logout handler, makefile changes, bug fix to SAML initiator.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Sun, 8 Jul 2007 20:56:57 +0000 (20:56 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Sun, 8 Jul 2007 20:56:57 +0000 (20:56 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2342 cb58f699-b61c-0410-a6fe-9272a202ed29

shibsp/Makefile.am
shibsp/handler/impl/LocalLogoutInitiator.cpp [new file with mode: 0644]
shibsp/handler/impl/SAML2LogoutInitiator.cpp [new file with mode: 0644]
shibsp/shibsp-lite.vcproj
shibsp/shibsp.vcproj

index 35fb053..14ef697 100644 (file)
@@ -69,6 +69,7 @@ handinclude_HEADERS = \
        handler/AbstractHandler.h \
        handler/AssertionConsumerService.h \
        handler/Handler.h \
+       handler/LogoutHandler.h \
        handler/RemotedHandler.h \
        handler/SessionInitiator.h
 
@@ -108,11 +109,15 @@ common_sources = \
        handler/impl/AbstractHandler.cpp \
        handler/impl/AssertionConsumerService.cpp \
        handler/impl/AssertionLookup.cpp \
+       handler/impl/ChainingLogoutInitiator.cpp \
        handler/impl/ChainingSessionInitiator.cpp \
+       handler/impl/LocalLogoutInitiator.cpp \
+       handler/impl/LogoutHandler.cpp \
        handler/impl/RemotedHandler.cpp \
        handler/impl/SAML1Consumer.cpp \
        handler/impl/SAML2Consumer.cpp \
        handler/impl/SAML2ArtifactResolution.cpp \
+       handler/impl/SAML2LogoutInitiator.cpp \
        handler/impl/SAML2SessionInitiator.cpp \
        handler/impl/SAMLDSSessionInitiator.cpp \
        handler/impl/SessionInitiator.cpp \
diff --git a/shibsp/handler/impl/LocalLogoutInitiator.cpp b/shibsp/handler/impl/LocalLogoutInitiator.cpp
new file mode 100644 (file)
index 0000000..004af9f
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ *  Copyright 2001-2007 Internet2
+ * 
+ * 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.
+ */
+
+/**
+ * LocalLogoutInitiator.cpp
+ * 
+ * Logs out a session locally.
+ */
+
+#include "internal.h"
+#include "Application.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "handler/AbstractHandler.h"
+#include "handler/LogoutHandler.h"
+
+using namespace shibsp;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+
+    class SHIBSP_DLLLOCAL LocalLogoutInitiator : public AbstractHandler, public LogoutHandler
+    {
+    public:
+        LocalLogoutInitiator(const DOMElement* e, const char* appId);
+        virtual ~LocalLogoutInitiator() {}
+        
+        pair<bool,long> run(SPRequest& request, bool isHandler=true) const;
+
+    private:
+        vector<Handler*> m_handlers;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    Handler* SHIBSP_DLLLOCAL LocalLogoutInitiatorFactory(const pair<const DOMElement*,const char*>& p)
+    {
+        return new LocalLogoutInitiator(p.first, p.second);
+    }
+};
+
+LocalLogoutInitiator::LocalLogoutInitiator(const DOMElement* e, const char* appId)
+    : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".LogoutInitiator"))
+{
+    pair<bool,const char*> loc = getString("Location");
+    if (loc.first) {
+        string address = string(appId) + loc.second + "::run::LocalLI";
+        setAddress(address.c_str());
+    }
+}
+
+pair<bool,long> LocalLogoutInitiator::run(SPRequest& request, bool isHandler) const
+{
+    // Defer to base class first.
+    pair<bool,long> ret = LogoutHandler::run(request, isHandler);
+    if (ret.first)
+        return ret;
+
+    // Get session ID from cookie.
+    pair<string,const char*> shib_cookie = request.getApplication().getCookieNameProps("_shibsession_");
+    const char* session_id = request.getCookie(shib_cookie.first.c_str());
+    if (session_id)
+        request.getServiceProvider().getSessionCache()->remove(session_id, request.getApplication());
+
+    return sendLogoutPage(request.getApplication(), request, true, "Logout was successful.");
+}
diff --git a/shibsp/handler/impl/SAML2LogoutInitiator.cpp b/shibsp/handler/impl/SAML2LogoutInitiator.cpp
new file mode 100644 (file)
index 0000000..c60ad96
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ *  Copyright 2001-2007 Internet2
+ * 
+ * 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.
+ */
+
+/**
+ * SAML2LogoutInitiator.cpp
+ * 
+ * Triggers SP-initiated logout for SAML 2.0 sessions.
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "Application.h"
+#include "ServiceProvider.h"
+#include "SessionCache.h"
+#include "handler/AbstractHandler.h"
+#include "handler/LogoutHandler.h"
+
+#ifndef SHIBSP_LITE
+# include "binding/SOAPClient.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/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 log4cpp;
+using namespace std;
+
+namespace shibsp {
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4250 )
+#endif
+    
+    class SHIBSP_DLLLOCAL SAML2LogoutInitiator : public AbstractHandler, public LogoutHandler
+    {
+    public:
+        SAML2LogoutInitiator(const DOMElement* e, const char* appId);
+        virtual ~SAML2LogoutInitiator() {
+#ifndef SHIBSP_LITE
+            if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+                XMLString::release(&m_outgoing);
+                for_each(m_encoders.begin(), m_encoders.end(), cleanup_pair<const XMLCh*,MessageEncoder>());
+            }
+#endif
+        }
+        
+        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, Session* session_id, HTTPResponse& httpResponse) const;
+
+        string m_appId;
+#ifndef SHIBSP_LITE
+        LogoutRequest* buildRequest(
+            const Application& application, const Session& session, const IDPSSODescriptor& role, const MessageEncoder* encoder=NULL
+            ) const;
+
+        XMLCh* m_outgoing;
+        vector<const XMLCh*> m_bindings;
+        map<const XMLCh*,MessageEncoder*> m_encoders;
+#endif
+        auto_ptr_char m_protocol;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+    Handler* SHIBSP_DLLLOCAL SAML2LogoutInitiatorFactory(const pair<const DOMElement*,const char*>& p)
+    {
+        return new SAML2LogoutInitiator(p.first, p.second);
+    }
+};
+
+SAML2LogoutInitiator::SAML2LogoutInitiator(const DOMElement* e, const char* appId)
+    : AbstractHandler(e, Category::getInstance(SHIBSP_LOGCAT".LogoutInitiator")), m_appId(appId),
+#ifndef SHIBSP_LITE
+        m_outgoing(NULL),
+#endif
+        m_protocol(samlconstants::SAML20P_NS)
+{
+#ifndef SHIBSP_LITE
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+        // Handle outgoing binding setup.
+        pair<bool,const XMLCh*> outgoing = getXMLString("outgoingBindings");
+        if (outgoing.first) {
+            m_outgoing = XMLString::replicate(outgoing.second);
+            XMLString::trim(m_outgoing);
+        }
+        else {
+            // No override, so we'll install a default binding precedence.
+            string prec = string(samlconstants::SAML20_BINDING_HTTP_REDIRECT) + ' ' + samlconstants::SAML20_BINDING_HTTP_POST + ' ' +
+                samlconstants::SAML20_BINDING_HTTP_POST_SIMPLESIGN + ' ' + samlconstants::SAML20_BINDING_HTTP_ARTIFACT;
+            m_outgoing = XMLString::transcode(prec.c_str());
+        }
+
+        int pos;
+        XMLCh* start = m_outgoing;
+        while (start && *start) {
+            pos = XMLString::indexOf(start,chSpace);
+            if (pos != -1)
+                *(start + pos)=chNull;
+            m_bindings.push_back(start);
+            try {
+                auto_ptr_char b(start);
+                MessageEncoder * encoder = SAMLConfig::getConfig().MessageEncoderManager.newPlugin(b.get(),e);
+                m_encoders[start] = encoder;
+                m_log.debug("supporting outgoing binding (%s)", b.get());
+            }
+            catch (exception& ex) {
+                m_log.error("error building MessageEncoder: %s", ex.what());
+            }
+            if (pos != -1)
+                start = start + pos + 1;
+            else
+                break;
+        }
+    }
+#endif
+
+    pair<bool,const char*> loc = getString("Location");
+    if (loc.first) {
+        string address = m_appId + loc.second + "::run::SAML2LI";
+        setAddress(address.c_str());
+    }
+}
+
+void SAML2LogoutInitiator::setParent(const PropertySet* parent)
+{
+    DOMPropertySet::setParent(parent);
+    pair<bool,const char*> loc = getString("Location");
+    if (loc.first) {
+        string address = m_appId + loc.second + "::run::SAML2LI";
+        setAddress(address.c_str());
+    }
+    else {
+        m_log.warn("no Location property in SAML2 LogoutInitiator (or parent), can't register as remoted handler");
+    }
+}
+
+pair<bool,long> SAML2LogoutInitiator::run(SPRequest& request, bool isHandler) const
+{
+    // Defer to base class first.
+    pair<bool,long> ret = LogoutHandler::run(request, isHandler);
+    if (ret.first)
+        return ret;
+
+    Session* session = NULL;
+    try {
+        session = request.getSession(false, true, false);  // don't cache it and ignore all checks
+        if (!session)
+            return make_pair(false,0);
+
+        // We only handle SAML 2.0 sessions.
+        if (!XMLString::equals(session->getProtocol(), m_protocol.get()))
+            return make_pair(false,0);
+    }
+    catch (exception& ex) {
+        m_log.error("error accessing current session: %s", ex.what());
+        return make_pair(false,0);
+    }
+
+    // At this point, notification is completed, and we're ready to issue a LogoutRequest.
+
+    if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+        // When out of process, we run natively.
+        return doRequest(request.getApplication(), session, request);
+    }
+    else {
+        // When not out of process, we remote the request.
+        Locker locker(session);
+        DDF out,in(m_address.c_str());
+        DDFJanitor jin(in), jout(out);
+        in.addmember("application_id").string(request.getApplication().getId());
+        in.addmember("session_id").string(session->getID());
+        out=request.getServiceProvider().getListenerService()->send(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) : NULL;
+    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?");
+    }
+    
+    // Set up a response shim.
+    DDF ret(NULL);
+    DDFJanitor jout(ret);
+    auto_ptr<HTTPResponse> resp(getResponse(ret));
+    
+    Session* session = NULL;
+    try {
+         session = app->getServiceProvider().getSessionCache()->find(in["session_id"].string(), *app, NULL, NULL);
+    }
+    catch (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, session, *resp.get());
+        }
+        else {
+             m_log.error("no NameID or issuing entityID found in session");
+             session->unlock();
+             session = NULL;
+             app->getServiceProvider().getSessionCache()->remove(in["session_id"].string(), *app);
+         }
+    }
+    out << ret;
+#else
+    throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
+#endif
+}
+
+pair<bool,long> SAML2LogoutInitiator::doRequest(const Application& application, Session* session, HTTPResponse& response) const
+{
+#ifndef SHIBSP_LITE
+    pair<bool,long> ret = make_pair(false,0);
+    try {
+        // With a session in hand, we can create a LogoutRequest message, if we can find a compatible endpoint.
+        Locker metadataLocker(application.getMetadataProvider());
+        const EntityDescriptor* entity = application.getMetadataProvider()->getEntityDescriptor(session->getEntityID());
+        if (!entity) {
+            throw MetadataException(
+                "Unable to locate metadata for identity provider ($entityID)",
+                namedparams(1, "entityID", session->getEntityID())
+                );
+        }
+        const IDPSSODescriptor* role = entity->getIDPSSODescriptor(samlconstants::SAML20P_NS);
+        if (!role) {
+            throw MetadataException(
+                "Unable to locate SAML 2.0 IdP role for identity provider ($entityID).",
+                namedparams(1, "entityID", session->getEntityID())
+                );
+        }
+
+        const EndpointType* ep=NULL;
+        const MessageEncoder* encoder=NULL;
+        vector<const XMLCh*>::const_iterator b;
+        for (b = m_bindings.begin(); b!=m_bindings.end(); ++b) {
+            if (ep=EndpointManager<SingleLogoutService>(role->getSingleLogoutServices()).getByBinding(*b)) {
+                map<const XMLCh*,MessageEncoder*>::const_iterator enc = m_encoders.find(*b);
+                if (enc!=m_encoders.end())
+                    encoder = enc->second;
+                break;
+            }
+        }
+        if (!ep || !encoder) {
+            m_log.warn("no compatible front channel SingleLogoutService, trying back channel...");
+            shibsp::SecurityPolicy policy(application);
+            shibsp::SOAPClient soaper(policy);
+            MetadataCredentialCriteria mcc(*role);
+
+            LogoutResponse* logoutResponse=NULL;
+            auto_ptr_XMLCh binding(samlconstants::SAML20_BINDING_SOAP);
+            const vector<SingleLogoutService*>& endpoints=role->getSingleLogoutServices();
+            for (vector<SingleLogoutService*>::const_iterator epit=endpoints.begin(); !logoutResponse && epit!=endpoints.end(); ++epit) {
+                try {
+                    if (!XMLString::equals((*epit)->getBinding(),binding.get()))
+                        continue;
+                    LogoutRequest* msg = buildRequest(application, *session, *role);
+                    auto_ptr_char dest((*epit)->getLocation());
+
+                    SAML2SOAPClient client(soaper, false);
+                    client.sendSAML(msg, mcc, dest.get());
+                    StatusResponseType* srt = client.receiveSAML();
+                    if (!(logoutResponse = dynamic_cast<LogoutResponse*>(srt))) {
+                        delete srt;
+                        break;
+                    }
+                }
+                catch (exception& ex) {
+                    m_log.error("error sending LogoutRequest message: %s", ex.what());
+                    soaper.reset();
+                }
+            }
+
+            if (!logoutResponse)
+                return sendLogoutPage(application, response, false, "Identity provider did not respond to logout request.");
+            if (!logoutResponse->getStatus() || !logoutResponse->getStatus()->getStatusCode() ||
+                   !XMLString::equals(logoutResponse->getStatus()->getStatusCode()->getValue(), saml2p::StatusCode::SUCCESS)) {
+                delete logoutResponse;
+                return sendLogoutPage(application, response, false, "Identity provider returned a SAML error in response to logout request.");
+            }
+            delete logoutResponse;
+            return sendLogoutPage(application, response, false, "Logout completed successfully.");
+        }
+
+        auto_ptr<LogoutRequest> msg(buildRequest(application, *session, *role, encoder));
+
+        msg->setDestination(ep->getLocation());
+        auto_ptr_char dest(ep->getLocation());
+        ret.second = sendMessage(*encoder, msg.get(), NULL, dest.get(), role, application, response, "signRequests");
+        ret.first = true;
+        msg.release();  // freed by encoder
+    }
+    catch (exception& ex) {
+        m_log.error("error issuing SAML 2.0 logout request: %s", ex.what());
+    }
+
+    if (session) {
+        string session_id = session->getID();
+        session->unlock();
+        session = NULL;
+        application.getServiceProvider().getSessionCache()->remove(session_id.c_str(), application);
+    }
+
+    return ret;
+#else
+    string session_id = session->getID();
+    session->unlock();
+    application.getServiceProvider().getSessionCache()->remove(session_id.c_str(), application);
+    throw ConfigurationException("Cannot perform logout using lite version of shibsp library.");
+#endif
+}
+
+#ifndef SHIBSP_LITE
+
+LogoutRequest* SAML2LogoutInitiator::buildRequest(
+    const Application& application, const Session& session, const IDPSSODescriptor& role, const MessageEncoder* encoder
+    ) const
+{
+    auto_ptr<LogoutRequest> msg(LogoutRequestBuilder::buildLogoutRequest());
+    Issuer* issuer = IssuerBuilder::buildIssuer();
+    msg->setIssuer(issuer);
+    issuer->setName(application.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();
+    const PropertySet* relyingParty = application.getRelyingParty(dynamic_cast<EntityDescriptor*>(role.getParent()));
+    pair<bool,const char*> flag = relyingParty->getString("encryptRequests");
+    if (flag.first &&
+        (!strcmp(flag.second, "true") || (encoder && !strcmp(flag.second, "front")) || (!encoder && !strcmp(flag.second, "back")))) {
+        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.release());
+    }
+
+    if (!encoder) {
+        // No encoder being used, so sign for SOAP client manually.
+        flag = relyingParty->getString("signRequests");
+        if (flag.first && (!strcmp(flag.second, "true") || !strcmp(flag.second, "back"))) {
+            CredentialResolver* credResolver=application.getCredentialResolver();
+            if (credResolver) {
+                Locker credLocker(credResolver);
+                // Fill in criteria to use.
+                MetadataCredentialCriteria mcc(role);
+                mcc.setUsage(CredentialCriteria::SIGNING_CREDENTIAL);
+                pair<bool,const char*> keyName = relyingParty->getString("keyName");
+                if (keyName.first)
+                    mcc.getKeyNames().insert(keyName.second);
+                pair<bool,const XMLCh*> sigalg = relyingParty->getXMLString("signatureAlg");
+                if (sigalg.first)
+                    mcc.setXMLAlgorithm(sigalg.second);
+                const Credential* cred = credResolver->resolve(&mcc);
+                if (cred) {
+                    xmlsignature::Signature* sig = xmlsignature::SignatureBuilder::buildSignature();\r
+                    msg->setSignature(sig);\r
+                    pair<bool, const XMLCh*> alg = relyingParty->getXMLString("signatureAlg");\r
+                    if (alg.first)\r
+                        sig->setSignatureAlgorithm(alg.second);\r
+                    alg = relyingParty->getXMLString("digestAlg");\r
+                    if (alg.first) {\r
+                        ContentReference* cr = dynamic_cast<ContentReference*>(sig->getContentReference());\r
+                        if (cr)\r
+                            cr->setDigestAlgorithm(alg.second);\r
+                    }\r
+            \r
+                    // Sign response while marshalling.\r
+                    vector<xmlsignature::Signature*> sigs(1,sig);\r
+                    msg->marshall((DOMDocument*)NULL,&sigs,cred);\r
+                }
+                else {
+                    m_log.warn("no signing credential resolved, leaving message unsigned");
+                }
+            }
+            else {
+                m_log.warn("no credential resolver installed, leaving message unsigned");
+            }
+        }
+    }
+
+    return msg.release();
+}
+
+#endif
index c59c2c3..dd06344 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\ChainingLogoutInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\ChainingSessionInitiator.cpp"\r
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\LocalLogoutInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
+                                               RelativePath=".\handler\impl\LogoutHandler.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\RemotedHandler.cpp"\r
                                                >\r
                                        </File>\r
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\SAML2LogoutInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\SAML2SessionInitiator.cpp"\r
                                                >\r
                                        </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\handler\LogoutHandler.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\handler\RemotedHandler.h"\r
                                        >\r
                                </File>\r
index c07f175..7cca0a0 100644 (file)
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\ChainingLogoutInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\ChainingSessionInitiator.cpp"\r
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\LocalLogoutInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
+                                               RelativePath=".\handler\impl\LogoutHandler.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\RemotedHandler.cpp"\r
                                                >\r
                                        </File>\r
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\handler\impl\SAML2LogoutInitiator.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\handler\impl\SAML2SessionInitiator.cpp"\r
                                                >\r
                                        </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\handler\LogoutHandler.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\handler\RemotedHandler.h"\r
                                        >\r
                                </File>\r