# include <unistd.h>
#endif
-#include <shib/shib-threads.h>
-
-#include <log4cpp/Category.hh>
-
+#include <ctime>
#include <algorithm>
#include <sstream>
#include <stdexcept>
+#include <shibsp/SPConfig.h>
#ifdef HAVE_LIBDMALLOCXX
#include <dmalloc.h>
#endif
-using namespace std;
-using namespace log4cpp;
-using namespace saml;
-using namespace shibboleth;
+using namespace shibsp;
using namespace shibtarget;
+using namespace saml;
+using namespace opensaml::saml2md;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+using xmlsignature::CredentialResolver;
static const XMLCh cleanupInterval[] =
{ chLatin_c, chLatin_l, chLatin_e, chLatin_a, chLatin_n, chLatin_u, chLatin_p,
{ chLatin_p, chLatin_r, chLatin_o, chLatin_p, chLatin_a, chLatin_g, chLatin_a, chLatin_t, chLatin_e,
chLatin_E, chLatin_r, chLatin_r, chLatin_o, chLatin_r, chLatin_s, chNull
};
+static const XMLCh writeThrough[] =
+{ chLatin_w, chLatin_r, chLatin_i, chLatin_t, chLatin_e,
+ chLatin_T, chLatin_h, chLatin_r, chLatin_o, chLatin_u, chLatin_g, chLatin_h, chNull
+};
/*
string insert(
const IApplication* application,
- const IRoleDescriptor* source,
+ const RoleDescriptor* role,
const char* client_addr,
const SAMLSubject* subject,
const char* authnContext,
- SAMLResponse* tokens
+ const SAMLResponse* tokens
);
ISessionCacheEntry* find(const char* key, const IApplication* application, const char* client_addr);
void remove(const char* key, const IApplication* application, const char* client_addr);
+ bool setBackingStore(ISessionCacheStore*) { return false; }
+
private:
- // The front-end cache is never loaded from the "back door".
- void load(
- const char* key,
- const IApplication* application,
- const IRoleDescriptor* source,
- const char* client_addr,
- const char* providerId,
- const char* subject,
- const char* authnContext,
- const char* tokens,
- int majorVersion,
- int minorVersion,
- time_t created=0,
- time_t accessed=0
- ) { throw SAMLException("Unsupported operation."); }
-
Category* m_log;
};
string StubCache::insert(
const IApplication* application,
- const IRoleDescriptor* source,
+ const RoleDescriptor* role,
const char* client_addr,
const SAMLSubject* subject,
const char* authnContext,
- SAMLResponse* tokens
+ const SAMLResponse* tokens
)
{
DDF in("SessionCache::insert"),out;
in.structure();
in.addmember("application_id").string(application->getId());
in.addmember("client_address").string(client_addr);
- auto_ptr_char provid(source->getEntityDescriptor()->getId());
+ xmltooling::auto_ptr_char provid(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
in.addmember("provider_id").string(provid.get());
- auto_ptr_char prot(source->getProtocolSupportEnumeration().next());
- in.addmember("protocol").string(prot.get());
in.addmember("major_version").integer(1);
in.addmember("minor_version").integer(tokens->getMinorVersion());
in.addmember("authn_context").string(authnContext);
os << *tokens;
in.addmember("tokens.unfiltered").string(os.str().c_str());
- out=ShibTargetConfig::getConfig().getINI()->getListener()->send(in);
+ out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);
if (out["key"].isstring())
return out["key"].string();
- throw InvalidSessionException("A remoted cache insertion operation did not return a usable session key.");
+ throw opensaml::RetryableProfileException("A remoted cache insertion operation did not return a usable session key.");
}
ISessionCacheEntry* StubCache::find(const char* key, const IApplication* application, const char* client_addr)
in.addmember("client_address").string(client_addr);
try {
- out=ShibTargetConfig::getConfig().getINI()->getListener()->send(in);
+ out=SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);
if (!out.isstruct()) {
out.destroy();
return NULL;
in.addmember("application_id").string(application->getId());
in.addmember("client_address").string(client_addr);
- ShibTargetConfig::getConfig().getINI()->getListener()->send(in);
+ SPConfig::getConfig().getServiceProvider()->getListenerService()->send(in);
}
-
/*
* Long-lived cache entries that store the actual sessions and
* wrap attribute query/refresh/filtering
MemorySessionCache* cache,
const char* key,
const IApplication* application,
- const IRoleDescriptor* source,
+ const RoleDescriptor* role,
const char* client_addr,
const SAMLSubject* subject,
const char* authnContext,
- SAMLResponse* tokens
+ const SAMLResponse* tokens
);
MemorySessionCacheEntry(
MemorySessionCache* cache,
const char* key,
const IApplication* application,
- const IRoleDescriptor* source,
+ const RoleDescriptor* role,
const char* client_addr,
- const char* providerId,
const char* subject,
const char* authnContext,
const char* tokens,
void lock() { m_lock->lock(); }
void unlock() { m_lock->unlock(); }
-
- pair<const char*,const SAMLResponse*> getTokens(bool xml=true, bool obj=false) const
- { populate(); return StubCacheEntry::getTokens(xml,obj); }
- pair<const char*,const SAMLResponse*> getFilteredTokens(bool xml=true, bool obj=false) const
- { return StubCacheEntry::getFilteredTokens(xml,obj); }
-
+
HRESULT isValid(const IApplication* application, const char* client_addr) const;
+ void populate(const IApplication* application, const EntityDescriptor* source, bool initial=false) const;
bool checkApplication(const IApplication* application) { return (m_obj["application_id"]==application->getId()); }
+ time_t created() const { return m_sessionCreated; }
time_t lastAccess() const { return m_lastAccess; }
const DDF& getDDF() const { return m_obj; }
private:
bool hasAttributes(const SAMLResponse& r) const;
time_t calculateExpiration(const SAMLResponse& r) const;
- void populate() const; // wraps process of checking cache, and repopulating if need be
- bool responseValid() const; // checks validity of existing response
- pair<SAMLResponse*,SAMLResponse*> getNewResponse() const; // wraps an actual query
- SAMLResponse* filter(SAMLResponse* r, const IApplication* application, const IRoleDescriptor* source) const;
+ pair<SAMLResponse*,SAMLResponse*> getNewResponse(const IApplication* application, const EntityDescriptor* source) const;
+ SAMLResponse* filter(const SAMLResponse* r, const IApplication* application, const RoleDescriptor* role) const;
time_t m_sessionCreated;
mutable time_t m_responseExpiration, m_lastAccess, m_lastRetry;
/*
* The actual in-memory session cache implementation.
*/
-class MemorySessionCache : public virtual ISessionCache, public virtual IRemoted
+class MemorySessionCache : public virtual ISessionCache, public virtual Remoted
{
public:
MemorySessionCache(const DOMElement* e);
string insert(
const IApplication* application,
- const IRoleDescriptor* source,
+ const RoleDescriptor* role,
const char* client_addr,
const SAMLSubject* subject,
const char* authnContext,
- SAMLResponse* tokens
+ const SAMLResponse* tokens
);
ISessionCacheEntry* find(const char* key, const IApplication* application, const char* client_addr);
void remove(const char* key, const IApplication* application, const char* client_addr);
void cleanup();
-private:
- void load(
- const char* key,
- const IApplication* application,
- const IRoleDescriptor* source,
- const char* client_addr,
- const char* providerId,
- const char* subject,
- const char* authnContext,
- const char* tokens,
- int majorVersion,
- int minorVersion,
- time_t created,
- time_t accessed
- );
+ bool setBackingStore(ISessionCacheStore* store);
+private:
const DOMElement* m_root; // Only valid during initialization
RWLock* m_lock;
map<string,MemorySessionCacheEntry*> m_hashtable;
Category* m_log;
- IRemoted* restoreInsert;
- IRemoted* restoreFind;
- IRemoted* restoreRemove;
+ Remoted* restoreInsert;
+ Remoted* restoreFind;
+ Remoted* restoreRemove;
+ ISessionCacheStore* m_sink;
+ void dormant(const char* key);
static void* cleanup_fcn(void*);
bool shutdown;
CondWait* shutdown_wait;
// extracted config settings
unsigned int m_AATimeout,m_AAConnectTimeout;
unsigned int m_defaultLifetime,m_retryInterval;
- bool m_strictValidity,m_propagateErrors;
+ bool m_strictValidity,m_propagateErrors,m_writeThrough;
friend class MemorySessionCacheEntry;
};
MemorySessionCache* cache,
const char* key,
const IApplication* application,
- const IRoleDescriptor* source,
+ const RoleDescriptor* role,
const char* client_addr,
const SAMLSubject* subject,
const char* authnContext,
- SAMLResponse* tokens
+ const SAMLResponse* tokens
) : StubCacheEntry(cache->m_log), m_cache(cache), m_responseExpiration(0), m_lastRetry(0)
{
- auto_ptr<SAMLResponse> unfiltered(tokens);
m_sessionCreated = m_lastAccess = time(NULL);
// Store session properties in DDF.
m_obj.addmember("key").string(key);
m_obj.addmember("client_address").string(client_addr);
m_obj.addmember("application_id").string(application->getId());
- auto_ptr_char pid(source ? source->getEntityDescriptor()->getId() : tokens->getAssertions().next()->getIssuer());
+ xmltooling::auto_ptr_char pid(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
m_obj.addmember("provider_id").string(pid.get());
m_obj.addmember("major_version").integer(1);
m_obj.addmember("minor_version").integer(tokens->getMinorVersion());
if (hasAttributes(*tokens)) {
// Filter attributes in the response.
- auto_ptr<SAMLResponse> filtered(filter(tokens, application, source));
+ auto_ptr<SAMLResponse> filtered(filter(tokens, application, role));
// Calculate expiration.
m_responseExpiration=calculateExpiration(*(filtered.get()));
// Save actual objects only if we're running inprocess. The subject needs to be
// owned by the entry, so we'll defer creation of a cloned copy.
- if (ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::InProcess)) {
- m_pUnfiltered=unfiltered.release();
+ if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
if (m_obj["tokens.filtered"].isstring())
m_pFiltered=filtered.release();
}
}
// Transaction Logging
- auto_ptr_char hname(subject->getNameIdentifier()->getName());
+ xmltooling::auto_ptr_char hname(subject->getNameIdentifier()->getName());
STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
stc.getTransactionLog().infoStream() <<
"New session (ID: " <<
MemorySessionCache* cache,
const char* key,
const IApplication* application,
- const IRoleDescriptor* source,
+ const RoleDescriptor* role,
const char* client_addr,
- const char* providerId,
const char* subject,
const char* authnContext,
const char* tokens,
m_obj.addmember("key").string(key);
m_obj.addmember("client_address").string(client_addr);
m_obj.addmember("application_id").string(application->getId());
- m_obj.addmember("provider_id").string(providerId);
+ xmltooling::auto_ptr_char pid(dynamic_cast<EntityDescriptor*>(role->getParent())->getEntityID());
+ m_obj.addmember("provider_id").string(pid.get());
m_obj.addmember("subject").string(subject);
m_obj.addmember("authn_context").string(authnContext);
m_obj.addmember("tokens.unfiltered").string(tokens);
m_obj.addmember("minor_version").integer(minorVersion);
if (hasAttributes(*(unfiltered.get()))) {
- auto_ptr<SAMLResponse> filtered(filter(unfiltered.get(), application, source));
+ auto_ptr<SAMLResponse> filtered(filter(unfiltered.get(), application, role));
// Calculate expiration.
m_responseExpiration=calculateExpiration(*(filtered.get()));
m_obj.addmember("tokens.filtered").string(fstr.c_str());
// Save actual objects only if we're running inprocess.
- if (ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::InProcess)) {
+ if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
m_pUnfiltered=unfiltered.release();
if (m_obj["tokens.filtered"].isstring())
m_pFiltered=filtered.release();
MemorySessionCacheEntry::~MemorySessionCacheEntry()
{
- if (m_log->isDebugEnabled())
- m_log->debug("deleting cache entry (ID: %s)", m_obj["key"].string());
delete m_lock;
}
HRESULT MemorySessionCacheEntry::isValid(const IApplication* app, const char* client_addr) const
{
#ifdef _DEBUG
- saml::NDC ndc("isValid");
+ xmltooling::NDC ndc("isValid");
#endif
+
// Obtain validation rules from application settings.
- bool checkIPAddress=true;
+ bool consistentIPAddress=true;
int lifetime=0,timeout=0;
- const IPropertySet* props=app->getPropertySet("Sessions");
+ const PropertySet* props=app->getPropertySet("Sessions");
if (props) {
pair<bool,unsigned int> p=props->getUnsignedInt("lifetime");
if (p.first)
p=props->getUnsignedInt("timeout");
if (p.first)
timeout = p.second;
- pair<bool,bool> pcheck=props->getBool("checkAddress");
+ pair<bool,bool> pcheck=props->getBool("consistentIPAddress");
if (pcheck.first)
- checkIPAddress = pcheck.second;
+ consistentIPAddress = pcheck.second;
}
if (m_log->isDebugEnabled())
}
if (timeout > 0 && now-m_lastAccess >= timeout) {
- m_log->info("session timed out (ID: %s)", m_obj["key"].string());
- return SESSION_E_EXPIRED;
+ // May need to query sink first to find out if another cluster member has been used.
+ if (m_cache->m_sink && m_cache->m_writeThrough) {
+ if (NOERROR!=m_cache->m_sink->onRead(m_obj["key"].string(),m_lastAccess))
+ m_log->error("cache store failed to return last access timestamp");
+ if (now-m_lastAccess >= timeout) {
+ m_log->info("session timed out (ID: %s)", m_obj["key"].string());
+ return SESSION_E_EXPIRED;
+ }
+ }
+ else {
+ m_log->info("session timed out (ID: %s)", m_obj["key"].string());
+ return SESSION_E_EXPIRED;
+ }
}
- if (checkIPAddress) {
+ if (consistentIPAddress) {
if (m_log->isDebugEnabled())
m_log->debug("comparing client address %s against %s", client_addr, getClientAddress());
if (strcmp(client_addr, getClientAddress())) {
}
m_lastAccess=now;
- populate();
+
+ if (m_cache->m_sink && m_cache->m_writeThrough && timeout > 0) {
+ // Update sink with last access data, if possible.
+ if (FAILED(m_cache->m_sink->onUpdate(m_obj["key"].string(),NULL,m_lastAccess)))
+ m_log->error("cache store failed to update last access timestamp");
+ }
+
return NOERROR;
}
return expiration;
}
-void MemorySessionCacheEntry::populate() const
+void MemorySessionCacheEntry::populate(const IApplication* application, const EntityDescriptor* source, bool initial) const
{
#ifdef _DEBUG
- saml::NDC ndc("populate");
+ xmltooling::NDC ndc("populate");
#endif
// Do we have any attribute data cached?
// Can we use what we have?
if (time(NULL) < m_responseExpiration)
return;
-
+
+ // Possibly check the sink in case another cluster member already refreshed it.
+ if (m_cache->m_sink && m_cache->m_writeThrough) {
+ string tokensFromSink;
+ HRESULT hr=m_cache->m_sink->onRead(m_obj["key"].string(),tokensFromSink);
+ if (FAILED(hr))
+ m_log->error("cache store failed to return updated tokens");
+ else if (hr==NOERROR && tokensFromSink!=m_obj["tokens.unfiltered"].string()) {
+
+ // Bah...find role again.
+ const RoleDescriptor* role=source->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+ if (!role)
+ role=source->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+ if (!role)
+ role=source->getIDPSSODescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+ if (!role)
+ role=source->getIDPSSODescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+ if (!role) {
+ throw MetadataException("Unable to locate attribute-issuing role in metadata.");
+ }
+
+ // The tokens in the sink were different.
+ istringstream is(tokensFromSink);
+ auto_ptr<SAMLResponse> respFromSink(new SAMLResponse(is,m_obj["minor_version"].integer()));
+ auto_ptr<SAMLResponse> filteredFromSink(filter(respFromSink.get(),application,role));
+ time_t expFromSink=calculateExpiration(*(filteredFromSink.get()));
+
+ // Recheck to see if the new tokens are valid.
+ if (expFromSink < time(NULL)) {
+ m_log->info("loading replacement tokens into memory from cache store");
+ m_obj["tokens"].destroy();
+ delete m_pUnfiltered;
+ delete m_pFiltered;
+ m_pUnfiltered=m_pFiltered=NULL;
+ m_obj.addmember("tokens.unfiltered").string(tokensFromSink.c_str());
+
+ // Serialize filtered assertions (if changes were made).
+ ostringstream os;
+ os << *(filteredFromSink.get());
+ string fstr=os.str();
+ if (fstr.length() != m_obj.getmember("tokens.unfiltered").strlen())
+ m_obj.addmember("tokens.filtered").string(fstr.c_str());
+
+ // Save actual objects only if we're running inprocess.
+ if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
+ m_pUnfiltered=respFromSink.release();
+ if (m_obj["tokens.filtered"].isstring())
+ m_pFiltered=filteredFromSink.release();
+ }
+
+ m_responseExpiration=expFromSink;
+ m_lastRetry=0;
+ return;
+ }
+ }
+ }
+
// If we're being strict, dump what we have and reset timestamps.
if (m_cache->m_strictValidity) {
m_log->info("strictly enforcing attribute validity, dumping expired data");
delete m_pFiltered;
m_pUnfiltered=m_pFiltered=NULL;
m_responseExpiration=0;
- m_lastRetry=0;
+ m_lastRetry=0;
+ if (m_cache->m_sink) {
+ if (FAILED(m_cache->m_sink->onUpdate(m_obj["key"].string(),"")))
+ m_log->error("cache store returned failure while clearing tokens from entry");
+ }
}
}
try {
- pair<SAMLResponse*,SAMLResponse*> new_responses=getNewResponse();
+ pair<SAMLResponse*,SAMLResponse*> new_responses=getNewResponse(application,source);
auto_ptr<SAMLResponse> r1(new_responses.first),r2(new_responses.second);
if (new_responses.first) {
m_obj["tokens"].destroy();
m_responseExpiration=calculateExpiration(*new_responses.second);
// Save actual objects only if we're running inprocess.
- if (ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::InProcess)) {
+ if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) {
m_pUnfiltered=r1.release();
if (m_obj["tokens.filtered"].isstring())
m_pFiltered=r2.release();
}
+ // Update backing store.
+ if (!initial && m_cache->m_sink) {
+ if (FAILED(m_cache->m_sink->onUpdate(m_obj["key"].string(),m_obj["tokens.unfiltered"].string())))
+ m_log->error("cache store returned failure while updating tokens in entry");
+ }
+
m_lastRetry=0;
m_log->debug("fetched and stored new response");
STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
stc.releaseTransactionLog();
}
}
- catch (SAMLException&) {
+ catch (exception&) {
if (m_cache->m_propagateErrors)
throw;
- m_log->warn("suppressed SAML exception caught while trying to fetch attributes");
+ m_log->warn("suppressed exception caught while trying to fetch attributes");
}
#ifndef _DEBUG
catch (...) {
#endif
}
-pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse() const
+pair<SAMLResponse*,SAMLResponse*> MemorySessionCacheEntry::getNewResponse(
+ const IApplication* application, const EntityDescriptor* source
+ ) const
{
#ifdef _DEBUG
- saml::NDC ndc("getNewResponse");
+ xmltooling::NDC ndc("getNewResponse");
#endif
// The retryInterval determines how often to poll an AA that might be down.
stc.releaseTransactionLog();
- // Caller must be holding the config lock.
- // Lookup application for session to get providerId and attributes to request.
- IConfig* conf=ShibTargetConfig::getConfig().getINI();
- const IApplication* application=conf->getApplication(m_obj["application_id"].string());
- if (!application) {
- m_log->crit("unable to locate application for session, deleted?");
- throw SAMLException("Unable to locate application for session, deleted?");
- }
pair<bool,const XMLCh*> providerID=application->getXMLString("providerId");
if (!providerID.first) {
m_log->crit("unable to determine ProviderID for application, not set?");
- throw SAMLException("Unable to determine ProviderID for application, not set?");
- }
-
- // Try this request.
- Metadata m(application->getMetadataProviders());
- const IEntityDescriptor* site=m.lookup(m_obj["provider_id"].string());
- if (!site) {
- m_log->error("unable to locate identity provider's metadata for attribute query");
- throw MetadataException("Unable to locate identity provider's metadata for attribute query.");
+ throw ConfigurationException("Unable to determine ProviderID for application, not set?");
}
// Try to locate an AA role.
- const IAttributeAuthorityDescriptor* AA=site->getAttributeAuthorityDescriptor(
- m_obj["minor_version"].integer()==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
+ const AttributeAuthorityDescriptor* AA=source->getAttributeAuthorityDescriptor(
+ m_obj["minor_version"].integer()==1 ? samlconstants::SAML11_PROTOCOL_ENUM : samlconstants::SAML10_PROTOCOL_ENUM
);
if (!AA) {
m_log->warn("unable to locate metadata for identity provider's Attribute Authority");
}
// Get protocol signing policy.
- const IPropertySet* credUse=application->getCredentialUse(site);
+ const PropertySet* credUse=application->getCredentialUse(source);
pair<bool,bool> signRequest=credUse ? credUse->getBool("signRequest") : make_pair(false,false);
pair<bool,const char*> signatureAlg=credUse ? credUse->getString("signatureAlg") : pair<bool,const char*>(false,NULL);
if (!signatureAlg.first)
// Build a SAML Request....
SAMLAttributeQuery* q=new SAMLAttributeQuery(
new SAMLSubject(nameid),
- providerID.second,
- application->getAttributeDesignators().clone()
+ providerID.second
);
auto_ptr<SAMLRequest> req(new SAMLRequest(q));
req->setMinorVersion(m_obj["minor_version"].integer());
// Sign it?
if (signRequest.first && signRequest.second && signingCred.first) {
if (req->getMinorVersion()==1) {
- Credentials creds(conf->getCredentialsProviders());
- const ICredResolver* cr=creds.lookup(signingCred.second);
- if (cr)
+ CredentialResolver* cr=SPConfig::getConfig().getServiceProvider()->getCredentialResolver(signingCred.second);
+ if (cr) {
+ xmltooling::Locker locker(cr);
req->sign(cr->getKey(),cr->getCertificates(),signatureAlg.second,digestAlg.second);
+ }
else
m_log->error("unable to sign attribute query, specified credential (%s) was not found",signingCred.second);
}
// Call context object
ShibHTTPHook::ShibHTTPHookCallContext ctx(credUse,AA);
- Trust t(application->getTrustProviders());
// Use metadata to locate endpoints.
- Iterator<const IEndpoint*> endpoints=AA->getAttributeServiceManager()->getEndpoints();
- while (!response && endpoints.hasNext()) {
- const IEndpoint* ep=endpoints.next();
+ const vector<AttributeService*>& endpoints=AA->getAttributeServices();
+ for (vector<AttributeService*>::const_iterator ep=endpoints.begin(); !response && ep!=endpoints.end(); ++ep) {
try {
// Get a binding object for this protocol.
- const SAMLBinding* binding = application->getBinding(ep->getBinding());
+ const SAMLBinding* binding = application->getBinding((*ep)->getBinding());
if (!binding) {
- auto_ptr_char prot(ep->getBinding());
+ xmltooling::auto_ptr_char prot((*ep)->getBinding());
m_log->warn("skipping binding on unsupported protocol (%s)", prot.get());
continue;
}
static const XMLCh https[] = {chLatin_h, chLatin_t, chLatin_t, chLatin_p, chLatin_s, chColon, chNull};
- auto_ptr<SAMLResponse> r(binding->send(ep->getLocation(), *(req.get()), &ctx));
+ auto_ptr<SAMLResponse> r(binding->send((*ep)->getLocation(), *(req.get()), &ctx));
if (r->isSigned()) {
- if (!t.validate(*r,AA))
- throw TrustException("Unable to verify signed response message.");
+ // TODO: trust stuff will be changing anyway...
+ //if (!t.validate(*r,AA))
+ // throw TrustException("Unable to verify signed response message.");
}
- else if (!ctx.isAuthenticated() || XMLString::compareNString(ep->getLocation(),https,6))
- throw TrustException("Response message was unauthenticated.");
+ else if (!ctx.isAuthenticated() || XMLString::compareNString((*ep)->getLocation(),https,6))
+ throw XMLSecurityException("Response message was unauthenticated.");
response = r.release();
}
- catch (SAMLException& e) {
- m_log->error("caught SAML exception during SAML attribute query: %s", e.what());
- // Check for shib:InvalidHandle error and propagate it out.
- Iterator<saml::QName> codes=e.getCodes();
- if (codes.size()>1) {
- const saml::QName& code=codes[1];
- if (!XMLString::compareString(code.getNamespaceURI(),shibboleth::Constants::SHIB_NS) &&
- !XMLString::compareString(code.getLocalName(), shibboleth::Constants::InvalidHandle)) {
- codes.reset();
- throw InvalidHandleException(e.what(),params(),codes);
- }
- }
+ catch (exception& e) {
+ m_log->error("caught exception during SAML attribute query: %s", e.what());
}
}
if (signedResponse.first && signedResponse.second && !response->isSigned()) {
delete response;
m_log->error("unsigned response obtained, but we were told it must be signed.");
- throw TrustException("Unable to obtain a signed response message.");
+ throw XMLSecurityException("Unable to obtain a signed response message.");
}
+ // Iterate over the tokens and apply basic validation.
+ time_t now=time(NULL);
+ Iterator<SAMLAssertion*> assertions=response->getAssertions();
+ for (unsigned int a=0; a<assertions.size();) {
+ // Discard any assertions not issued by the right entity.
+ if (XMLString::compareString(source->getEntityID(),assertions[a]->getIssuer())) {
+ xmltooling::auto_ptr_char bad(assertions[a]->getIssuer());
+ m_log->warn("discarding assertion not issued by (%s), instead by (%s)",m_obj["provider_id"].string(),bad.get());
+ response->removeAssertion(a);
+ continue;
+ }
+
+ // Validate the token.
+ try {
+ application->validateToken(assertions[a],now,AA,application->getTrustEngine());
+ a++;
+ }
+ catch (exception&) {
+ m_log->warn("assertion failed to validate, removing it from response");
+ response->removeAssertion(a);
+ }
+ }
+
// Run it through the filter.
return make_pair(response,filter(response,application,AA));
}
}
- catch (SAMLException& e) {
- m_log->error("caught SAML exception during query to AA: %s", e.what());
- annotateException(&e,AA);
+ catch (exception& e) {
+ m_log->error("caught exception during query to AA: %s", e.what());
+ throw;
}
m_log->error("no response obtained");
}
SAMLResponse* MemorySessionCacheEntry::filter(
- SAMLResponse* r, const IApplication* application, const IRoleDescriptor* source
+ const SAMLResponse* r, const IApplication* application, const RoleDescriptor* role
) const
{
- const IPropertySet* credUse=application->getCredentialUse(source->getEntityDescriptor());
- pair<bool,bool> signedAssertions=credUse ? credUse->getBool("signedAssertions") : make_pair(false,false);
- Trust t(application->getTrustProviders());
-
- // Examine each original assertion...
- Iterator<SAMLAssertion*> assertions=r->getAssertions();
- for (unsigned long i=0; i < assertions.size();) {
- // Check signing policy.
- if (signedAssertions.first && signedAssertions.second && !(assertions[i]->isSigned())) {
- m_log->warn("removing unsigned assertion from response, in accordance with signedAssertions policy");
- r->removeAssertion(i);
- continue;
- }
-
- // Check any conditions.
- bool pruned=false;
- Iterator<SAMLCondition*> conds=assertions[i]->getConditions();
- while (conds.hasNext()) {
- SAMLAudienceRestrictionCondition* cond=dynamic_cast<SAMLAudienceRestrictionCondition*>(conds.next());
- if (!cond || !cond->eval(application->getAudiences())) {
- m_log->warn("assertion condition invalid, removing it");
- r->removeAssertion(i);
- pruned=true;
- break;
- }
- }
- if (pruned)
- continue;
-
- // Check token signature.
- if (assertions[i]->isSigned() && !t.validate(*(assertions[i]),source)) {
- m_log->warn("signed assertion failed to validate, removing it");
- r->removeAssertion(i);
- continue;
- }
- i++;
- }
+#ifdef _DEBUG
+ xmltooling::NDC ndc("filter");
+#endif
- // Make a copy of whatever's left and process that against the AAP.
+ // Make a copy of the original and process that against the AAP.
auto_ptr<SAMLResponse> copy(static_cast<SAMLResponse*>(r->clone()));
copy->toDOM();
for (unsigned long j=0; j < copies.size();) {
try {
// Finally, filter the content.
- AAP::apply(application->getAAPProviders(),*(copies[j]),source);
+ shibboleth::AAP::apply(application->getAAPProviders(),*(copies[j]),role);
j++;
}
- catch (SAMLException&) {
+ catch (exception&) {
m_log->info("no statements remain after AAP, removing assertion");
copy->removeAssertion(j);
}
Iterator<SAMLAttribute*> attrs=state ? state->getAttributes() : EMPTY(SAMLAttribute*);
while (attrs.hasNext()) {
SAMLAttribute* attr=attrs.next();
- auto_ptr_char attrname(attr->getName());
+ xmltooling::auto_ptr_char attrname(attr->getName());
tran.infoStream() << "\t" << attrname.get() << " (" << attr->getValues().size() << " values)";
}
}
MemorySessionCache::MemorySessionCache(const DOMElement* e)
: m_root(e), m_AATimeout(30), m_AAConnectTimeout(15), m_defaultLifetime(1800), m_retryInterval(300),
- m_strictValidity(true), m_propagateErrors(false), m_lock(RWLock::create()),
+ m_strictValidity(true), m_propagateErrors(false), m_writeThrough(false), m_lock(RWLock::create()),
m_log(&Category::getInstance(SHIBT_LOGCAT".SessionCache")),
- restoreInsert(NULL),restoreFind(NULL),restoreRemove(NULL)
+ restoreInsert(NULL), restoreFind(NULL), restoreRemove(NULL), m_sink(NULL)
{
- const XMLCh* tag=m_root->getAttributeNS(NULL,AATimeout);
- if (tag && *tag) {
- m_AATimeout = XMLString::parseInt(tag);
- if (!m_AATimeout)
- m_AATimeout=30;
- }
- SAMLConfig::getConfig().timeout = m_AATimeout;
+ if (m_root) {
+ const XMLCh* tag=m_root->getAttributeNS(NULL,AATimeout);
+ if (tag && *tag) {
+ m_AATimeout = XMLString::parseInt(tag);
+ if (!m_AATimeout)
+ m_AATimeout=30;
+ }
- tag=m_root->getAttributeNS(NULL,AAConnectTimeout);
- if (tag && *tag) {
- m_AAConnectTimeout = XMLString::parseInt(tag);
- if (!m_AAConnectTimeout)
- m_AAConnectTimeout=15;
- }
- SAMLConfig::getConfig().conn_timeout = m_AAConnectTimeout;
-
- tag=m_root->getAttributeNS(NULL,defaultLifetime);
- if (tag && *tag) {
- m_defaultLifetime = XMLString::parseInt(tag);
- if (!m_defaultLifetime)
- m_defaultLifetime=1800;
- }
+ tag=m_root->getAttributeNS(NULL,AAConnectTimeout);
+ if (tag && *tag) {
+ m_AAConnectTimeout = XMLString::parseInt(tag);
+ if (!m_AAConnectTimeout)
+ m_AAConnectTimeout=15;
+ }
+
+ tag=m_root->getAttributeNS(NULL,defaultLifetime);
+ if (tag && *tag) {
+ m_defaultLifetime = XMLString::parseInt(tag);
+ if (!m_defaultLifetime)
+ m_defaultLifetime=1800;
+ }
- tag=m_root->getAttributeNS(NULL,retryInterval);
- if (tag && *tag) {
- m_retryInterval = XMLString::parseInt(tag);
- if (!m_retryInterval)
- m_retryInterval=300;
- }
-
- tag=m_root->getAttributeNS(NULL,strictValidity);
- if (tag && (*tag==chDigit_0 || *tag==chLatin_f))
- m_strictValidity=false;
+ tag=m_root->getAttributeNS(NULL,retryInterval);
+ if (tag && *tag) {
+ m_retryInterval = XMLString::parseInt(tag);
+ if (!m_retryInterval)
+ m_retryInterval=300;
+ }
- tag=m_root->getAttributeNS(NULL,propagateErrors);
- if (tag && (*tag==chDigit_1 || *tag==chLatin_t))
- m_propagateErrors=true;
+ tag=m_root->getAttributeNS(NULL,strictValidity);
+ if (tag && (*tag==chDigit_0 || *tag==chLatin_f))
+ m_strictValidity=false;
+
+ tag=m_root->getAttributeNS(NULL,propagateErrors);
+ if (tag && (*tag==chDigit_1 || *tag==chLatin_t))
+ m_propagateErrors=true;
+
+ tag=m_root->getAttributeNS(NULL,writeThrough);
+ if (tag && (*tag==chDigit_1 || *tag==chLatin_t))
+ m_writeThrough=true;
+ }
+
+ SAMLConfig::getConfig().timeout = m_AATimeout;
+ SAMLConfig::getConfig().conn_timeout = m_AAConnectTimeout;
// Register for remoted messages.
- IListener* listener=ShibTargetConfig::getConfig().getINI()->getListener();
- if (listener && ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::OutOfProcess)) {
+ ListenerService* listener=SPConfig::getConfig().getServiceProvider()->getListenerService(false);
+ if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
restoreInsert=listener->regListener("SessionCache::insert",this);
restoreFind=listener->regListener("SessionCache::find",this);
restoreRemove=listener->regListener("SessionCache::remove",this);
cleanup_thread->join(NULL);
// Unregister remoted messages.
- IListener* listener=ShibTargetConfig::getConfig().getINI()->getListener();
- if (listener && ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::OutOfProcess)) {
+ ListenerService* listener=SPConfig::getConfig().getServiceProvider()->getListenerService(false);
+ if (listener && SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) {
listener->unregListener("SessionCache::insert",this,restoreInsert);
listener->unregListener("SessionCache::find",this,restoreFind);
listener->unregListener("SessionCache::remove",this,restoreRemove);
}
- for_each(m_hashtable.begin(),m_hashtable.end(),shibtarget::cleanup<string,MemorySessionCacheEntry>);
+ for_each(m_hashtable.begin(),m_hashtable.end(),xmltooling::cleanup_pair<string,MemorySessionCacheEntry>());
delete m_lock;
delete shutdown_wait;
}
+bool MemorySessionCache::setBackingStore(ISessionCacheStore* store)
+{
+ if (m_sink && store!=m_sink)
+ return false;
+ m_sink=store;
+ return true;
+}
+
/*
* IPC message definitions:
*
* application_id
* client_address
* provider_id
- * protocol
* major_version
* minor_version
* authn_context
DDF MemorySessionCache::receive(const DDF& in)
{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("receive");
+#endif
+
// Find application.
+ xmltooling::Locker confLocker(SPConfig::getConfig().getServiceProvider());
const char* aid=in["application_id"].string();
- const IApplication* app=aid ? ShibTargetConfig::getConfig().getINI()->getApplication(aid) : NULL;
+ const IApplication* app=aid ? dynamic_cast<const IApplication*>(SPConfig::getConfig().getServiceProvider()->getApplication(aid)) : NULL;
if (!app) {
// Something's horribly wrong.
m_log->error("couldn't find application (%s) for session", aid ? aid : "(missing)");
- throw SAMLException("Unable to locate application for session, deleted?");
+ throw ConfigurationException("Unable to locate application for session, deleted?");
}
if (!strcmp(in.name(),"SessionCache::find")) {
entry->unlock();
return dup;
}
- catch (SAMLException&) {
+ catch (exception&) {
remove(key,app,client_address);
throw;
}
// Check required parameters.
const char* client_address=in["client_address"].string();
const char* provider_id=in["provider_id"].string();
- const char* protocol=in["protocol"].string();
const char* authn_context=in["authn_context"].string();
const char* subject=in["subject"].string();
const char* tokens=in["tokens.unfiltered"].string();
- if (!client_address || !provider_id || !protocol || !authn_context || !subject || !tokens)
+ if (!client_address || !provider_id || !authn_context || !subject || !tokens)
throw SAMLException("Required parameters missing in call to SessionCache::insert");
int minor=in["minor_version"].integer();
- // Locate role descriptor to use in filtering.
- Metadata m(app->getMetadataProviders());
- const IEntityDescriptor* site=m.lookup(provider_id);
+ // Locate entity descriptor to use in filtering.
+ MetadataProvider* m=app->getMetadataProvider();
+ xmltooling::Locker locker(m);
+ const EntityDescriptor* site=m->getEntityDescriptor(provider_id);
if (!site) {
m_log->error("unable to locate issuing identity provider's metadata");
throw MetadataException("Unable to locate identity provider's metadata.");
}
- auto_ptr_XMLCh prot(protocol);
- const IIDPSSODescriptor* IDP=site->getIDPSSODescriptor(prot.get());
- if (!IDP)
- m_log->warn("unable to locate metadata for identity provider role, scoped attributes may be limited");
-
+ const RoleDescriptor* role=site->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+ if (!role)
+ role=site->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+ if (!role)
+ role=site->getIDPSSODescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+ if (!role)
+ role=site->getIDPSSODescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+ if (!role) {
+ m_log->error("unable to locate attribute-issuing role in identity provider's metadata");
+ throw MetadataException("Unable to locate attribute-issuing role in identity provider's metadata.");
+ }
+
// Deserialize XML for insert method.
istringstream subis(subject);
auto_ptr<SAMLSubject> pSubject(new SAMLSubject(subis));
istringstream tokis(tokens);
- SAMLResponse* pTokens=new SAMLResponse(tokis,minor);
+ auto_ptr<SAMLResponse> pTokens(new SAMLResponse(tokis,minor));
// Insert the data and return the cache key.
- string key=insert(app,IDP,client_address,pSubject.get(),authn_context,pTokens);
+ string key=insert(app,role,client_address,pSubject.get(),authn_context,pTokens.get());
DDF out(NULL);
out.structure();
out.addmember("key").string(key.c_str());
return out;
}
- throw ListenerException("unsupported operation ($1)",params(1,in.name()));
+ throw ListenerException("Unsupported operation ($1)",xmltooling::params(1,in.name()));
}
string MemorySessionCache::insert(
const IApplication* application,
- const IRoleDescriptor* source,
+ const RoleDescriptor* role,
const char* client_addr,
const SAMLSubject* subject,
const char* authnContext,
- SAMLResponse* tokens
+ const SAMLResponse* tokens
)
{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("insert");
+#endif
+
SAMLIdentifier id;
- auto_ptr_char key(id);
+ xmltooling::auto_ptr_char key(id);
if (m_log->isDebugEnabled())
m_log->debug("creating new cache entry for application %s: \"%s\"", application->getId(), key.get());
- MemorySessionCacheEntry* entry = new MemorySessionCacheEntry(
- this,
- key.get(),
- application,
- source,
- client_addr,
- subject,
- authnContext,
- tokens
+ auto_ptr<MemorySessionCacheEntry> entry(
+ new MemorySessionCacheEntry(
+ this,
+ key.get(),
+ application,
+ role,
+ client_addr,
+ subject,
+ authnContext,
+ tokens
+ )
);
+ entry->populate(application,dynamic_cast<EntityDescriptor*>(role->getParent()),true);
+
+ if (m_sink) {
+ HRESULT hr=m_sink->onCreate(key.get(),application,entry.get(),1,tokens->getMinorVersion(),entry->created());
+ if (FAILED(hr)) {
+ m_log->error("cache store returned failure while storing new entry");
+ throw IOException("Unable to record new session in cache store.");
+ }
+ }
m_lock->wrlock();
- m_hashtable[key.get()]=entry;
+ m_hashtable[key.get()]=entry.release();
m_lock->unlock();
return key.get();
}
-void MemorySessionCache::load(
- const char* key,
- const IApplication* application,
- const IRoleDescriptor* source,
- const char* client_addr,
- const char* providerId,
- const char* subject,
- const char* authnContext,
- const char* tokens,
- int minorVersion,
- int majorVersion,
- time_t created,
- time_t accessed
- )
-{
- if (m_log->isDebugEnabled())
- m_log->debug("loading cache entry back into memory for application %s: \"%s\"", application->getId(), key);
-
- MemorySessionCacheEntry* entry = new MemorySessionCacheEntry(
- this,
- key,
- application,
- source,
- client_addr,
- providerId,
- subject,
- authnContext,
- tokens,
- minorVersion,
- majorVersion,
- created,
- accessed
- );
-
- m_lock->wrlock();
- m_hashtable[key]=entry;
- m_lock->unlock();
-}
-
ISessionCacheEntry* MemorySessionCache::find(const char* key, const IApplication* application, const char* client_addr)
{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("find");
+#endif
+
m_log->debug("searching memory cache for key (%s)", key);
- ReadLock rwlock(m_lock);
+ m_lock->rdlock();
map<string,MemorySessionCacheEntry*>::const_iterator i=m_hashtable.find(key);
if (i==m_hashtable.end()) {
+ m_lock->unlock();
m_log->debug("no match found");
- return NULL;
+ if (!m_sink)
+ return NULL; // no backing store to search
+
+ m_log->debug("searching backing store");
+ string appid,addr,pid,sub,ac,tokens;
+ int major,minor;
+ time_t created,accessed;
+ HRESULT hr=m_sink->onRead(key,appid,addr,pid,sub,ac,tokens,major,minor,created,accessed);
+ if (hr==S_FALSE)
+ return NULL;
+ else if (FAILED(hr)) {
+ m_log->error("cache store returned failure during search");
+ return NULL;
+ }
+ const IApplication* eapp=dynamic_cast<const IApplication*>(SPConfig::getConfig().getServiceProvider()->getApplication(appid.c_str()));
+ if (!eapp) {
+ // Something's horribly wrong.
+ m_log->error("couldn't find application (%s) for session", appid.c_str());
+ if (FAILED(m_sink->onDelete(key)))
+ m_log->error("cache store returned failure during delete");
+ return NULL;
+ }
+ if (m_log->isDebugEnabled())
+ m_log->debug("loading cache entry (ID: %s) back into memory for application (%s)", key, appid.c_str());
+
+ // Locate role to use in filtering.
+ MetadataProvider* m=eapp->getMetadataProvider();
+ xmltooling::Locker locker(m);
+ const EntityDescriptor* site=m->getEntityDescriptor(pid.c_str());
+ if (!site) {
+ m_log->error("unable to locate issuing identity provider's metadata");
+ if (FAILED(m_sink->onDelete(key)))
+ m_log->error("cache store returned failure during delete");
+ return NULL;
+ }
+ const RoleDescriptor* role=site->getAttributeAuthorityDescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+ if (!role)
+ role=site->getAttributeAuthorityDescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+ if (!role)
+ role=site->getIDPSSODescriptor(samlconstants::SAML11_PROTOCOL_ENUM);
+ if (!role)
+ role=site->getIDPSSODescriptor(samlconstants::SAML10_PROTOCOL_ENUM);
+ if (!role) {
+ m_log->error("unable to locate attribute-issuing role in identity provider's metadata");
+ if (FAILED(m_sink->onDelete(key)))
+ m_log->error("cache store returned failure during delete");
+ return NULL;
+ }
+
+ MemorySessionCacheEntry* entry = new MemorySessionCacheEntry(
+ this,
+ key,
+ eapp,
+ role,
+ addr.c_str(),
+ sub.c_str(),
+ ac.c_str(),
+ tokens.c_str(),
+ major,
+ minor,
+ created,
+ accessed
+ );
+ m_lock->wrlock();
+ m_hashtable[key]=entry;
+ m_lock->unlock();
+
+ // Downgrade to a read lock and repeat the initial search.
+ m_lock->rdlock();
+ i=m_hashtable.find(key);
+ if (i==m_hashtable.end()) {
+ m_lock->unlock();
+ m_log->warn("cache entry was loaded from backing store, but disappeared after lock downgrade");
+ return NULL;
+ }
}
- m_log->debug("match found");
+ else
+ m_log->debug("match found");
// Check for application mismatch (could also do this with partitioned caches by application ID)
if (!i->second->checkApplication(application)) {
+ m_lock->unlock();
m_log->crit("An application (%s) attempted to access another application's session!", application->getId());
return NULL;
}
// Check for timeouts, expiration, address mismatch, etc (also updates last access)
// Use the return code to assign specific error messages.
- HRESULT hr=i->second->isValid(application, client_addr);
- if (FAILED(hr)) {
- Metadata m(application->getMetadataProviders());
- switch (hr) {
- case SESSION_E_EXPIRED: {
- InvalidSessionException ex(SESSION_E_EXPIRED, "Your session has expired, and you must re-authenticate.");
- annotateException(&ex,m.lookup(i->second->getProviderId())); // throws it
- }
-
- case SESSION_E_ADDRESSMISMATCH: {
- InvalidSessionException ex(
- SESSION_E_ADDRESSMISMATCH,
- "Your IP address (%1) does not match the address recorded at the time the session was established.",
- params(1,client_addr)
- );
- annotateException(&ex,m.lookup(i->second->getProviderId())); // throws it
- }
-
- default: {
- InvalidSessionException ex(hr, "Your session is invalid.");
- annotateException(&ex,m.lookup(i->second->getProviderId())); // throws it
+ try {
+ HRESULT hr=i->second->isValid(application, client_addr);
+ if (FAILED(hr)) {
+ MetadataProvider* m=application->getMetadataProvider();
+ xmltooling::Locker locker(m);
+ switch (hr) {
+ case SESSION_E_EXPIRED: {
+ opensaml::RetryableProfileException ex("Your session has expired, and you must re-authenticate.");
+ annotateException(&ex,m->getEntityDescriptor(i->second->getProviderId(),false)); // throws it
+ }
+
+ case SESSION_E_ADDRESSMISMATCH: {
+ opensaml::RetryableProfileException ex(
+ "Your IP address ($1) does not match the address recorded at the time the session was established.",
+ xmltooling::params(1,client_addr)
+ );
+ annotateException(&ex,m->getEntityDescriptor(i->second->getProviderId(),false)); // throws it
+ }
+
+ default: {
+ opensaml::RetryableProfileException ex("Your session is invalid.");
+ annotateException(&ex,m->getEntityDescriptor(i->second->getProviderId(),false)); // throws it
+ }
}
}
}
+ catch (...) {
+ m_lock->unlock();
+ throw;
+ }
// Lock the cache entry for the caller -- they have to unlock it.
i->second->lock();
+ m_lock->unlock();
+
+ try {
+ // Make sure the entry has valid tokens.
+ MetadataProvider* m=application->getMetadataProvider();
+ xmltooling::Locker locker(m);
+ i->second->populate(application,m->getEntityDescriptor(i->second->getProviderId()));
+ }
+ catch (...) {
+ i->second->unlock();
+ throw;
+ }
+
return i->second;
}
void MemorySessionCache::remove(const char* key, const IApplication* application, const char* client_addr)
{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("remove");
+#endif
+
m_log->debug("removing cache entry with key (%s)", key);
// lock the cache for writing, which means we know nobody is sitting in find()
// unlock the cache
m_lock->unlock();
- // we can release the cache entry lock because we know we're not in the cache anymore
entry->unlock();
- // Now delete the entry
- delete entry;
+ // Notify sink. Smart ptr will make sure entry gets deleted.
+ auto_ptr<ISessionCacheEntry> entrywrap(entry);
+ if (m_sink) {
+ if (FAILED(m_sink->onDelete(key)))
+ m_log->error("cache store failed to delete entry");
+ }
// Transaction Logging
STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
stc.releaseTransactionLog();
}
+void MemorySessionCache::dormant(const char* key)
+{
+#ifdef _DEBUG
+ xmltooling::NDC ndc("dormant");
+#endif
+
+ m_log->debug("purging old cache entry with key (%s)", key);
+
+ // lock the cache for writing, which means we know nobody is sitting in find()
+ m_lock->wrlock();
+
+ // grab the entry from the database.
+ map<string,MemorySessionCacheEntry*>::const_iterator i=m_hashtable.find(key);
+ if (i==m_hashtable.end()) {
+ m_lock->unlock();
+ return;
+ }
+
+ // ok, remove the entry and lock it
+ MemorySessionCacheEntry* entry=i->second;
+ m_hashtable.erase(key);
+ entry->lock();
+
+ // unlock the cache
+ m_lock->unlock();
+
+ // we can release the cache entry lock because we know we're not in the cache anymore
+ entry->unlock();
+
+ auto_ptr<ISessionCacheEntry> entrywrap(entry);
+ if (m_sink && !m_writeThrough) {
+ // Update sink with last access data. Wrapper will make sure entry gets deleted.
+ if (FAILED(m_sink->onUpdate(key,NULL,entry->lastAccess())))
+ m_log->error("cache store failed to update last access timestamp");
+ }
+}
+
void MemorySessionCache::cleanup()
{
#ifdef _DEBUG
- saml::NDC ndc("cleanup()");
+ xmltooling::NDC ndc("cleanup()");
#endif
int rerun_timer = 0;
mutex->lock();
- m_log->info("Cleanup thread started...Run every %d secs; timeout after %d secs", rerun_timer, timeout_life);
+ m_log->info("cleanup thread started...Run every %d secs; timeout after %d secs", rerun_timer, timeout_life);
while (!shutdown) {
shutdown_wait->timedwait(mutex,rerun_timer);
if (shutdown)
break;
- m_log->debug("Cleanup thread running...");
-
// Ok, let's run through the cleanup process and clean out
// really old sessions. This is a two-pass process. The
// first pass is done holding a read-lock while we iterate over
}
m_lock->unlock();
- m_log->info("deleting %d old items.", stale_keys.size());
+ if (!stale_keys.empty()) {
+ m_log->info("purging %d old sessions", stale_keys.size());
- // Pass 2: walk through the list of stale entries and remove them from the cache
- for (vector<string>::const_iterator j = stale_keys.begin(); j != stale_keys.end(); j++) {
- remove(j->c_str(),NULL,NULL);
- // Transaction Logging
- STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
- stc.getTransactionLog().infoStream() << "Purged expired session from memory (ID: " << j->c_str() << ")";
- stc.releaseTransactionLog();
+ // Pass 2: walk through the list of stale entries and remove them from the cache
+ for (vector<string>::const_iterator j = stale_keys.begin(); j != stale_keys.end(); j++)
+ dormant(j->c_str());
}
}
- m_log->info("Cleanup thread finished.");
+ m_log->info("cleanup thread finished.");
mutex->unlock();
delete mutex;
{
MemorySessionCache* cache = reinterpret_cast<MemorySessionCache*>(cache_p);
+#ifndef WIN32
// First, let's block all signals
Thread::mask_all_signals();
+#endif
// Now run the cleanup process.
cache->cleanup();
return NULL;
}
-IPlugIn* MemoryCacheFactory(const DOMElement* e)
+SessionCache* MemoryCacheFactory(const DOMElement* const & e)
{
// If this is a long-lived process, we return the "real" cache.
- if (ShibTargetConfig::getConfig().isEnabled(ShibTargetConfig::OutOfProcess))
+ if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess))
return new MemorySessionCache(e);
// Otherwise, we return a stubbed front-end that remotes calls to the real cache.
return new StubCache(e);