Combined SAML versions in one session API, implemented in-process half of cache.
authorcantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 22 Jan 2007 06:41:44 +0000 (06:41 +0000)
committercantor <cantor@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 22 Jan 2007 06:41:44 +0000 (06:41 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@2139 cb58f699-b61c-0410-a6fe-9272a202ed29

shibsp/Makefile.am
shibsp/SessionCache.cpp [new file with mode: 0644]
shibsp/SessionCache.h
shibsp/impl/RemotedSessionCache.cpp [new file with mode: 0644]
shibsp/impl/StorageServiceSessionCache.cpp
shibsp/shibsp.vcproj

index 6975ad6..ecede0e 100644 (file)
@@ -64,7 +64,9 @@ libshibsp_la_SOURCES = \
        AbstractSPRequest.cpp \
        Application.cpp \
        ServiceProvider.cpp \
+       SessionCache.cpp \
        SPConfig.cpp \
+       impl/RemotedSessionCache.cpp \
        impl/StorageServiceSessionCache.cpp \
        impl/XMLAccessControl.cpp \
        impl/XMLRequestMapper.cpp \
diff --git a/shibsp/SessionCache.cpp b/shibsp/SessionCache.cpp
new file mode 100644 (file)
index 0000000..3673c1d
--- /dev/null
@@ -0,0 +1,73 @@
+/*\r
+ *  Copyright 2001-2005 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * SessionCache.cpp\r
+ * \r
+ * SessionCache base class and factory registration\r
+ */\r
+\r
+#include "internal.h"\r
+#include "SessionCache.h"\r
+\r
+using namespace shibsp;\r
+using namespace xmltooling;\r
+\r
+namespace shibsp {\r
+\r
+    SHIBSP_DLLLOCAL PluginManager<SessionCache,const DOMElement*>::Factory RemotedCacheFactory;\r
+    SHIBSP_DLLLOCAL PluginManager<SessionCache,const DOMElement*>::Factory StorageServiceCacheFactory;\r
+\r
+    static const XMLCh cleanupInterval[] =  UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);\r
+    static const XMLCh cacheTimeout[] =     UNICODE_LITERAL_12(c,a,c,h,e,T,i,m,e,o,u,t);\r
+    static const XMLCh strictValidity[] =   UNICODE_LITERAL_14(s,t,r,i,c,t,V,a,l,i,d,i,t,y);\r
+    static const XMLCh writeThrough[] =     UNICODE_LITERAL_12(w,r,i,t,e,T,h,r,o,u,g,h);\r
+}\r
+\r
+void SHIBSP_API shibsp::registerSessionCaches()\r
+{\r
+    SPConfig& conf = SPConfig::getConfig();\r
+    conf.SessionCacheManager.registerFactory(REMOTED_SESSION_CACHE, RemotedCacheFactory);\r
+    conf.SessionCacheManager.registerFactory(STORAGESERVICE_SESSION_CACHE, StorageServiceCacheFactory);\r
+}\r
+\r
+SessionCache::SessionCache(const DOMElement* e)\r
+    : m_cleanupInterval(60*5), m_cacheTimeout(60*60*8), m_strictValidity(true), m_writeThrough(false)\r
+{\r
+    if (e) {\r
+        const XMLCh* tag=e->getAttributeNS(NULL,cleanupInterval);\r
+        if (tag && *tag) {\r
+            m_cleanupInterval = XMLString::parseInt(tag);\r
+            if (!m_cleanupInterval)\r
+                m_cleanupInterval=60*5;\r
+        }\r
+\r
+        tag=e->getAttributeNS(NULL,cacheTimeout);\r
+        if (tag && *tag) {\r
+            m_cacheTimeout = XMLString::parseInt(tag);\r
+            if (!m_cacheTimeout)\r
+                m_cacheTimeout=60*60*8;\r
+        }\r
+        \r
+        tag=e->getAttributeNS(NULL,strictValidity);\r
+        if (tag && (*tag==chDigit_0 || *tag==chLatin_f))\r
+            m_strictValidity=false;\r
+            \r
+        tag=e->getAttributeNS(NULL,writeThrough);\r
+        if (tag && (*tag==chDigit_1 || *tag==chLatin_t))\r
+            m_writeThrough=true;\r
+    }\r
+}\r
index ac531cf..b1cda79 100644 (file)
@@ -60,6 +60,38 @@ namespace shibsp {
          * @return  the authentication timestamp 
          */
         virtual time_t getAuthnInstant() const=0;
+
+        /**
+         * Returns the NameID associated with a session.
+         * 
+         * <p>SAML 1.x identifiers will be promoted to the 2.0 type.
+         * 
+         * @return reference to a SAML 2.0 NameID
+         */
+        virtual const opensaml::saml2::NameID& getNameID() const=0;
+
+        /**
+         * Returns the SessionIndex provided with the session.
+         * 
+         * @return the SessionIndex from the original SSO assertion, if any
+         */
+        virtual const char* getSessionIndex() const=0;
+
+        /**
+         * Returns a URI containing an AuthnContextClassRef provided with the session.
+         * 
+         * <p>SAML 1.x AuthenticationMethods will be returned as class references.
+         * 
+         * @return  a URI identifying the authentication context class
+         */
+        virtual const char* getAuthnContextClassRef() const=0;
+
+        /**
+         * Returns a URI containing an AuthnContextDeclRef provided with the session.
+         * 
+         * @return  a URI identifying the authentication context declaration
+         */
+        virtual const char* getAuthnContextDeclRef() const=0;
         
         /**
          * Returns the set of resolved attributes associated with the session.
@@ -100,66 +132,6 @@ namespace shibsp {
         virtual void addAssertion(opensaml::RootObject* assertion)=0;        
     };
     
-    class SHIBSP_API SAML1Session : public virtual Session
-    {
-    protected:
-        SAML1Session() {}
-        virtual ~SAML1Session() {}
-        
-    public:        
-        /**
-         * Returns the NameIdentifier associated with a SAML 1.x session.
-         * 
-         * @return reference to a SAML 1.x NameIdentifier
-         */
-        virtual const opensaml::saml1::NameIdentifier& getNameIdentifier() const=0;
-
-        /**
-         * Returns a URI containing the AuthenticationMethod.
-         * 
-         * @return  a URI identifying the authentication method
-         */
-        virtual const char* getAuthenticationMethod() const=0;
-
-    };
-
-    class SHIBSP_API SAML2Session : public virtual Session
-    {
-    protected:
-        SAML2Session() {}
-        virtual ~SAML2Session() {}
-        
-    public:        
-        /**
-         * Returns the NameID associated with a SAML 2.0 session.
-         * 
-         * @return reference to a SAML 2.0 NameID
-         */
-        virtual const opensaml::saml2::NameID& getNameID() const=0;
-
-        /**
-         * Returns the SessionIndex provided with the session.
-         * 
-         * @return the SessionIndex from the original SSO assertion, if any
-         */
-        virtual const char* getSessionIndex() const=0;
-
-        /**
-         * Returns a URI containing an AuthnContextClassRef provided with the session.
-         * 
-         * @return  a URI identifying the authentication context class
-         */
-        virtual const char* getAuthnContextClassRef() const=0;
-
-        /**
-         * Returns a URI containing an AuthnContextDeclRef provided with the session.
-         * 
-         * @return  a URI identifying the authentication context declaration
-         */
-        virtual const char* getAuthnContextDeclRef() const=0;
-
-    };
-    
     /**
      * Creates and manages user sessions
      * 
@@ -193,6 +165,18 @@ namespace shibsp {
          */
         SessionCache(const DOMElement* e);
         
