--- /dev/null
+/*\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
* @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.
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
*
*/
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() {}
* @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)
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"
/**
--- /dev/null
+/*\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