[cpp-sp] branch master updated: SSPCPP-775 - Client-side session storage

Scott Cantor cantor.2 at osu.edu
Thu Feb 22 20:16:05 EST 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=328324acf969617e9157aa3ae07d36f808e5a187

The following commit(s) were added to refs/heads/master by this push:
       new  328324a   SSPCPP-775 -  Client-side session storage
328324a is described below

commit 328324acf969617e9157aa3ae07d36f808e5a187
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Feb 22 20:14:39 2018 -0500

    SSPCPP-775 -  Client-side session storage
    
    https://issues.shibboleth.net/jira/browse/SSPCPP-775
    
    Lightly tested implementation of session save/reload features.
---
 shibsp/SessionCache.h                      |  35 ++--
 shibsp/impl/StorageServiceSessionCache.cpp | 266 +++++++++++++++++++++++++++--
 shibsp/impl/StorageServiceSessionCache.h   |  55 +++---
 shibsp/impl/StoredSession.cpp              |  12 +-
 shibsp/impl/StoredSession.h                |  20 +--
 5 files changed, 325 insertions(+), 63 deletions(-)

diff --git a/shibsp/SessionCache.h b/shibsp/SessionCache.h
index 5a86b1a..eca14db 100644
--- a/shibsp/SessionCache.h
+++ b/shibsp/SessionCache.h
@@ -66,7 +66,7 @@ namespace shibsp {
      * <p>The SessionCache does not itself require locking to manage
      * concurrency, but access to each Session is generally exclusive
      * or at least controlled, and the caller must unlock a Session
-     * to dispose of it.
+     * to dispose of it.</p>
      */
     class SHIBSP_API Session : public virtual xmltooling::Lockable
     {
@@ -135,7 +135,7 @@ namespace shibsp {
         /**
          * Returns the NameID associated with a session.
          *
-         * <p>SAML 1.x identifiers will be promoted to the 2.0 type.
+         * <p>SAML 1.x identifiers will be promoted to the 2.0 type.</p>
          *
          * @return a SAML 2.0 NameID associated with the session, if any
          */
@@ -152,7 +152,7 @@ namespace shibsp {
         /**
          * Returns a URI containing an AuthnContextClassRef provided with the session.
          *
-         * <p>SAML 1.x AuthenticationMethods will be returned as class references.
+         * <p>SAML 1.x AuthenticationMethods will be returned as class references.</p>
          *
          * @return  a URI identifying the authentication context class
          */
@@ -173,7 +173,7 @@ namespace shibsp {
         virtual const std::vector<Attribute*>& getAttributes() const=0;
 
         /**
-         * Returns the resolved attributes associated with the session, indexed by ID
+         * Returns the resolved attributes associated with the session, indexed by ID.
          *
          * @return an immutable map of attributes keyed by attribute ID
          */
@@ -182,7 +182,7 @@ namespace shibsp {
         /**
          * Returns the identifiers of the assertion(s) cached by the session.
          *
-         * <p>The SSO assertion is guaranteed to be first in the set.
+         * <p>The SSO assertion is guaranteed to be first in the set.</p>
          *
          * @return  an immutable array of AssertionID values
          */
@@ -235,9 +235,9 @@ namespace shibsp {
          * Inserts a new session into the cache and binds the session to the outgoing
          * client response.
          *
-         * <p>The newly created session ID is placed into the first parameter.
+         * <p>The newly created session ID is placed into the first parameter.</p>
          *
-         * <p>The SSO tokens and Attributes remain owned by the caller and are copied by the cache.
+         * <p>The SSO tokens and Attributes remain owned by the caller and are copied by the cache.</p>
          *
          * @param sessionID         reference to string to capture newly inserted session ID
          * @param application       reference to Application that owns the Session
@@ -334,10 +334,10 @@ namespace shibsp {
          * Locates an existing session bound to a request.
          *
          * <p>If the client address is supplied, then a check will be performed against
-         * the address recorded in the record.
+         * the address recorded in the record.</p>
          *
          * <p>If a bound session is found to have expired, be invalid, etc., and if the request
-         * can be used to "clear" the session from subsequent client requests, then it may be cleared.
+         * can be used to "clear" the session from subsequent client requests, then it may be cleared.</p>
          *
          * @param application   reference to Application that owns the Session
          * @param request       request from client bound to session
@@ -346,7 +346,10 @@ namespace shibsp {
          * @return  pointer to locked Session, or nullptr
          */
         virtual Session* find(
-            const Application& application, xmltooling::HTTPRequest& request, const char* client_addr=nullptr, time_t* timeout=nullptr
+            const Application& application,
+            xmltooling::HTTPRequest& request,
+            const char* client_addr=nullptr,
+            time_t* timeout=nullptr
             )=0;
 
         /**
@@ -356,21 +359,23 @@ namespace shibsp {
          * @param request       request from client containing session, or a reference to it
          * @param response      optional response to client enabling removal of session or reference
          */
-        virtual void remove(const Application& application, const xmltooling::HTTPRequest& request, xmltooling::HTTPResponse* response=nullptr)=0;
+        virtual void remove(
+            const Application& application,
+            const xmltooling::HTTPRequest& request,
+            xmltooling::HTTPResponse* response=nullptr
+        )=0;
 
         /**
         * Locates an existing session by ID.
         *
         * <p>If the client address is supplied, then a check will be performed against
-        * the address recorded in the record.
+        * the address recorded in the record.</p>
         *
         * @param application   reference to Application that owns the Session
         * @param key           session key
-        * @param client_addr   network address of client (if known)
-        * @param timeout       inactivity timeout to enforce (0 for none, nullptr to bypass check/update of last access)
         * @return  pointer to locked Session, or nullptr
         */
-        virtual Session* find(const Application& application, const char* key, const char* client_addr = nullptr, time_t* timeout = nullptr)=0;
+        virtual Session* find(const Application& application, const char* key)=0;
 
         /**
         * Deletes an existing session.
diff --git a/shibsp/impl/StorageServiceSessionCache.cpp b/shibsp/impl/StorageServiceSessionCache.cpp
index 9dce5ba..bb2001d 100644
--- a/shibsp/impl/StorageServiceSessionCache.cpp
+++ b/shibsp/impl/StorageServiceSessionCache.cpp
@@ -48,8 +48,10 @@
 #include <boost/bind.hpp>
 #include <xmltooling/io/HTTPRequest.h>
 #include <xmltooling/io/HTTPResponse.h>
+#include <xmltooling/util/DataSealer.h>
 #include <xmltooling/util/NDC.h>
 #include <xmltooling/util/Threads.h>
+#include <xmltooling/util/URLEncoder.h>
 #include <xmltooling/util/XMLHelper.h>
 #include <xercesc/util/XMLUniDefs.hpp>
 
@@ -59,6 +61,7 @@
 # include <saml/saml2/core/Assertions.h>
 # include <saml/saml2/metadata/Metadata.h>
 # include <xmltooling/XMLToolingConfig.h>
+# include <xmltooling/util/ParserPool.h>
 # include <xmltooling/util/StorageService.h>
 # include <xercesc/util/XMLStringTokenizer.hpp>
 using namespace opensaml::saml2md;
@@ -104,6 +107,7 @@ SSCache::SSCache(const DOMElement* e)
     static const XMLCh cacheAssertions[] =      UNICODE_LITERAL_15(c,a,c,h,e,A,s,s,e,r,t,i,o,n,s);
     static const XMLCh cacheTimeout[] =         UNICODE_LITERAL_12(c,a,c,h,e,T,i,m,e,o,u,t);
     static const XMLCh excludeReverseIndex[] =  UNICODE_LITERAL_19(e,x,c,l,u,d,e,R,e,v,e,r,s,e,I,n,d,e,x);
+    static const XMLCh persistedAttributes[] =  UNICODE_LITERAL_19(p,e,r,s,i,s,t,e,d,A,t,t,r,i,b,u,t,e,s);
     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);
@@ -162,6 +166,19 @@ SSCache::SSCache(const DOMElement* e)
             while (toks.hasMoreTokens())
                 m_excludedNames.insert(toks.nextToken());
         }
+
+        const XMLCh* persistedAttributeIds = e ? e->getAttributeNS(nullptr, persistedAttributes) : nullptr;
+        if (persistedAttributeIds && *persistedAttributeIds) {
+            XMLStringTokenizer toks(persistedAttributeIds);
+            while (toks.hasMoreTokens()) {
+                auto_ptr_char tok(toks.nextToken());
+                m_persistedAttributeIds.insert(tok.get());
+            }
+        }
+
+        if (!m_persistedAttributeIds.empty() && XMLToolingConfig::getConfig().getDataSealer() == nullptr) {
+            throw ConfigurationException("Persisting sessions across nodes requires DataSealer component, check configuration");
+        }
     }
 #endif
 
@@ -177,6 +194,7 @@ SSCache::SSCache(const DOMElement* e)
     else {
         if (listener && conf.isEnabled(SPConfig::OutOfProcess)) {
             listener->regListener("find::" STORAGESERVICE_SESSION_CACHE "::SessionCache",this);
+            listener->regListener("recover::" STORAGESERVICE_SESSION_CACHE "::SessionCache", this);
             listener->regListener("remove::" STORAGESERVICE_SESSION_CACHE "::SessionCache",this);
             listener->regListener("touch::" STORAGESERVICE_SESSION_CACHE "::SessionCache",this);
         }
@@ -205,6 +223,7 @@ SSCache::~SSCache()
         ListenerService* listener=conf.getServiceProvider()->getListenerService(false);
         if (listener && conf.isEnabled(SPConfig::OutOfProcess)) {
             listener->unregListener("find::" STORAGESERVICE_SESSION_CACHE "::SessionCache",this);
+            listener->unregListener("recover::" STORAGESERVICE_SESSION_CACHE "::SessionCache", this);
             listener->unregListener("remove::" STORAGESERVICE_SESSION_CACHE "::SessionCache",this);
             listener->unregListener("touch::" STORAGESERVICE_SESSION_CACHE "::SessionCache",this);
         }
@@ -264,6 +283,9 @@ void SSCache::test()
 
 void SSCache::insert(const char* key, time_t expires, const char* name, const char* index, short attempts)
 {
+#ifdef _DEBUG
+    xmltooling::NDC ndc("insert");
+#endif
     if (attempts > 10) {
         throw IOException("Exceeded retry limit.");
     }
@@ -517,6 +539,71 @@ void SSCache::insert(
 
     httpResponse.setCookie(shib_cookie.first.c_str(), k.c_str());
     sessionID = key.get();
+
+    // See if we need to persist the session data itself to a cookie for cross-node recovery.
+    if (!m_persistedAttributeIds.empty()) {
+        persist(app, httpResponse, obj, expires);
+    }
+}
+
+void SSCache::persist(const Application& app, HTTPResponse& httpResponse, DDF& session, time_t expires) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("persist");
+#endif
+
+    m_log.debug("checking if session (%s) should be persisted to cookie", session.name());
+
+    // We don't save assertions...
+    session["assertions"].destroy();
+
+    // Check each attribute.
+    DDF attrs = session["attributes"];
+    DDF attr = attrs.first();
+    while (!attr.isnull()) {
+        const char* aname = attr.first().name();
+        if (m_persistedAttributeIds.count(aname) == 0) {
+            m_log.debug("not persisting attribute for session recovery: %s", aname);
+            attr.destroy();
+        }
+        else {
+            m_log.debug("persisting attribute for session recovery: %s", aname);
+        }
+        attr = attrs.next();
+    }
+
+    if (attrs.integer() == 0) {
+        m_log.info("session (%s) contained no attributes requiring persistence, will not be recoverable", session.name());
+        return;
+    }
+
+    ostringstream persisted;
+    persisted << session;
+
+    try {
+        string sealed = XMLToolingConfig::getConfig().getDataSealer()->wrap(persisted.str().c_str(), expires);
+        sealed = XMLToolingConfig::getConfig().getURLEncoder()->encode(sealed.c_str());
+
+        time_t cookieLifetime;
+        pair<string,const char*> shib_cookie = app.getCookieNameProps("_shibsealed_", &cookieLifetime);
+        sealed += shib_cookie.second;
+        if (cookieLifetime > 0) {
+            cookieLifetime += time(nullptr);
+#ifndef HAVE_GMTIME_R
+            struct tm* ptime = gmtime(&cookieLifetime);
+#else
+            struct tm res;
+            struct tm* ptime = gmtime_r(&cookieLifetime, &res);
+#endif
+            char cookietimebuf[64];
+            strftime(cookietimebuf, 64, "; expires=%a, %d %b %Y %H:%M:%S GMT", ptime);
+            sealed += cookietimebuf;
+        }
+        httpResponse.setCookie(shib_cookie.first.c_str(), sealed.c_str());
+    }
+    catch (std::exception& e) {
+        m_log.error("failed to wrap session (%s) with DataSealer: %s", session.name(), e.what());
+    }
 }
 
 bool SSCache::matches(
@@ -775,7 +862,7 @@ LogoutEvent* SSCache::newLogoutEvent(const Application& app) const
 
 #endif
 
-Session* SSCache::find(const Application& app, const char* key, const char* client_addr, time_t* timeout)
+Session* SSCache::_find(const Application& app, const char* key, const char* recovery, const char* client_addr, time_t* timeout)
 {
 #ifdef _DEBUG
     xmltooling::NDC ndc("find");
@@ -806,6 +893,7 @@ Session* SSCache::find(const Application& app, const char* key, const char* clie
             DDFJanitor jin(in);
             in.structure();
             in.addmember("key").string(key);
+            in.addmember("sealed").string(recovery);
             in.addmember("application_id").string(app.getId());
             if (timeout && *timeout) {
                 // On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps.
@@ -850,8 +938,16 @@ Session* SSCache::find(const Application& app, const char* key, const char* clie
             time_t lastAccess = 0;
             string record;
             int ver = m_storage->readText(key, "session", &record, &lastAccess);
-            if (!ver)
-                return nullptr;
+            if (!ver) {
+                if (recovery && *recovery && recover(app, key, recovery)) {
+                    // Retry the read.
+                    ver = m_storage->readText(key, "session", &record, &lastAccess);
+                    if (!ver)
+                        m_log.warn("recovered session (%s) is missing from storage service", key);
+                }
+                if (!ver)
+                    return nullptr;
+            }
 
             if (0 == lastAccess) {
                 m_log.error("session (ID: %s) did not report time of last access", key);
@@ -952,13 +1048,21 @@ Session* SSCache::find(const Application& app, const char* key, const char* clie
 
 Session* SSCache::find(const Application& app, HTTPRequest& request, const char* client_addr, time_t* timeout)
 {
+#ifdef _DEBUG
+    xmltooling::NDC ndc("find");
+#endif
     string id = active(app, request);
     if (id.empty())
         return nullptr;
+
+    pair<string, const char*> shib_cookie = app.getCookieNameProps("_shibsealed_");
+    const char* c = request.getCookie(shib_cookie.first.c_str());
+
     try {
-        Session* session = find(app, id.c_str(), client_addr, timeout);
+        Session* session = _find(app, id.c_str(), c, client_addr, timeout);
         if (session)
             return session;
+
         HTTPResponse* response = dynamic_cast<HTTPResponse*>(&request);
         if (response) {
             if (!m_outboundHeader.empty())
@@ -967,6 +1071,8 @@ Session* SSCache::find(const Application& app, HTTPRequest& request, const char*
             string exp(shib_cookie.second);
             exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT";
             response->setCookie(shib_cookie.first.c_str(), exp.c_str());
+            shib_cookie = app.getCookieNameProps("_shibsealed_");
+            response->setCookie(shib_cookie.first.c_str(), exp.c_str());
         }
     }
     catch (std::exception&) {
@@ -978,14 +1084,124 @@ Session* SSCache::find(const Application& app, HTTPRequest& request, const char*
             string exp(shib_cookie.second);
             exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT";
             response->setCookie(shib_cookie.first.c_str(), exp.c_str());
+            shib_cookie = app.getCookieNameProps("_shibsealed_");
+            response->setCookie(shib_cookie.first.c_str(), exp.c_str());
         }
         throw;
     }
     return nullptr;
 }
 
+bool SSCache::recover(const Application& app, const char* key, const char* data)
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("recover");
+#endif
+
+    if (!SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
+        m_log.debug("remoting recovery of session from sealed cookie");
+        // Remote the request.
+        DDF in("recover::" STORAGESERVICE_SESSION_CACHE "::SessionCache"), out;
+        DDFJanitor jin(in);
+        in.structure();
+        in.addmember("key").string(key);
+        in.addmember("application_id").string(app.getId());
+        in.addmember("sealed").string(data);
+
+        out = app.getServiceProvider().getListenerService()->send(in);
+        if (!out.isint() || out.integer() != 1) {
+            out.destroy();
+            m_log.debug("recovery of session (%s) failed", key);
+            return false;
+        }
+
+        out.destroy();
+        m_log.debug("session (%s) recovered from sealed cookie", key);
+    }
+    else {
+        // We're out of process, so we can recover the session.
+#ifndef SHIBSP_LITE
+        m_log.debug("attempting recovery of session (%s)", key);
+
+        DDF obj;
+        DDFJanitor jobj(obj);
+        string unwrapped;
+
+        char* dup = nullptr;
+        try {
+            dup = strdup(data);
+            XMLToolingConfig::getConfig().getURLEncoder()->decode(dup);
+            unwrapped = XMLToolingConfig::getConfig().getDataSealer()->unwrap(dup);
+            free(dup);
+
+            stringstream str(unwrapped);
+            str >> obj;
+        }
+        catch (std::exception& e) {
+            if (dup)
+                free(dup);
+            m_log.error("failed to unwrap sealed session data with DataSealer: %s", e.what());
+            return false;
+        }
+
+        if (!obj.isstruct() || !obj.name() || strcmp(obj.name(), key)) {
+            m_log.info("recovered session data was invalid for session (%s)", key);
+            return false;
+        }
+
+        auto_ptr<saml2::NameID> nameidObject;
+        const char* nameid = obj["nameid"].string();
+        if (nameid) {
+            // Parse and bind the document into an XMLObject.
+            istringstream instr(nameid);
+            DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(instr);
+            XercesJanitor<DOMDocument> janitor(doc);
+            nameidObject.reset(saml2::NameIDBuilder::buildNameID());
+            nameidObject->unmarshall(doc->getDocumentElement(), true);
+            janitor.release();
+        }
+
+        m_log.debug("storing recovered session (%s)...", key);
+        time_t now = time(nullptr);
+        if (!m_storage->createText(key, "session", unwrapped.c_str(), now + getCacheTimeout(app))) {
+            m_log.debug("recovered session (%s) matched existing record, likely a race condition");
+            return true;
+        }
+
+        // Store the reverse mapping for logout.
+        auto_ptr_char name(nameidObject.get() ? nameidObject->getName() : nullptr);
+        if (name.get() && *name.get() && m_reverseIndex
+            && (m_excludedNames.size() == 0 || m_excludedNames.count(nameidObject->getName()) == 0)) {
+            try {
+                auto_ptr_XMLCh exp(obj["expires"].string());
+                if (exp.get()) {
+                    XMLDateTime iso(exp.get());
+                    iso.parseDateTime();
+                    insert(key, iso.getEpoch(), name.get(), obj["session_index"].string());
+                }
+            }
+            catch (std::exception& ex) {
+                m_log.error("error storing back mapping of NameID for logout: %s", ex.what());
+            }
+        }
+
+        const char* pid = obj["entity_id"].string();
+        const char* prot = obj["protocol"].string();
+        m_log.info("session recovered: ID (%s) IdP (%s) Protocol(%s)",
+            key, pid ? pid : "none", prot ? prot : "none");
+#else
+        throw ConfigurationException("SessionCache recovery requires a DataSealer.");
+#endif
+    }
+
+    return true;
+}
+
 void SSCache::remove(const Application& app, const HTTPRequest& request, HTTPResponse* response)
 {
+#ifdef _DEBUG
+    xmltooling::NDC ndc("remove");
+#endif
     string session_id;
     pair<string,const char*> shib_cookie = app.getCookieNameProps("_shibsession_");
 
@@ -1004,6 +1220,9 @@ void SSCache::remove(const Application& app, const HTTPRequest& request, HTTPRes
             string exp(shib_cookie.second);
             exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT";
             response->setCookie(shib_cookie.first.c_str(), exp.c_str());
+
+            shib_cookie = app.getCookieNameProps("_shibsealed_");
+            response->setCookie(shib_cookie.first.c_str(), exp.c_str());
         }
         remove(app, session_id.c_str());
     }
@@ -1171,14 +1390,25 @@ void SSCache::receive(DDF& in, ostream& out)
         // Do an unversioned read.
         string record;
         time_t lastAccess = 0;
-        if (!m_storage->readText(key, "session", &record, &lastAccess)) {
-            m_log.debug("session not found in cache (%s)", key);
-            DDF ret(nullptr);
-            DDFJanitor jan(ret);
-            out << ret;
-            return;
+        int ver = m_storage->readText(key, "session", &record, &lastAccess);
+        if (!ver) {
+            const char* recovery = in["sealed"].string();
+            if (recovery && *recovery && recover(*app, key, recovery)) {
+                // Retry the read.
+                ver = m_storage->readText(key, "session", &record, &lastAccess);
+                if (!ver)
+                    m_log.warn("recovered session (%s) is missing from storage service", key);
+            }
+
+            if (!ver) {
+                DDF ret(nullptr);
+                DDFJanitor jan(ret);
+                out << ret;
+                return;
+            }
         }
-        else if (lastAccess == 0) {
+        
+        if (lastAccess == 0) {
             m_log.error("session (ID: %s) did not report time of last access", key);
             throw RetryableProfileException("Your session has expired, and you must re-authenticate.");
         }
@@ -1346,6 +1576,20 @@ void SSCache::receive(DDF& in, ostream& out)
         DDFJanitor jan(ret);
         out << ret;
     }
+    else if (!strcmp(in.name(), "recover::" STORAGESERVICE_SESSION_CACHE "::SessionCache")) {
+        const char* key = in["key"].string();
+        const char* cookie = in["sealed"].string();
+        if (!key || !cookie)
+            throw ListenerException("Required parameter missing for session recovery.");
+
+        DDF ret(nullptr);
+        DDFJanitor jan(ret);
+        if (recover(*app, key, cookie))
+            ret.integer(1L);
+        else
+            ret.integer(0L);
+        out << ret;
+    }
 }
 
 #endif
diff --git a/shibsp/impl/StorageServiceSessionCache.h b/shibsp/impl/StorageServiceSessionCache.h
index ba68bd0..d2411ba 100644
--- a/shibsp/impl/StorageServiceSessionCache.h
+++ b/shibsp/impl/StorageServiceSessionCache.h
@@ -56,9 +56,9 @@ namespace opensaml {
 namespace shibsp {
 
     class StoredSession;
-    class SSCache : public shibsp::SessionCache
+    class SSCache : public SessionCache
 #ifndef SHIBSP_LITE
-        ,public virtual shibsp::Remoted
+        ,public virtual Remoted
 #endif
     {
     public:
@@ -66,11 +66,11 @@ namespace shibsp {
         virtual ~SSCache();
 
 #ifndef SHIBSP_LITE
-        void receive(shibsp::DDF& in, std::ostream& out);
+        void receive(DDF& in, std::ostream& out);
 
         void insert(
             std::string& sessionID,
-            const shibsp::Application& app,
+            const Application& app,
             const xmltooling::HTTPRequest& httpRequest,
             xmltooling::HTTPResponse& httpResponse,
             time_t expires,
@@ -82,10 +82,10 @@ namespace shibsp {
             const XMLCh* authncontext_class=nullptr,
             const XMLCh* authncontext_decl=nullptr,
             const std::vector<const opensaml::Assertion*>* tokens=nullptr,
-            const std::vector<shibsp::Attribute*>* attributes=nullptr
+            const std::vector<Attribute*>* attributes=nullptr
             );
         std::vector<std::string>::size_type logout(
-            const shibsp::Application& app,
+            const Application& app,
             const opensaml::saml2md::EntityDescriptor* issuer,
             const opensaml::saml2::NameID& nameid,
             const std::set<std::string>* indexes,
@@ -95,41 +95,52 @@ namespace shibsp {
             return _logout(app, issuer, nameid, indexes, expires, sessions, 0);
         }
         bool matches(
-            const shibsp::Application& app,
+            const Application& app,
             xmltooling::HTTPRequest& request,
             const opensaml::saml2md::EntityDescriptor* issuer,
             const opensaml::saml2::NameID& nameid,
             const std::set<std::string>* indexes
             );
 #endif
-        shibsp::Session* find(const shibsp::Application& app, const char* key, const char* client_addr=nullptr, time_t* timeout=nullptr);
-        void remove(const shibsp::Application& app, const char* key);
-        void test();
+        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);
 
-        std::string active(const shibsp::Application& app, const xmltooling::HTTPRequest& request);
-        shibsp::Session* find(const shibsp::Application& app, xmltooling::HTTPRequest& request, const char* client_addr = nullptr, time_t* timeout = nullptr);
-        void remove(const shibsp::Application& app, const xmltooling::HTTPRequest& request, xmltooling::HTTPResponse* response=nullptr);
+        Session* find(const Application& app, const char* key) {
+            return _find(app, key, nullptr, nullptr, nullptr);
+        }
+        void remove(const Application& app, const char* key);
+        void test();
 
-        unsigned long getCacheTimeout(const shibsp::Application& app) const;
+        unsigned long getCacheTimeout(const Application& app) const;
 
     private:
+        // internal delegates of external methods
+        Session * _find(
+            const Application& app,
+            const char* key,
+            const char* recovery,
+            const char* client_addr,
+            time_t* timeout);
 #ifndef SHIBSP_LITE
-        // maintain back-mappings of NameID/SessionIndex -> session key
-        void insert(const char* key, time_t expires, const char* name, const char* index, short attempts=0);
         std::vector<std::string>::size_type _logout(
-            const shibsp::Application& app,
+            const Application& app,
             const opensaml::saml2md::EntityDescriptor* issuer,
             const opensaml::saml2::NameID& nameid,
             const std::set<std::string>* indexes,
             time_t expires,
             std::vector<std::string>& sessions,
             short attempts
-            );
+        );
+
+        // maintain back-mappings of NameID/SessionIndex -> session key
+        void insert(const char* key, time_t expires, const char* name, const char* index, short attempts=0);
         bool stronglyMatches(const XMLCh* idp, const XMLCh* sp, const opensaml::saml2::NameID& n1, const opensaml::saml2::NameID& n2) const;
-        shibsp::LogoutEvent* newLogoutEvent(const shibsp::Application& app) const;
+        LogoutEvent* newLogoutEvent(const Application& app) const;
 
         bool m_cacheAssertions,m_reverseIndex;
         std::set<xmltooling::xstring> m_excludedNames;
+        std::set<std::string> m_persistedAttributeIds;
 #endif
         const xercesc::DOMElement* m_root;         // Only valid during initialization
         unsigned long m_inprocTimeout,m_cacheTimeout,m_cacheAllowance;
@@ -143,6 +154,12 @@ namespace shibsp {
         void dormant(const char* key);
         static void* cleanup_fn(void*);
 
+#ifndef SHIBSP_LITE
+        // persistence across nodes
+        void persist(const Application& app, xmltooling::HTTPResponse& httpResponse, DDF& session, time_t expires) const;
+#endif
+        bool recover(const Application& app, const char* key, const char* data);
+
         xmltooling::logging::Category& m_log;
         bool inproc;
 #ifndef SHIBSP_LITE
diff --git a/shibsp/impl/StoredSession.cpp b/shibsp/impl/StoredSession.cpp
index 94fbcf6..d7d9379 100644
--- a/shibsp/impl/StoredSession.cpp
+++ b/shibsp/impl/StoredSession.cpp
@@ -225,14 +225,7 @@ void StoredSession::validate(const Application& app, const char* client_addr, ti
             in.addmember("timeout").string(timebuf);
         }
 
-        try {
-            out=app.getServiceProvider().getListenerService()->send(in);
-        }
-        catch (...) {
-            out.destroy();
-            throw;
-        }
-
+        out = app.getServiceProvider().getListenerService()->send(in);
         if (out.isstruct()) {
             // We got an updated record back.
             m_cache->m_log.debug("session updated, reconstituting it");
@@ -243,6 +236,9 @@ void StoredSession::validate(const Application& app, const char* client_addr, ti
             m_obj.destroy();
             m_obj = out;
         }
+        else {
+            out.destroy();
+        }
     }
     else {
 #ifndef SHIBSP_LITE
diff --git a/shibsp/impl/StoredSession.h b/shibsp/impl/StoredSession.h
index d5ac966..ae46ff0 100644
--- a/shibsp/impl/StoredSession.h
+++ b/shibsp/impl/StoredSession.h
@@ -52,10 +52,10 @@ namespace shibsp {
 
     class SSCache;
 
-    class StoredSession : public virtual shibsp::Session
+    class StoredSession : public virtual Session
     {
     public:
-        StoredSession(SSCache* cache, shibsp::DDF& obj);
+        StoredSession(SSCache* cache, DDF& obj);
 
         virtual ~StoredSession();
 
@@ -78,7 +78,7 @@ namespace shibsp {
             return nullptr;
         }
         void setClientAddress(const char* client_addr) {
-            shibsp::DDF obj = m_obj["client_addr"];
+            DDF obj = m_obj["client_addr"];
             if (!obj.isstruct())
                 obj = m_obj.addmember("client_addr").structure();
             obj.addmember(getAddressFamily(client_addr)).string(client_addr);
@@ -107,19 +107,19 @@ namespace shibsp {
         const char* getAuthnContextDeclRef() const {
             return m_obj["authncontext_decl"].string();
         }
-        const std::vector<shibsp::Attribute*>& getAttributes() const {
+        const std::vector<Attribute*>& getAttributes() const {
             if (m_attributes.empty())
                 unmarshallAttributes();
             return m_attributes;
         }
-        const std::multimap<std::string, const shibsp::Attribute*>& getIndexedAttributes() const;
+        const std::multimap<std::string, const Attribute*>& getIndexedAttributes() const;
 
         const std::vector<const char*>& getAssertionIDs() const;
 
-        void validate(const shibsp::Application& application, const char* client_addr, time_t* timeout);
+        void validate(const Application& application, const char* client_addr, time_t* timeout);
 
 #ifndef SHIBSP_LITE
-        void addAttributes(const std::vector<shibsp::Attribute*>& attributes);
+        void addAttributes(const std::vector<Attribute*>& attributes);
         const opensaml::Assertion* getAssertion(const char* id) const;
         void addAssertion(opensaml::Assertion* assertion);
 #endif
@@ -134,13 +134,13 @@ namespace shibsp {
     private:
         void unmarshallAttributes() const;
 
-        shibsp::DDF m_obj;
+        DDF m_obj;
 #ifndef SHIBSP_LITE
         boost::scoped_ptr<opensaml::saml2::NameID> m_nameid;
         mutable std::map< std::string,boost::shared_ptr<opensaml::Assertion> > m_tokens;
 #endif
-        mutable std::vector<shibsp::Attribute*> m_attributes;
-        mutable std::multimap<std::string,const shibsp::Attribute*> m_attributeIndex;
+        mutable std::vector<Attribute*> m_attributes;
+        mutable std::multimap<std::string,const Attribute*> m_attributeIndex;
         mutable std::vector<const char*> m_ids;
 
         SSCache* m_cache;

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


More information about the commits mailing list