[cpp-sp] branch main updated: WIP on SS cache impl.

Codeberg noreply at shibboleth.net
Mon Nov 24 15:56:02 UTC 2025


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/ecb6bfbac932e9f48d3a9e23a1ae544964677a6c

The following commit(s) were added to refs/heads/main by this push:
     new ecb6bfba WIP on SS cache impl.
ecb6bfba is described below

commit ecb6bfbac932e9f48d3a9e23a1ae544964677a6c
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Nov 24 10:55:48 2025 -0500

    WIP on SS cache impl.
---
 shibsp/session/impl/StorageServiceSessionCache.cpp | 278 +++++++++++++++++++++
 1 file changed, 278 insertions(+)

diff --git a/shibsp/session/impl/StorageServiceSessionCache.cpp b/shibsp/session/impl/StorageServiceSessionCache.cpp
new file mode 100644
index 00000000..7769a35d
--- /dev/null
+++ b/shibsp/session/impl/StorageServiceSessionCache.cpp
@@ -0,0 +1,278 @@
+/**
+ * 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.
+ */
+
+/**
+ * session/impl/StorageServiceSessionCache.cpp
+ *
+ * SessionCache implementation using remoted access to StorageService via Hub.
+ * 
+ * <p>This is not a heavily optimized implementation at present, as there are
+ * definitely more round trips than would be possible with additional purpose-
+ * specific work in various places in the Hub to optimize the semantics of
+ * some of the operations.</p>
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "Agent.h"
+#include "AgentConfig.h"
+#include "csprng/csprng.hpp"
+#include "remoting/RemotingService.h"
+#include "session/AbstractSessionCache.h"
+#include "logging/Category.h"
+#include "util/Date.h"
+#include "util/Misc.h"
+
+#include <boost/property_tree/ptree.hpp>
+
+using namespace shibsp;
+using namespace boost::property_tree;
+using namespace std;
+
+namespace {
+    class StorageServiceSessionCache : public virtual AbstractSessionCache {
+    public:
+        StorageServiceSessionCache(const ptree& pt);
+        ~StorageServiceSessionCache();
+
+        string cache_create(SPRequest* request, DDF& sessionData);
+        DDF cache_read(
+            SPRequest* request,
+            const char* applicationId,
+            const char* key,
+            unsigned int version=1,
+            unsigned int lifetime=0,
+            unsigned int timeout=0,
+            const char* client_addr=nullptr
+            );
+        bool cache_update(SPRequest* request, const char* key, unsigned int version, DDF& data);
+        bool cache_touch(SPRequest* request, const char* key, unsigned int version=1, unsigned int timeout=0);
+        void cache_remove(SPRequest* request, const char* key);
+    
+    private:
+        Category& m_spilog;
+        duthomhas::csprng m_rng;
+        unsigned int m_storageTimeout;
+    };
+
+    static const char STORAGE_TIMEOUT_PROP_NAME[] = "storageTimeout";
+
+    static unsigned int STORAGE_TIMEOUT_PROP_DEFAULT = 3600 * 2;
+};
+
+namespace shibsp {
+    SessionCache* SHIBSP_DLLLOCAL StorageServiceSessionCacheFactory(ptree& pt, bool deprecationSupport) {
+        return new StorageServiceSessionCache(pt);
+    }
+}
+
+StorageServiceSessionCache::StorageServiceSessionCache(const ptree& pt)
+    : AbstractSessionCache(pt), m_spilog(Category::getInstance(SHIBSP_LOGCAT ".SessionCache.StorageService"))
+{
+    m_storageTimeout = getUnsignedInt(STORAGE_TIMEOUT_PROP_NAME, STORAGE_TIMEOUT_PROP_DEFAULT);
+}
+
+StorageServiceSessionCache::~StorageServiceSessionCache()
+{
+}
+
+string StorageServiceSessionCache::cache_create(SPRequest* request, DDF& sessionData)
+{
+    DDF in("storage");
+    DDFJanitor injanitor(in);
+
+    ostringstream sink;
+    sink << sessionData;
+
+    in.addmember("op").string("C");
+    in.addmember("context").string("sessions");
+    in.addmember("value").string(sink.str());
+    in.addmember("exp").longinteger(time(nullptr) + m_storageTimeout);
+
+    // Extremely unlikely to collide but we try 3 times just in case.
+
+    int attempts = 0;
+    do {
+        string key = hex_encode(m_rng(string(16,0)));
+        in.addmember("key").string(key);
+
+        try {
+            DDF out = AgentConfig::getConfig().getAgent().getRemotingService()->send(in);
+            out.destroy();
+            return key;
+        }
+        catch (const OperationException& e) {
+            const char* event = e.getProperty(AgentException::EVENT_PROP_NAME);
+            if (!event || strcmp(event, "DuplicateRecord")) {
+                m_spilog.error("failure storing session via Hub: event (%s): %s",
+                    event ? event : "null", e.what());
+                throw;
+            }
+        }
+        catch (const exception& e) {
+            m_spilog.error("exception attempting to store session via hub: %s", e.what());
+            throw;
+        }
+
+    } while (++attempts < 3);
+
+    m_spilog.error("failed to store new session via Hub after 3 attempts to generate a unique key");
+    throw IOException("Exhausted attempts to generate a unique session key.");
+}
+
+/*
+DDF StorageServiceSessionCache::cache_read(
+    SPRequest* request,
+    const char* applicationId,
+    const char* key,
+    unsigned int version,
+    unsigned int lifetime,
+    unsigned int timeout,
+    const char* client_addr
+    )
+{
+    auto entry = m_storage.find(key);
+    if (entry == m_storage.end()) {
+        m_lock.unlock();
+        return DDF();
+    }
+
+    time_t now = time(nullptr);
+
+    if (timeout ) {
+        if (entry->second.second + timeout < now) {
+            if (m_spilog.isInfoEnabled()) {
+                string ts(date::format("%FT%TZ", chrono::system_clock::from_time_t(entry->second.second)));
+                m_spilog.info("session (%s) expired for inactivity, timeout (%lu), last access (%s)", key, timeout, ts.c_str());
+            }
+            cache_remove(request, key);
+            return DDF();
+        }
+    }
+
+    const char* appId = entry->second.first["app_id"].string();
+    if (strcmp(applicationId, appId)) {
+        m_spilog.warn("session (%s) issued for application (%s), accessed via application (%s)", key, appId, applicationId);
+        return DDF();
+    }
+
+    if (lifetime) {
+        time_t start = entry->second.first["ts"].longinteger();
+        if (start + lifetime < now) {
+            if (m_spilog.isInfoEnabled()) {
+                string created(date::format("%FT%TZ", chrono::system_clock::from_time_t(start)));
+                string expired(date::format("%FT%TZ", chrono::system_clock::from_time_t(start + lifetime)));
+                m_spilog.info("session (%s) has expired, created (%s), expired (%s)", key, created.c_str(), expired.c_str());
+            }
+            cache_remove(request, key);
+            return DDF();
+        }
+    }
+
+    if (client_addr) {
+        const char* family = getAddressFamily(client_addr);
+        const char* addr = entry->second.first[family].string();
+        if (addr) {
+            if (!isAddressMatch(client_addr, addr)) {
+                m_spilog.info("session (%s) use invalid, bound to address (%s), accessed from (%s)", key, addr, client_addr);
+                return DDF();
+            }
+        }
+        else {
+            // We have to rebind the session to a new address family, requiring an update to the session.
+            m_spilog.info("attempting update of session (%s) to rebind to new address (%s)", key, client_addr);
+
+            // Fill in the new address and attempt the update.
+            entry->second.first.addmember(family).string(client_addr);
+            unsigned int oldver = entry->second.first.getmember("ver").integer();
+            entry->second.first.addmember("ver").integer(oldver == 0 ? 2 : oldver + 1);
+        }
+    }
+
+    entry->second.second = now;
+    return entry->second.first.copy();
+}
+
+bool StorageServiceSessionCache::cache_touch(SPRequest* request, const char* key, unsigned int version, unsigned int timeout)
+{
+    auto entry = m_storage.find(key);
+    if (entry != m_storage.end()) {
+
+        time_t now = time(nullptr);
+
+        if (timeout && entry->second.second + timeout < now) {
+            if (m_spilog.isInfoEnabled()) {
+                string ts(date::format("%FT%TZ", chrono::system_clock::from_time_t(entry->second.second)));
+                m_spilog.info("session (%s) expired for inactivity, timeout (%lu), last access (%s)", key, timeout, ts.c_str());
+            }
+            m_storage.erase(entry);
+            return false;
+        }
+
+        entry->second.second = now;
+        return true;
+    }
+
+    return false;
+}
+
+bool StorageServiceSessionCache::cache_update(SPRequest* request, const char* key, unsigned int version, DDF& data)
+{
+    auto entry = m_storage.find(key);
+    if (entry == m_storage.end()) {
+        return false;
+    }
+
+    unsigned int oldver = entry->second.first.getmember("ver").integer();
+    if (oldver == 0) {
+        oldver = 1;
+    }
+
+    if (version != oldver) {
+        return false;
+    }
+
+    data.addmember("ver").integer(++version);
+    entry->second.first.destroy();
+    entry->second.first = data.copy();
+    entry->second.second = time(nullptr);
+    return true;
+}
+*/
+
+void StorageServiceSessionCache::cache_remove(SPRequest* request, const char* key)
+{
+    DDF in("storage");
+    DDFJanitor injanitor(in);
+
+    in.addmember("op").string("D");
+    in.addmember("context").string("sessions");
+    in.addmember("key").string(key);
+
+    try {
+        DDF out = AgentConfig::getConfig().getAgent().getRemotingService()->send(in);
+        out.destroy();
+    }
+    catch (const OperationException& e) {
+        const char* event = e.getProperty(AgentException::EVENT_PROP_NAME);
+        m_spilog.error("failure deleting session via Hub: event (%s): %s", event ? event : "null", e.what());
+        throw;
+    }
+    catch (const exception& e) {
+        m_spilog.error("exception attempting to delete session via hub: %s", e.what());
+        throw;
+    }
+
+    m_spilog.debug("removed session from storage (%s)", key);
+}

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


More information about the commits mailing list