+        /** maximum lifetime in seconds for sessions */
+        unsigned long m_cacheTimeout;
+        
+        /** interval in seconds between attempts to purge expired sessions */
+        unsigned long m_cleanupInterval;
+        
+        /** whether to honor SessionNotOnOrAfter information */
+        bool m_strictValidity;
+        
+        /** whether every session access should update persistent storage */
+        bool m_writeThrough;
+        
     public:
         virtual ~SessionCache() {}
         
@@ -205,21 +189,25 @@ namespace shibsp {
          * @param application   reference to Application that owns the Session
          * @param client_addr   network address of client
          * @param ssoToken      reference to SSO assertion initiating the session
-         * @param issuer        issuing metadata role of assertion issuer, if known
+         * @param issuer        issuing metadata of assertion issuer, if known
          * @param attributes    optional set of resolved Attributes to cache with session
-         * @return  pointer to newly created (and locked) Session
+         * @return  newly created session's key
          */
-        virtual Session* insert(
+        virtual std::string insert(
             const Application& application,
             const char* client_addr,
             const opensaml::RootObject& ssoToken,
-            const opensaml::saml2md::RoleDescriptor* issuer=NULL,
+            const opensaml::saml2md::EntityDescriptor* issuer=NULL,
             const std::vector<Attribute*>* attributes=NULL
             )=0;
 
         /**
          * Locates an existing session.
          * 
+         * <p>If "writeThrough" is configured, then every attempt to locate a session
+         * requires that the record be updated in persistent storage to reflect the fact
+         * that it was accessed (to maintain timeout information).
+         * 
          * @param key           session key
          * @param application   reference to Application that owns the Session
          * @param client_addr   network address of client (if known)
@@ -237,7 +225,10 @@ namespace shibsp {
         virtual void remove(const char* key, const Application& application, const char* client_addr)=0;
     };
 
-    /** Remoting-aware SessionCache implementation backed by a StorageService. */
+    /** SessionCache implementation that delegates to a remoted version. */
+    #define REMOTED_SESSION_CACHE    "edu.internet2.middleware.shibboleth.sp.provider.RemotedSessionCache"
+
+    /** SessionCache implementation backed by a StorageService. */
     #define STORAGESERVICE_SESSION_CACHE    "edu.internet2.middleware.shibboleth.sp.provider.StorageServiceSessionCache"
 
     /**
diff --git a/shibsp/impl/RemotedSessionCache.cpp b/shibsp/impl/RemotedSessionCache.cpp
new file mode 100644 (file)
index 0000000..a858ecc
--- /dev/null
@@ -0,0 +1,305 @@
+/*\r
+ *  Copyright 2001-2005 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * RemotedSessionCache.cpp\r
+ * \r
+ * SessionCache implementation that delegates to a remoted version.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "Application.h"\r
+#include "exceptions.h"\r
+#include "ServiceProvider.h"\r
+#include "SessionCache.h"\r
+#include "attribute/Attribute.h"\r
+#include "remoting/ListenerService.h"\r
+#include "util/SPConstants.h"\r
+\r
+#include <log4cpp/Category.hh>\r
+#include <xmltooling/XMLToolingConfig.h>\r
+#include <xmltooling/util/NDC.h>\r
+#include <xmltooling/util/XMLHelper.h>\r
+\r
+using namespace shibsp;\r
+using namespace opensaml::saml2md;\r
+using namespace opensaml;\r
+using namespace xmltooling;\r
+using namespace log4cpp;\r
+using namespace std;\r
+\r
+namespace shibsp {\r
+\r
+    class RemotedSession : public virtual Session\r
+    {\r
+    public:\r
+        RemotedSession(const char* key, DDF& obj) : m_key(key), m_obj(obj), m_nameid(NULL) {\r
+            const char* nameid = obj["nameid"].string();\r
+            if (!nameid)\r
+                throw FatalProfileException("NameID missing from remotely cached session.");\r
+            \r
+            // Parse and bind the document into an XMLObject.\r
+            istringstream instr(nameid);\r
+            DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(instr); \r
+            XercesJanitor<DOMDocument> janitor(doc);\r
+            auto_ptr<saml2::NameID> n(saml2::NameIDBuilder::buildNameID());\r
+            n->unmarshall(doc->getDocumentElement(), true);\r
+            janitor.release();\r
+            \r
+            // TODO: Process attributes...\r
+\r
+            m_nameid = n.release();\r
+        }\r
+        \r
+        ~RemotedSession() {\r
+            m_obj.destroy();\r
+            delete m_nameid;\r
+            for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
+            for_each(m_tokens.begin(), m_tokens.end(), xmltooling::cleanup<RootObject>());\r
+        }\r
+        \r
+        Lockable* lock() {\r
+            return this;\r
+        }\r
+        void unlock() {\r
+            delete this;\r
+        }\r
+        \r
+        const char* getClientAddress() const {\r
+            return m_obj["client_address"].string();\r
+        }\r
+        const char* getEntityID() const {\r
+            return m_obj["entity_id"].string();\r
+        }\r
+        time_t getAuthnInstant() const {\r
+            return m_obj["authn_instant"].integer();\r
+        }\r
+        const opensaml::saml2::NameID& getNameID() const {\r
+            return *m_nameid;\r
+        }\r
+        const char* getSessionIndex() const {\r
+            return m_obj["session_index"].string();\r
+        }\r
+        const char* getAuthnContextClassRef() const {\r
+            return m_obj["authncontext_class"].string();\r
+        }\r
+        const char* getAuthnContextDeclRef() const {\r
+            return m_obj["authncontext_decl"].string();\r
+        }\r
+        const vector<const Attribute*>& getAttributes() const {\r
+            return m_attributes;\r
+        }\r
+        const vector<const char*>& getAssertionIDs() const {\r
+            if (m_ids.empty()) {\r
+                DDF id = m_obj["assertion_ids"].first();\r
+                while (id.isstring()) {\r
+                    m_ids.push_back(id.string());\r
+                    id = id.next();\r
+                }\r
+            }\r
+            return m_ids;\r
+        }\r
+        \r
+        void addAttributes(const vector<Attribute*>& attributes);\r
+        const RootObject* getAssertion(const char* id) const;\r
+        void addAssertion(RootObject* assertion);\r
+\r
+    protected:\r
+        string m_key;\r
+        mutable DDF m_obj;\r
+        saml2::NameID* m_nameid;\r
+        vector<const Attribute*> m_attributes;\r
+        mutable vector<const char*> m_ids;\r
+        mutable vector<RootObject*> m_tokens;\r
+    };\r
+    \r
+    class RemotedCache : public SessionCache\r
+    {\r
+    public:\r
+        RemotedCache(const DOMElement* e);\r
+    \r
+        string insert(\r
+            const Application& application,\r
+            const char* client_addr,\r
+            const RootObject& ssoToken,\r
+            const EntityDescriptor* issuer=NULL,\r
+            const vector<Attribute*>* attributes=NULL\r
+            );\r
+        Session* find(const char* key, const Application& application, const char* client_addr);\r
+        void remove(const char* key, const Application& application, const char* client_addr);\r
+    };\r
+\r
+    SessionCache* SHIBSP_DLLLOCAL RemotedCacheFactory(const DOMElement* const & e)\r
+    {\r
+        return NULL;\r
+    }\r
+}\r
+\r
+void RemotedSession::addAttributes(const vector<Attribute*>& attributes)\r
+{\r
+    DDF in("addAttributes::"REMOTED_SESSION_CACHE);\r
+    DDFJanitor jin(in);\r
+    in.structure();\r
+    in.addmember("key").string(m_key.c_str());\r
+\r
+    DDF attr;\r
+    DDF attrs = in.addmember("attributes").list();\r
+    for (vector<Attribute*>::const_iterator a=attributes.begin(); a!=attributes.end(); ++a) {\r
+        attr = (*a)->marshall();\r
+        attrs.add(attr);\r
+    }\r
+\r
+    attr=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+    DDFJanitor jout(attr);\r
+    \r
+    // Transfer ownership to us.\r
+    m_attributes.insert(m_attributes.end(), attributes.begin(), attributes.end());\r
+}\r
+\r
+const RootObject* RemotedSession::getAssertion(const char* id) const\r
+{\r
+    DDF in("getAssertion::"REMOTED_SESSION_CACHE);\r
+    DDFJanitor jin(in);\r
+    in.structure();\r
+    in.addmember("key").string(m_key.c_str());\r
+    in.addmember("assertion_id").string(id);\r
+\r
+    DDF out = SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+    DDFJanitor jout(out);\r
+    \r
+    const char* tokenstr = out["assertion"].string();\r
+    if (!tokenstr)\r
+        return NULL;\r
+    \r
+    // Parse and bind the document into an XMLObject.\r
+    istringstream instr(tokenstr);\r
+    DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(instr); \r
+    XercesJanitor<DOMDocument> janitor(doc);\r
+    auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(doc->getDocumentElement(), true));\r
+    janitor.release();\r
+    \r
+    RootObject* token = dynamic_cast<RootObject*>(xmlObject.get());\r
+    if (!token || !token->isAssertion())\r
+        throw FatalProfileException("Remoted call for cached assertion returned an unknown object type.");\r
+\r
+    // Transfer ownership to us.\r
+    xmlObject.release();\r
+    m_tokens.push_back(token);\r
+    return token;\r
+}\r
+\r
+void RemotedSession::addAssertion(RootObject* assertion)\r
+{\r
+    if (!assertion || !assertion->isAssertion())\r
+        throw FatalProfileException("Unknown object type passed to session cache for storage.");\r
+\r
+    DDF in("addAssertion::"REMOTED_SESSION_CACHE);\r
+    DDFJanitor jin(in);\r
+    in.structure();\r
+    in.addmember("key").string(m_key.c_str());\r
+    \r
+    ostringstream os;\r
+    os << *assertion;\r
+    in.addmember("assertion").string(os.str().c_str());\r
+\r
+    DDF out = SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+    out.destroy();\r
+    delete assertion;\r
+}\r
+\r
+RemotedCache::RemotedCache(const DOMElement* e) : SessionCache(e)\r
+{\r
+    if (!SPConfig::getConfig().getServiceProvider()->getListenerService())\r
+        throw ConfigurationException("RemotedCacheService requires a ListenerService, but none available.");\r
+}\r
+\r
+string RemotedCache::insert(\r
+    const Application& application,\r
+    const char* client_addr,\r
+    const RootObject& ssoToken,\r
+    const EntityDescriptor* issuer,\r
+    const vector<Attribute*>* attributes\r
+    )\r
+{\r
+    DDF in("insert::"REMOTED_SESSION_CACHE);\r
+    DDFJanitor jin(in);\r
+    in.structure();\r
+    in.addmember("application_id").string(application.getId());\r
+    in.addmember("client_address").string(client_addr);\r
+    if (issuer) {\r
+        auto_ptr_char provid(issuer->getEntityID());\r
+        in.addmember("entity_id").string(provid.get());\r
+    }\r
+    \r
+    ostringstream os;\r
+    os << ssoToken;\r
+    in.addmember("token").string(os.str().c_str());\r
+    \r
+    if (attributes) {\r
+        DDF attr;\r
+        DDF attrs = in.addmember("attributes").list();\r
+        for (vector<Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a) {\r
+            attr = (*a)->marshall();\r
+            attrs.add(attr);\r
+        }\r
+    }\r
+\r
+    DDF out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+    DDFJanitor jout(out);\r
+    if (out["key"].isstring()) {\r
+        for_each(attributes->begin(), attributes->end(), xmltooling::cleanup<Attribute>());\r
+        return out["key"].string();\r
+    }\r
+    throw RetryableProfileException("A remoted cache insertion operation did not return a usable session key.");\r
+}\r
+\r
+Session* RemotedCache::find(const char* key, const Application& application, const char* client_addr)\r
+{\r
+    DDF in("find::"REMOTED_SESSION_CACHE), out;\r
+    DDFJanitor jin(in);\r
+    in.structure();\r
+    in.addmember("key").string(key);\r
+    in.addmember("application_id").string(application.getId());\r
+    in.addmember("client_address").string(client_addr);\r
+    \r
+    try {\r
+        out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+        if (!out.isstruct()) {\r
+            out.destroy();\r
+            return NULL;\r
+        }\r
+        \r
+        // Wrap the results in a stub entry and return it to the caller.\r
+        return new RemotedSession(key, out);\r
+    }\r
+    catch (...) {\r
+        out.destroy();\r
+        throw;\r
+    }\r
+}\r
+\r
+void RemotedCache::remove(const char* key, const Application& application, const char* client_addr)\r
+{\r
+    DDF in("remove::"REMOTED_SESSION_CACHE);\r
+    DDFJanitor jin(in);\r
+    in.structure();\r
+    in.addmember("key").string(key);\r
+    in.addmember("application_id").string(application.getId());\r
+    in.addmember("client_address").string(client_addr);\r
+    \r
+    DDF out = SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+    out.destroy();\r
+}\r
index 5b305d4..1490bd8 100644 (file)
@@ -40,9 +40,5 @@ namespace shibsp {
         return NULL;\r
     }\r
 \r
-}\r
-\r
-void SHIBSP_API shibsp::registerSessionCaches()\r
-{\r
-    SPConfig::getConfig().SessionCacheManager.registerFactory(STORAGESERVICE_SESSION_CACHE, StorageServiceCacheFactory);\r
+    static const XMLCh storageService[] =   UNICODE_LITERAL_14(s,t,o,r,a,g,e,S,e,r,v,i,c,e);\r
 }\r
index 6fd0b62..4b9e1bb 100644 (file)
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\SessionCache.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\SPConfig.cpp"\r
                                >\r
                        </File>\r
                                Name="impl"\r
                                >\r
                                <File\r
+                                       RelativePath=".\impl\RemotedSessionCache.cpp"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\impl\StorageServiceSessionCache.cpp"\r
                                        >\r
                                </File>\r