[cpp-sp] branch main updated: Commit new CookieManager sources.

Scott Cantor cantor.2 at osu.edu
Mon May 5 20:28:43 UTC 2025


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository cpp-sp.

View the commit online:
http://git.shibboleth.net/view/?p=cpp-sp.git;a=commit;h=0ee4b42186cd740bcd293b49cb828801a47fa7f6

The following commit(s) were added to refs/heads/main by this push:
     new 0ee4b421 Commit new CookieManager sources.
0ee4b421 is described below

commit 0ee4b42186cd740bcd293b49cb828801a47fa7f6
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon May 5 16:28:39 2025 -0400

    Commit new CookieManager sources.
---
 shibsp/io/CookieManager.h        | 204 +++++++++++++++++++++++++++++++++++++++
 shibsp/io/impl/CookieManager.cpp | 170 ++++++++++++++++++++++++++++++++
 2 files changed, 374 insertions(+)

diff --git a/shibsp/io/CookieManager.h b/shibsp/io/CookieManager.h
new file mode 100644
index 00000000..afc0de39
--- /dev/null
+++ b/shibsp/io/CookieManager.h
@@ -0,0 +1,204 @@
+/**
+ * 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.
+ */
+
+/**
+ * @file shibsp/util/CookieManager.h
+ * 
+ * Manages reading and writing HTTP cookies.
+ */
+
+#ifndef __shibsp_cookiemgr_h__
+#define __shibsp_cookiemgr_h__
+
+#include <shibsp/base.h>
+
+#include <map>
+#include <string>
+
+namespace shibsp {
+
+    class SHIBSP_API SPRequest;
+
+#if defined (_MSC_VER)
+    #pragma warning( push )
+    #pragma warning( disable : 4251 )
+#endif
+
+    /**
+     * HTTP cookie reading and writing helper object.
+     * 
+     * <p>This is a thread-safe object that encapsulates some of the older SP's messy handling
+     * of cookie names and properties in various use cases. The name of the cookie managed is
+     * determined by combining various parameters together with the RequestMapper settings
+     * associated with the request provided to the mutating methods.</p>
+     * 
+     * <p>The only required input for use of the object is the default cookie name, input
+     * to the constructor. The other settings default to absent, and the default max-age to
+     * -1, indicating a per-session cookie (which in practice are semi-permanent now).</p>
+     */
+    class SHIBSP_API CookieManager
+    {
+        MAKE_NONCOPYABLE(CookieManager);
+    public:
+        /**
+         * Constructor.
+         * 
+         * @param default basis for cookie name
+         */
+        CookieManager(const char* defaultName);
+
+        /**
+         * Destructor.
+         */
+        ~CookieManager();
+
+        /** Cookie SameSite values. */
+        enum samesite_t {
+            SAMESITE_ABSENT = 0,
+            SAMESITE_NONE = 1,
+            SAMESITE_LAX = 2,
+            SAMESITE_STRICT = 3
+        };
+
+        /**
+         * Installs controls governing the generation of the name of the cookie managed by this
+         * object.
+         * 
+         * <p>If the override property is specified and is present in the request's content
+         * settings, then it will stipulate the exact cookie name to use.</p>
+         * 
+         * <p>If no override property is specified, or if the named property is not present in
+         * the request's content settings, then the default name will be used unless modified by
+         * the applicationSpecific flag. If set, the default name will be decorated by adding
+         * a suffix generated from the agent and applicable "applicationId" setting for the request.</p>
+         * 
+         * @param overrideProperty name of request/content setting that if present will override
+         *      the default cookoe name
+         * @param applicationSpecific whether the default cookie name, if not overridden, should be
+         *      decorated so as to be unique to the agent and request's applicationId setting
+         */
+        void setCookieNamePolicy(const char* overrideProperty=nullptr, bool applicationSpecific=false);
+
+        /**
+         * Sets the path attribute for cookies created by this object.
+         * 
+         * <p>Defaults to "/".</p>
+         * 
+         * @param path path to set
+         */
+        void setPath(const char* path);
+
+        /**
+         * Sets the domain attribute for cookies created by this object.
+         * 
+         * <p>Defaults to absent.</p>
+         * 
+         * @param domain domain to set
+         */
+        void setDomain(const char* domain);
+
+        /**
+         * Sets the default max-age for cookies created by this object.
+         * 
+         * <p>Defaults to -1.</p>
+         * 
+         * @param maxAge max-age value
+         */
+        void setMaxAge(int maxAge);
+
+        /**
+         * Sets default Secure attribute for cookies created by this object.
+         * 
+         * <p>Defaults to true.</p>
+         * 
+         * @param secure value of attribute
+         */
+        void setSecure(bool secure);
+
+        /**
+         * Sets default HttpOnly attribute for cookies created by this object.
+         * 
+         * <p>Defaults to true.</p>
+         * 
+         * @param httpOnly value of attribute
+         */
+        void setHttpOnly(bool httpOnly);
+
+        /**
+         * Sets default SameSite attribute for cookies created by this object.
+         * 
+         * <p>Defaults to absent.</p>
+         * 
+         * @param sameSiteValue attribute value
+         */
+        void setSameSite(samesite_t sameSiteValue);
+
+        /**
+         * Adds a cookie with the specified value to the outgoing response for the supplied request.
+         * 
+         * <p>The name is determined per the settings configured on this object and the settings
+         * associated with the request.</p>
+         * 
+         * <p>The value will NOT be encoded by this method and must be encoded by the caller
+         * if necessary.</p>
+         * 
+         * @param request the request to operate on
+         * @param value cookie value
+         */
+        void setCookie(SPRequest& request, const char* value) const;
+
+        /**
+         * Unsets a cookie via the outgoing response for the supplied request.
+         * 
+         * <p>The name is determined per the settings configured on this object and the settings
+         * associated with the request.</p>
+         * 
+         * @param request the request to operate on
+         */
+        void unsetCookie(SPRequest& request) const;
+
+        /**
+         * Gets the value (if any) for the specified cookie.
+         * 
+         * <p>If multiple cookies of the same name are present, it is unspecified
+         * which value will be returned.</p>
+         * 
+         * @param request the request to operate on
+         * 
+         * @return a value for the named cookie, or nullptr if none exists
+         */
+        const char* getCookieValue(const SPRequest& request) const;
+        
+    private:
+        std::string computeCookieName(const SPRequest& request) const;
+        void outputHeader(SPRequest& request, int maxAge) const;
+
+        std::string m_defaultName;
+        std::string m_overrideProperty;
+        bool m_appSpecific;
+        std::string m_path;
+        std::string m_domain;
+        int m_maxAge;
+        bool m_secure;
+        bool m_httpOnly;
+        samesite_t m_sameSiteValue;
+    };
+
+#if defined (_MSC_VER)
+    #pragma warning( pop )
+#endif
+
+};
+
+#endif /* __shibsp_cookiemgr_h__ */
diff --git a/shibsp/io/impl/CookieManager.cpp b/shibsp/io/impl/CookieManager.cpp
new file mode 100644
index 00000000..a1f18080
--- /dev/null
+++ b/shibsp/io/impl/CookieManager.cpp
@@ -0,0 +1,170 @@
+/**
+ * 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.
+ */
+
+/**
+ * io/impl/CookieManager.cpp
+ * 
+ * Manages reading and writing HTTP cookies.
+ */
+
+#include "internal.h"
+
+#include "Agent.h"
+#include "SPRequest.h"
+#include "RequestMapper.h"
+#include "io/CookieManager.h"
+#include "util/PropertySet.h"
+
+#include <boost/lexical_cast.hpp>>
+
+using namespace shibsp;
+using namespace std;
+
+CookieManager::CookieManager(const char* defaultName)
+    : m_defaultName(defaultName),
+        m_overrideProperty(nullptr),
+        m_appSpecific(false),
+        m_path("/"),
+        m_maxAge(-1),
+        m_secure(true),
+        m_httpOnly(true),
+        m_sameSiteValue(SAMESITE_ABSENT)
+{
+}
+
+CookieManager::~CookieManager()
+{
+}
+
+void CookieManager::setCookieNamePolicy(const char* overridePropertyName, bool appSpecific)
+{
+    m_overrideProperty = overridePropertyName ? overridePropertyName : nullptr;
+    m_appSpecific = appSpecific;
+}
+
+void CookieManager::setPath(const char* path)
+{
+    m_path = path ? path : "";
+}
+
+void CookieManager::setDomain(const char* domain)
+{
+    m_domain = domain ? domain : "";
+}
+
+void CookieManager::setMaxAge(int maxAge)
+{
+    m_maxAge = maxAge;
+}
+
+void CookieManager::setSecure(bool secure)
+{
+    m_secure = secure;
+}
+
+void CookieManager::setHttpOnly(bool httpOnly)
+{
+    m_httpOnly = httpOnly;
+}
+
+void CookieManager::setSameSite(samesite_t value)
+{
+    m_sameSiteValue = value;
+}
+
+string CookieManager::computeCookieName(const SPRequest& request) const
+{
+    // If not app-specific, return the overridden or default name unadorned.
+    if (!m_appSpecific) {
+        if (m_overrideProperty.empty()) {
+            return m_defaultName;
+        }
+        const char* overridden = request.getRequestSettings().first->getString(m_overrideProperty.c_str());
+        return overridden ? string(overridden) : m_defaultName;
+    }
+
+    // Otherwise, the base name is the default or the overridden name.
+    string cookieName(request.getRequestSettings().first->getString(m_overrideProperty.c_str(), m_defaultName.c_str()));
+
+    // This is just a hex-encode to avoid a dependency on a hashing API.
+    static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+    string encode(request.getAgent().getID());
+    encode += request.getRequestSettings().first->getString("applicationId", "default");
+    cookieName += '_';
+    for (const char* ch = encode.c_str(); *ch; ++ch) {
+        cookieName += (DIGITS[((unsigned char)(0xF0 & *ch)) >> 4 ]);
+        cookieName += (DIGITS[0x0F & *ch]);
+    }
+    
+    return cookieName;
+}
+
+void CookieManager::outputHeader(SPRequest& request, int maxAge) const
+{
+    string header(computeCookieName(request));
+    header += "; max-age=";
+    try {
+        header += boost::lexical_cast<string>(maxAge);
+    }
+    catch (boost::bad_lexical_cast&) {
+        header += "-1";
+    }
+    if (!m_path.empty()) {
+        header += "; path=";
+        header += m_path;
+    }
+    if (!m_domain.empty()) {
+        header += "; domain=";
+        header += m_domain;
+    }
+    if (m_secure) {
+        header += "; secure=1";
+    }
+    if (m_httpOnly) {
+        header += "; HttpOnly=1";
+    }
+    if (m_sameSiteValue != SAMESITE_ABSENT) {
+        switch (m_sameSiteValue) {
+            case SAMESITE_NONE:
+                header += "; SameSite=None";
+                break;
+            case SAMESITE_LAX:
+                header += "; SameSite=Lax";
+                break;
+            case SAMESITE_STRICT:
+                header += "; SameSite=Strict";
+                break;
+            default:
+        }
+    }
+
+    request.setResponseHeader("Set-Cookie", header.c_str());
+}
+
+const char* CookieManager::getCookieValue(const SPRequest& request) const
+{
+    const auto& cookies = request.getCookies();
+    const auto& entry = cookies.find(computeCookieName(request));
+    return entry == cookies.end() ? nullptr : entry->second.c_str();
+}
+
+void CookieManager::setCookie(SPRequest& request, const char* value) const
+{
+    outputHeader(request, request.getRequestSettings().first->getInt("cookieMaxAge", m_maxAge));
+}
+
+void CookieManager::unsetCookie(SPRequest& request) const
+{
+    outputHeader(request, 0);
+}

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


More information about the commits mailing list