/*\r
- * Copyright 2001-2005 Internet2\r
+ * Copyright 2001-2007 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
class RemotedSession : public virtual Session\r
{\r
public:\r
- RemotedSession(RemotedCache* cache, const Application& app, DDF& obj)\r
- : m_appId(app.getId()), m_version(obj["version"].integer()), m_obj(obj),\r
- m_nameid(NULL), m_expires(0), m_lastAccess(time(NULL)), m_cache(cache), m_lock(NULL) {\r
+ RemotedSession(RemotedCache* cache, DDF& obj) : m_version(obj["version"].integer()), m_obj(obj),\r
+ m_nameid(NULL), m_expires(0), m_lastAccess(time(NULL)), m_cache(cache), m_lock(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
+ // Parse and bind the NameID into an XMLObject.\r
istringstream instr(nameid);\r
DOMDocument* doc = XMLToolingConfig::getConfig().getParser().parse(instr); \r
XercesJanitor<DOMDocument> janitor(doc);\r
n->unmarshall(doc->getDocumentElement(), true);\r
janitor.release();\r
\r
- // TODO: Process attributes...\r
-\r
auto_ptr_XMLCh exp(m_obj["expires"].string());\r
if (exp.get()) {\r
DateTime iso(exp.get());\r
m_expires = iso.getEpoch();\r
}\r
\r
- m_nameid = n.release();\r
m_lock = Mutex::create();\r
+ m_nameid = n.release();\r
}\r
\r
~RemotedSession() {\r
delete m_lock;\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_pair<string,RootObject>());\r
+ for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair<string,Attribute>());\r
+ for_each(m_tokens.begin(), m_tokens.end(), cleanup_pair<string,Assertion>());\r
}\r
\r
Lockable* lock() {\r
void unlock() {\r
m_lock->unlock();\r
}\r
- \r
+\r
+ const char* getApplicationID() const {\r
+ return m_obj["application_id"].string();\r
+ }\r
const char* getClientAddress() const {\r
return m_obj["client_addr"].string();\r
}\r
const char* getAuthnContextDeclRef() const {\r
return m_obj["authncontext_decl"].string();\r
}\r
- const vector<const Attribute*>& getAttributes() const {\r
+ const map<string,const Attribute*>& getAttributes() const {\r
+ if (m_attributes.empty())\r
+ unmarshallAttributes();\r
return m_attributes;\r
}\r
const vector<const char*>& getAssertionIDs() const {\r
if (m_ids.empty()) {\r
DDF id = m_obj["assertions"].first();\r
while (id.isstring()) {\r
- m_ids.push_back(id.name());\r
+ m_ids.push_back(id.string());\r
id = id.next();\r
}\r
}\r
return m_ids;\r
}\r
\r
- const RootObject* getAssertion(const char* id) const;\r
+ const Assertion* getAssertion(const char* id) const;\r
\r
void addAttributes(const vector<Attribute*>& attributes) {\r
throw ConfigurationException("addAttributes method not implemented by this session cache plugin.");\r
}\r
- void addAssertion(RootObject* assertion) {\r
- throw ConfigurationException("addAttributes method not implemented by this session cache plugin.");\r
+ void addAssertion(Assertion* assertion) {\r
+ throw ConfigurationException("addAssertion method not implemented by this session cache plugin.");\r
}\r
\r
time_t expires() const { return m_expires; }\r
void validate(const Application& application, const char* client_addr, time_t timeout, bool local=true);\r
\r
private:\r
- string m_appId;\r
+ void unmarshallAttributes() const;\r
+\r
int m_version;\r
mutable DDF m_obj;\r
saml2::NameID* m_nameid;\r
- vector<const Attribute*> m_attributes;\r
+ mutable map<string,const Attribute*> m_attributes;\r
mutable vector<const char*> m_ids;\r
- mutable map<string,RootObject*> m_tokens;\r
+ mutable map<string,Assertion*> m_tokens;\r
time_t m_expires,m_lastAccess;\r
RemotedCache* m_cache;\r
Mutex* m_lock;\r
const char* session_index=NULL,\r
const char* authncontext_class=NULL,\r
const char* authncontext_decl=NULL,\r
- const RootObject* ssoToken=NULL,\r
+ const vector<const Assertion*>* tokens=NULL,\r
const vector<Attribute*>* attributes=NULL\r
);\r
Session* find(const char* key, const Application& application, const char* client_addr=NULL, time_t timeout=0);\r
}\r
}\r
\r
-const RootObject* RemotedSession::getAssertion(const char* id) const\r
+void RemotedSession::unmarshallAttributes() const\r
{\r
- map<string,RootObject*>::const_iterator i = m_tokens.find(id);\r
+ Attribute* attribute;\r
+ DDF attr = m_obj["attributes"].first();\r
+ while (!attr.isnull()) {\r
+ try {\r
+ attribute = Attribute::unmarshall(attr);\r
+ m_attributes[attribute->getId()] = attribute;\r
+ if (m_cache->m_log.isDebugEnabled())\r
+ m_cache->m_log.debug("unmarshalled attribute (ID: %s) with %d value%s",\r
+ attribute->getId(), attr.first().integer(), attr.first().integer()!=1 ? "s" : "");\r
+ }\r
+ catch (AttributeException& ex) {\r
+ const char* id = attr.first().name();\r
+ m_cache->m_log.error("error unmarshalling attribute (ID: %s): %s", id ? id : "none", ex.what());\r
+ }\r
+ attr = attr.next();\r
+ }\r
+}\r
+\r
+const Assertion* RemotedSession::getAssertion(const char* id) const\r
+{\r
+ map<string,Assertion*>::const_iterator i = m_tokens.find(id);\r
if (i!=m_tokens.end())\r
return i->second;\r
\r
- const char* tokenstr = m_obj["assertions"][id].string();\r
- if (!tokenstr)\r
- return NULL;\r
+ // Fetch from remoted cache.\r
+ DDF in("getAssertion::"REMOTED_SESSION_CACHE"::SessionCache");\r
+ DDFJanitor jin(in);\r
+ in.structure();\r
+ in.addmember("key").string(m_obj.name());\r
+ in.addmember("id").string(id);\r
+\r
+ DDF out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+ DDFJanitor jout(out);\r
\r
// Parse and bind the document into an XMLObject.\r
- istringstream instr(tokenstr);\r
+ istringstream instr(out.string());\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
+ Assertion* token = dynamic_cast<Assertion*>(xmlObject.get());\r
+ if (!token)\r
throw FatalProfileException("Cached assertion was of an unknown object type.");\r
\r
// Transfer ownership to us.\r
if (local)\r
return;\r
\r
- DDF in("touch::"REMOTED_SESSION_CACHE), out;\r
+ DDF in("touch::"REMOTED_SESSION_CACHE"::SessionCache"), out;\r
DDFJanitor jin(in);\r
in.structure();\r
in.addmember("key").string(m_obj.name());\r
- in.addmember("application_id").string(m_appId.c_str());\r
in.addmember("version").integer(m_obj["version"].integer());\r
if (timeout) {\r
// On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps. \r
}\r
\r
try {\r
- out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
- if (out.isstruct()) {\r
- // We got an updated record back.\r
- m_ids.clear();\r
- for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());\r
- m_attributes.clear();\r
- m_obj.destroy();\r
- m_obj = out;\r
- \r
- // TODO: handle attributes\r
- }\r
+ out=application.getServiceProvider().getListenerService()->send(in);\r
}\r
catch (...) {\r
out.destroy();\r
throw;\r
}\r
- \r
+\r
+ if (out.isstruct()) {\r
+ // We got an updated record back.\r
+ m_ids.clear();\r
+ for_each(m_attributes.begin(), m_attributes.end(), cleanup_const_pair<string,Attribute>());\r
+ m_attributes.clear();\r
+ m_obj.destroy();\r
+ m_obj = out;\r
+ }\r
+\r
m_lastAccess = now;\r
}\r
\r
const char* session_index,\r
const char* authncontext_class,\r
const char* authncontext_decl,\r
- const RootObject* ssoToken,\r
+ const vector<const Assertion*>* tokens,\r
const vector<Attribute*>* attributes\r
)\r
{\r
- DDF in("insert::"REMOTED_SESSION_CACHE);\r
+ DDF in("insert::"REMOTED_SESSION_CACHE"::SessionCache");\r
DDFJanitor jin(in);\r
in.structure();\r
if (expires) {\r
namestr << nameid;\r
in.addmember("nameid").string(namestr.str().c_str());\r
\r
- if (ssoToken) {\r
- ostringstream tokenstr;\r
- tokenstr << *ssoToken;\r
- auto_ptr_char tokenid(ssoToken->getID());\r
- in.addmember("assertions").list().add(DDF(tokenid.get()).string(tokenstr.str().c_str()));\r
+ if (tokens) {\r
+ in.addmember("assertions").list();\r
+ in.addmember("tokens").list();\r
+ for (vector<const Assertion*>::const_iterator t = tokens->begin(); t!=tokens->end(); ++t) {\r
+ ostringstream tokenstr;\r
+ tokenstr << *(*t);\r
+ auto_ptr_char tokenid((*t)->getID());\r
+ DDF tokid = DDF(NULL).string(tokenid.get());\r
+ in["assertions"].add(tokid);\r
+ DDF tok = DDF(tokenid.get()).string(tokenstr.str().c_str());\r
+ in["tokens"].add(tok);\r
+ }\r
}\r
\r
if (attributes) {\r
}\r
}\r
\r
- DDF out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+ DDF out=application.getServiceProvider().getListenerService()->send(in);\r
DDFJanitor jout(out);\r
if (out["key"].isstring()) {\r
- for_each(attributes->begin(), attributes->end(), xmltooling::cleanup<Attribute>());\r
-\r
// Transaction Logging\r
auto_ptr_char name(nameid.getName());\r
const char* pid = in["entity_id"].string();\r
- TransactionLog* xlog = SPConfig::getConfig().getServiceProvider()->getTransactionLog();\r
+ TransactionLog* xlog = application.getServiceProvider().getTransactionLog();\r
Locker locker(xlog);\r
xlog->log.infoStream() <<\r
"New session (ID: " <<\r
name.get() <<\r
")";\r
\r
+ if (attributes) {\r
+ xlog->log.infoStream() <<\r
+ "Cached the following attributes with session (ID: " <<\r
+ out["key"].string() <<\r
+ ") for (applicationId: " <<\r
+ application.getId() <<\r
+ ") {";\r
+ for (vector<Attribute*>::const_iterator a=attributes->begin(); a!=attributes->end(); ++a)\r
+ xlog->log.infoStream() << "\t" << (*a)->getId() << " (" << (*a)->valueCount() << " values)";\r
+ xlog->log.info("}");\r
+ for_each(attributes->begin(), attributes->end(), xmltooling::cleanup<Attribute>());\r
+ }\r
+\r
return out["key"].string();\r
}\r
throw RetryableProfileException("A remoted cache insertion operation did not return a usable session key.");\r
m_lock->unlock();\r
m_log.debug("session not found locally, searching remote cache");\r
\r
- DDF in("find::"REMOTED_SESSION_CACHE), out;\r
+ DDF in("find::"REMOTED_SESSION_CACHE"::SessionCache"), out;\r
DDFJanitor jin(in);\r
in.structure();\r
in.addmember("key").string(key);\r
- in.addmember("application_id").string(application.getId());\r
if (timeout) {\r
// On 64-bit Windows, time_t doesn't fit in a long, so I'm using ISO timestamps. \r
#ifndef HAVE_GMTIME_R\r
}\r
\r
try {\r
- out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+ out=application.getServiceProvider().getListenerService()->send(in);\r
if (!out.isstruct()) {\r
out.destroy();\r
m_log.debug("session not found in remote cache");\r
}\r
\r
// Wrap the results in a local entry and save it.\r
- session = new RemotedSession(this, application, out);\r
+ session = new RemotedSession(this, out);\r
// The remote end has handled timeout issues, we handle address and expiration checks.\r
localValidation = true;\r
}\r
m_log.debug("session found locally, validating it for use");\r
}\r
\r
- // Verify currency and update the timestamp. If the local switch is false, we also update the access time.\r
+ if (!XMLString::equals(session->getApplicationID(), application.getId())) {\r
+ m_log.error("an application (%s) tried to access another application's session", application.getId());\r
+ session->unlock();\r
+ return NULL;\r
+ }\r
+\r
+ // Verify currency and update the timestamp.\r
+ // If the local switch is false, we also update the access time.\r
try {\r
session->validate(application, client_addr, timeout, localValidation);\r
}\r
catch (...) {\r
session->unlock();\r
+ remove(key, application, client_addr);\r
throw;\r
}\r
\r
dormant(key);\r
\r
// Now remote...\r
- DDF in("remove::"REMOTED_SESSION_CACHE);\r
+ DDF in("remove::"REMOTED_SESSION_CACHE"::SessionCache");\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_addr").string(client_addr);\r
\r
- DDF out = SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);\r
+ DDF out = application.getServiceProvider().getListenerService()->send(in);\r
out.destroy();\r
}\r
\r
xmltooling::NDC ndc("cleanup");\r
#endif\r
\r
- int rerun_timer = 0;\r
Mutex* mutex = Mutex::create();\r
\r
// Load our configuration details...\r
static const XMLCh cleanupInterval[] = UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);\r
- const XMLCh* tag=m_root->getAttributeNS(NULL,cleanupInterval);\r
+ const XMLCh* tag=m_root ? m_root->getAttributeNS(NULL,cleanupInterval) : NULL;\r
+ int rerun_timer = 900;\r
if (tag && *tag)\r
rerun_timer = XMLString::parseInt(tag);\r
-\r
if (rerun_timer <= 0)\r
- rerun_timer = 300; // rerun every 5 minutes\r
+ rerun_timer = 900;\r
\r
mutex->lock();\r
\r
m_attributes.insert(m_attributes.end(), attributes.begin(), attributes.end());\r
}\r
\r
-void RemotedSession::addAssertion(RootObject* assertion)\r
+void RemotedSession::addAssertion(Assertion* assertion)\r
{\r
- if (!assertion || !assertion->isAssertion())\r
+ if (!assertion)\r
throw FatalProfileException("Unknown object type passed to session cache for storage.");\r
\r
DDF in("addAssertion::"REMOTED_SESSION_CACHE);\r