2 * shib-mysql-ccache.cpp: Shibboleth Credential Cache using MySQL.
4 * Created by: Derek Atkins <derek@ihtfp.com>
9 /* This file is loosely based off the Shibboleth Credential Cache.
10 * This plug-in is designed as a two-layer cache. Layer 1, the
11 * long-term cache, stores data in a MySQL embedded database. The
12 * data stored in layer 1 is only the session id (cookie), the
13 * "posted" SAML statement (expanded into an XML string), and usage
16 * Short-term data is cached in memory as SAML objects in the layer 2
17 * cache. Data like Attribute Authority assertions are stored in
21 // eventually we might be able to support autoconf via cygwin...
22 #if defined (_MSC_VER) || defined(__BORLANDC__)
23 # include "config_win32.h"
29 # define SHIBMYSQL_EXPORTS __declspec(dllexport)
31 # define SHIBMYSQL_EXPORTS
38 #include <shib-target/shib-target.h>
39 #include <shib/shib-threads.h>
40 #include <log4cpp/Category.hh>
47 // wanted to use MySQL codes for this, but can't seem to get back a 145
48 #define isCorrupt(s) strstr(s,"(errno: 145)")
50 #ifdef HAVE_LIBDMALLOCXX
56 using namespace shibboleth;
57 using namespace shibtarget;
58 using namespace log4cpp;
60 #define PLUGIN_VER_MAJOR 2
61 #define PLUGIN_VER_MINOR 0
64 "CREATE TABLE state (cookie VARCHAR(64) PRIMARY KEY, " \
65 "application_id VARCHAR(255)," \
68 "addr VARCHAR(128)," \
70 "provider VARCHAR(256)," \
71 "response_id VARCHAR(128)," \
75 static const XMLCh Argument[] =
76 { chLatin_A, chLatin_r, chLatin_g, chLatin_u, chLatin_m, chLatin_e, chLatin_n, chLatin_t, chNull };
77 static const XMLCh cleanupInterval[] =
78 { chLatin_c, chLatin_l, chLatin_e, chLatin_a, chLatin_n, chLatin_u, chLatin_p,
79 chLatin_I, chLatin_n, chLatin_t, chLatin_e, chLatin_r, chLatin_v, chLatin_a, chLatin_l, chNull
81 static const XMLCh cacheTimeout[] =
82 { chLatin_c, chLatin_a, chLatin_c, chLatin_h, chLatin_e, chLatin_T, chLatin_i, chLatin_m, chLatin_e, chLatin_o, chLatin_u, chLatin_t, chNull };
83 static const XMLCh mysqlTimeout[] =
84 { chLatin_m, chLatin_y, chLatin_s, chLatin_q, chLatin_l, chLatin_T, chLatin_i, chLatin_m, chLatin_e, chLatin_o, chLatin_u, chLatin_t, chNull };
85 static const XMLCh storeAttributes[] =
86 { chLatin_s, chLatin_t, chLatin_o, chLatin_r, chLatin_e, chLatin_A, chLatin_t, chLatin_t, chLatin_r, chLatin_i, chLatin_b, chLatin_u, chLatin_t, chLatin_e, chLatin_s, chNull };
88 class ShibMySQLCCache;
89 class ShibMySQLCCacheEntry : public ISessionCacheEntry
92 ShibMySQLCCacheEntry(const char* key, ISessionCacheEntry* entry, ShibMySQLCCache* cache)
93 : m_cacheEntry(entry), m_key(key), m_cache(cache), m_responseId(NULL) {}
94 ~ShibMySQLCCacheEntry() {if (m_responseId) XMLString::release(&m_responseId);}
96 virtual void lock() {}
97 virtual void unlock() { m_cacheEntry->unlock(); delete this; }
98 virtual bool isValid(time_t lifetime, time_t timeout) const;
99 virtual const char* getClientAddress() const { return m_cacheEntry->getClientAddress(); }
100 virtual ShibProfile getProfile() const { return m_cacheEntry->getProfile(); }
101 virtual const char* getProviderId() const { return m_cacheEntry->getProviderId(); }
102 virtual const SAMLAuthenticationStatement* getAuthnStatement() const { return m_cacheEntry->getAuthnStatement(); }
103 virtual CachedResponse getResponse();
108 ShibMySQLCCache* m_cache;
109 ISessionCacheEntry* m_cacheEntry;
114 class ShibMySQLCCache : public ISessionCache
117 ShibMySQLCCache(const DOMElement* e);
118 virtual ~ShibMySQLCCache();
120 virtual void thread_init();
121 virtual void thread_end() {}
123 virtual string generateKey() const {return m_cache->generateKey();}
124 virtual ISessionCacheEntry* find(const char* key, const IApplication* application);
127 const IApplication* application,
128 const char* client_addr,
130 const char* providerId,
131 saml::SAMLAuthenticationStatement* s,
132 saml::SAMLResponse* r=NULL,
133 const shibboleth::IRoleDescriptor* source=NULL,
137 virtual void remove(const char* key);
140 MYSQL* getMYSQL() const;
141 bool repairTable(MYSQL*&, const char* table);
143 log4cpp::Category* log;
144 bool m_storeAttributes;
147 ISessionCache* m_cache;
149 const DOMElement* m_root; // can only use this during initialization
151 static void* cleanup_fcn(void*); // XXX Assumed an ShibMySQLCCache
152 CondWait* shutdown_wait;
154 Thread* cleanup_thread;
158 void createDatabase(MYSQL*, int major, int minor);
159 void upgradeDatabase(MYSQL*);
160 void getVersion(MYSQL*, int* major_p, int* minor_p);
163 // Forward declarations
164 extern "C" void shib_mysql_destroy_handle(void* data);
165 void mysqlInit(const DOMElement* e, Category& log);
167 /*************************************************************************
168 * The CCache here talks to a MySQL database. The database stores
169 * three items: the cookie (session key index), the lastAccess time, and
170 * the SAMLAuthenticationStatement. All other access is performed
171 * through the memory cache provided by shibboleth.
174 MYSQL* ShibMySQLCCache::getMYSQL() const
176 return (MYSQL*)m_mysql->getData();
179 void ShibMySQLCCache::thread_init()
182 saml::NDC ndc("thread_init");
185 // Connect to the database
186 MYSQL* mysql = mysql_init(NULL);
188 log->error("mysql_init failed");
190 throw SAMLException("ShibMySQLCCache::thread_init(): mysql_init() failed");
193 if (!mysql_real_connect(mysql, NULL, NULL, NULL, "shar", 0, NULL, 0)) {
195 log->crit("mysql_real_connect failed: %s", mysql_error(mysql));
197 throw SAMLException("ShibMySQLCCache::thread_init(): mysql_real_connect() failed");
199 log->info("mysql_real_connect failed: %s. Trying to create", mysql_error(mysql));
201 // This will throw an exception if it fails.
202 createDatabase(mysql, PLUGIN_VER_MAJOR, PLUGIN_VER_MINOR);
206 int major = -1, minor = -1;
207 getVersion (mysql, &major, &minor);
209 // Make sure we've got the right version
210 if (major != PLUGIN_VER_MAJOR || minor != PLUGIN_VER_MINOR) {
212 // If we're capable, try upgrading on the fly...
213 if (major == 0 || major == 1) {
214 upgradeDatabase(mysql);
218 log->crit("Unknown database version: %d.%d", major, minor);
219 throw SAMLException("ShibMySQLCCache::thread_init(): Unknown database version");
223 // We're all set.. Save off the handle for this thread.
224 m_mysql->setData(mysql);
227 ShibMySQLCCache::ShibMySQLCCache(const DOMElement* e) : m_root(e), m_storeAttributes(false)
230 saml::NDC ndc("shibmysql::ShibMySQLCCache");
233 m_mysql = ThreadKey::create(&shib_mysql_destroy_handle);
234 log = &(Category::getInstance("shibmysql::ShibMySQLCCache"));
241 m_cache = dynamic_cast<ISessionCache*>(
242 SAMLConfig::getConfig().getPlugMgr().newPlugin(
243 "edu.internet2.middleware.shibboleth.sp.provider.MemorySessionCacheProvider", e
247 // Load our configuration details...
248 const XMLCh* tag=m_root->getAttributeNS(NULL,storeAttributes);
249 if (tag && *tag && (*tag==chLatin_t || *tag==chDigit_1))
250 m_storeAttributes=true;
252 // Initialize the cleanup thread
253 shutdown_wait = CondWait::create();
255 cleanup_thread = Thread::create(&cleanup_fcn, (void*)this);
258 ShibMySQLCCache::~ShibMySQLCCache()
261 shutdown_wait->signal();
262 cleanup_thread->join(NULL);
272 ISessionCacheEntry* ShibMySQLCCache::find(const char* key, const IApplication* application)
275 saml::NDC ndc("ShibMySQLCCache::find");
278 ISessionCacheEntry* res = m_cache->find(key, application);
281 log->debug("Looking in database...");
283 // nothing cached; see if this exists in the database
284 string q = string("SELECT application_id,UNIX_TIMESTAMP(ctime),UNIX_TIMESTAMP(atime),addr,profile,provider,statement,response FROM state WHERE cookie='") + key + "' LIMIT 1";
286 MYSQL* mysql = getMYSQL();
287 if (mysql_query(mysql, q.c_str())) {
288 const char* err=mysql_error(mysql);
289 log->error("Error searching for %s: %s", key, err);
290 if (isCorrupt(err) && repairTable(mysql,"state")) {
291 if (mysql_query(mysql, q.c_str()))
292 log->error("Error retrying search for %s: %s", key, mysql_error(mysql));
296 MYSQL_RES* rows = mysql_store_result(mysql);
298 // Nope, doesn't exist.
302 // Make sure we got 1 and only 1 rows.
303 if (mysql_num_rows(rows) != 1) {
304 log->error("Select returned wrong number of rows: %d", mysql_num_rows(rows));
305 mysql_free_result(rows);
309 log->debug("Match found. Parsing...");
322 // Pull apart the row and process the results
323 MYSQL_ROW row = mysql_fetch_row(rows);
324 if (strcmp(application->getId(),row[0])) {
325 log->crit("An application (%s) attempted to access another application's (%s) session!", application->getId(), row[0]);
326 mysql_free_result(rows);
330 Metadata m(application->getMetadataProviders());
331 const IEntityDescriptor* provider=m.lookup(row[5]);
333 log->crit("no metadata found for identity provider (%s) responsible for the session.", row[5]);
334 mysql_free_result(rows);
338 SAMLAuthenticationStatement* s=NULL;
339 SAMLResponse* r=NULL;
340 const IRoleDescriptor* role=provider->getIDPSSODescriptor(saml::XML::SAML11_PROTOCOL_ENUM);
342 log->crit("no SAML 1.1 IdP role found for identity provider (%s) responsible for the session.", row[5]);
343 mysql_free_result(rows);
347 // Try to parse the SAML data
349 istringstream istr(row[6]);
350 s = new SAMLAuthenticationStatement(istr);
352 istringstream istr2(row[7]);
353 r = new SAMLResponse(istr2);
356 catch (SAMLException& e) {
357 log->error(string("caught SAML exception while loading objects from SQL record: ") + e.what());
360 mysql_free_result(rows);
365 log->error("caught unknown exception while loading objects from SQL record");
368 mysql_free_result(rows);
373 // Insert it into the memory cache
378 static_cast<ShibProfile>(atoi(row[4])),
387 // Free the results, and then re-run the 'find' query
388 mysql_free_result(rows);
389 res = m_cache->find(key,application);
394 return new ShibMySQLCCacheEntry(key, res, this);
397 void ShibMySQLCCache::insert(
399 const IApplication* application,
400 const char* client_addr,
402 const char* providerId,
403 saml::SAMLAuthenticationStatement* s,
404 saml::SAMLResponse* r,
405 const shibboleth::IRoleDescriptor* source,
411 saml::NDC ndc("ShibMySQLCCache::insert");
415 q << "INSERT INTO state VALUES('" << key << "','" << application->getId() << "',";
419 q << "FROM_UNIXTIME(" << created << "),";
423 q << "FROM_UNIXTIME(" << accessed << "),'";
424 q << client_addr << "'," << profile << ",'" << providerId << "',";
425 if (m_storeAttributes && r) {
426 auto_ptr_char id(r->getId());
427 q << "'" << id.get() << "','" << *r << "','";
433 log->debug("Query: %s", q.str().c_str());
435 // then add it to the database
436 MYSQL* mysql = getMYSQL();
437 if (mysql_query(mysql, q.str().c_str())) {
438 const char* err=mysql_error(mysql);
439 log->error("Error inserting %s: %s", key, err);
440 if (isCorrupt(err) && repairTable(mysql,"state")) {
442 if (mysql_query(mysql, q.str().c_str()))
443 log->error("Error inserting %s: %s", key, mysql_error(mysql));
444 throw SAMLException("ShibMySQLCCache::insert(): inset failed");
448 // Add it to the memory cache
449 m_cache->insert(key, application, client_addr, profile, providerId, s, r, source, created, accessed);
452 void ShibMySQLCCache::remove(const char* key)
455 saml::NDC ndc("ShibMySQLCCache::remove");
458 // Remove the cached version
459 m_cache->remove(key);
461 // Remove from the database
462 string q = string("DELETE FROM state WHERE cookie='") + key + "'";
463 MYSQL* mysql = getMYSQL();
464 if (mysql_query(mysql, q.c_str())) {
465 const char* err=mysql_error(mysql);
466 log->error("Error deleting entry %s: %s", key, err);
467 if (isCorrupt(err) && repairTable(mysql,"state")) {
469 if (mysql_query(mysql, q.c_str()))
470 log->error("Error deleting entry %s: %s", key, mysql_error(mysql));
475 void ShibMySQLCCache::cleanup()
478 saml::NDC ndc("ShibMySQLCCache::cleanup");
481 Mutex* mutex = Mutex::create();
485 int timeout_life = 0;
487 // Load our configuration details...
488 const XMLCh* tag=m_root->getAttributeNS(NULL,cleanupInterval);
490 rerun_timer = XMLString::parseInt(tag);
492 // search for 'mysql-cache-timeout' and then the regular cache timeout
493 tag=m_root->getAttributeNS(NULL,mysqlTimeout);
495 timeout_life = XMLString::parseInt(tag);
497 tag=m_root->getAttributeNS(NULL,cacheTimeout);
499 timeout_life = XMLString::parseInt(tag);
502 if (rerun_timer <= 0)
503 rerun_timer = 300; // rerun every 5 minutes
505 if (timeout_life <= 0)
506 timeout_life = 28800; // timeout after 8 hours
510 MYSQL* mysql = getMYSQL();
512 while (shutdown == false) {
513 shutdown_wait->timedwait(mutex, rerun_timer);
515 if (shutdown == true)
518 // Find all the entries in the database that haven't been used
519 // recently In particular, find all entries that have not been
520 // accessed in 'timeout_life' seconds.
522 q << "SELECT cookie FROM state WHERE " <<
523 "UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(atime) >= " << timeout_life;
525 if (mysql_query(mysql, q.str().c_str())) {
526 const char* err=mysql_error(mysql);
527 log->error("Error searching for old items: %s", err);
528 if (isCorrupt(err) && repairTable(mysql,"state")) {
529 if (mysql_query(mysql, q.str().c_str()))
530 log->error("Error re-searching for old items: %s", mysql_error(mysql));
534 MYSQL_RES* rows = mysql_store_result(mysql);
538 if (mysql_num_fields(rows) != 1) {
539 log->error("Wrong number of columns, 1 != %d", mysql_num_fields(rows));
540 mysql_free_result(rows);
544 // For each row, remove the entry from the database.
546 while ((row = mysql_fetch_row(rows)) != NULL)
549 mysql_free_result(rows);
552 log->debug("cleanup thread exiting...");
560 void* ShibMySQLCCache::cleanup_fcn(void* cache_p)
562 ShibMySQLCCache* cache = (ShibMySQLCCache*)cache_p;
564 // First, let's block all signals
565 Thread::mask_all_signals();
567 // Now run the cleanup process.
572 bool ShibMySQLCCache::repairTable(MYSQL*& mysql, const char* table)
574 string q = string("REPAIR TABLE ") + table;
575 if (mysql_query(mysql, q.c_str())) {
576 log->error("Error repairing table %s: %s", table, mysql_error(mysql));
580 // seems we have to recycle the connection to get the thread to keep working
581 // other threads seem to be ok, but we should monitor that
583 m_mysql->setData(NULL);
589 void ShibMySQLCCache::createDatabase(MYSQL* mysql, int major, int minor)
591 log->info("Creating database.");
595 ms = mysql_init(NULL);
597 log->crit("mysql_init failed");
598 throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_init failed");
601 if (!mysql_real_connect(ms, NULL, NULL, NULL, NULL, 0, NULL, 0)) {
602 log->crit("cannot open DB file to create DB: %s", mysql_error(ms));
603 throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_real_connect failed");
606 if (mysql_query(ms, "CREATE DATABASE shar")) {
607 log->crit("cannot create shar database: %s", mysql_error(ms));
608 throw SAMLException("ShibMySQLCCache::createDatabase(): create db cmd failed");
611 if (!mysql_real_connect(mysql, NULL, NULL, NULL, "shar", 0, NULL, 0)) {
612 log->crit("cannot open SHAR database");
613 throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_real_connect to shar db failed");
619 catch (SAMLException&) {
626 // Now create the tables if they don't exist
627 log->info("Creating database tables");
629 if (mysql_query(mysql, "CREATE TABLE version (major INT, minor INT)")) {
630 log->error ("Error creating version: %s", mysql_error(mysql));
631 throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
634 if (mysql_query(mysql,STATE_TABLE)) {
635 log->error ("Error creating state: %s", mysql_error(mysql));
636 throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
640 q << "INSERT INTO version VALUES(" << major << "," << minor << ")";
641 if (mysql_query(mysql, q.str().c_str())) {
642 log->error ("Error setting version: %s", mysql_error(mysql));
643 throw SAMLException("ShibMySQLCCache::createDatabase(): version insert failed");
647 void ShibMySQLCCache::upgradeDatabase(MYSQL* mysql)
649 if (mysql_query(mysql, "DROP TABLE state")) {
650 log->error("Error dropping old session state table: %s", mysql_error(mysql));
653 if (mysql_query(mysql,STATE_TABLE)) {
654 log->error ("Error creating state table: %s", mysql_error(mysql));
655 throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error creating state table");
659 q << "UPDATE version SET major = " << PLUGIN_VER_MAJOR;
660 if (mysql_query(mysql, q.str().c_str())) {
661 log->error ("Error updating version: %s", mysql_error(mysql));
662 throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error updating version");
666 void ShibMySQLCCache::getVersion(MYSQL* mysql, int* major_p, int* minor_p)
668 // grab the version number from the database
669 if (mysql_query(mysql, "SELECT * FROM version"))
670 log->error ("Error reading version: %s", mysql_error(mysql));
672 MYSQL_RES* rows = mysql_store_result(mysql);
674 if (mysql_num_rows(rows) == 1 && mysql_num_fields(rows) == 2) {
675 MYSQL_ROW row = mysql_fetch_row(rows);
677 int major = row[0] ? atoi(row[0]) : -1;
678 int minor = row[1] ? atoi(row[1]) : -1;
679 log->debug("opening database version %d.%d", major, minor);
681 mysql_free_result (rows);
688 // Wrong number of rows or wrong number of fields...
690 log->crit("Houston, we've got a problem with the database...");
691 mysql_free_result (rows);
692 throw SAMLException("ShibMySQLCCache::getVersion(): version verification failed");
695 log->crit("MySQL Read Failed in version verificatoin");
696 throw SAMLException("ShibMySQLCCache::getVersion(): error reading version");
699 void mysqlInit(const DOMElement* e, Category& log)
701 static bool done = false;
703 log.info("MySQL embedded server already initialized");
706 log.info("initializing MySQL embedded server");
708 // Setup the argument array
709 vector<string> arg_array;
710 arg_array.push_back("shibboleth");
712 // grab any MySQL parameters from the config file
713 e=saml::XML::getFirstChildElement(e,ShibTargetConfig::SHIBTARGET_NS,Argument);
715 auto_ptr_char arg(e->getFirstChild()->getNodeValue());
717 arg_array.push_back(arg.get());
718 e=saml::XML::getNextSiblingElement(e,ShibTargetConfig::SHIBTARGET_NS,Argument);
721 // Compute the argument array
722 int arg_count = arg_array.size();
723 const char** args=new const char*[arg_count];
724 for (int i = 0; i < arg_count; i++)
725 args[i] = arg_array[i].c_str();
727 // Initialize MySQL with the arguments
728 mysql_server_init(arg_count, (char **)args, NULL);
734 /*************************************************************************
735 * The CCacheEntry here is mostly a wrapper around the "memory"
736 * cacheentry provided by shibboleth. The only difference is that we
737 * intercept isSessionValid() so that we can "touch()" the
738 * database if the session is still valid and getResponse() so we can
739 * store the data if we need to.
742 bool ShibMySQLCCacheEntry::isValid(time_t lifetime, time_t timeout) const
744 bool res = m_cacheEntry->isValid(lifetime, timeout);
750 bool ShibMySQLCCacheEntry::touch() const
752 string q=string("UPDATE state SET atime=NOW() WHERE cookie='") + m_key + "'";
754 MYSQL* mysql = m_cache->getMYSQL();
755 if (mysql_query(mysql, q.c_str())) {
756 m_cache->log->info("Error updating timestamp on %s: %s",
757 m_key.c_str(), mysql_error(mysql));
763 ISessionCacheEntry::CachedResponse ShibMySQLCCacheEntry::getResponse()
765 // Let the memory cache do the work first.
766 // If we're hands off, just pass it back.
767 if (!m_cache->m_storeAttributes)
768 return m_cacheEntry->getResponse();
770 CachedResponse r=m_cacheEntry->getResponse();
771 if (r.empty()) return r;
773 // Load the key from state if needed.
775 string qselect=string("SELECT response_id from state WHERE cookie='") + m_key + "' LIMIT 1";
776 MYSQL* mysql = m_cache->getMYSQL();
777 if (mysql_query(mysql, qselect.c_str())) {
778 const char* err=mysql_error(mysql);
779 m_cache->log->error("error accessing response ID for %s: %s", m_key.c_str(), err);
780 if (isCorrupt(err) && m_cache->repairTable(mysql,"state")) {
782 if (mysql_query(mysql, qselect.c_str())) {
783 m_cache->log->error("error accessing response ID for %s: %s", m_key.c_str(), mysql_error(mysql));
788 MYSQL_RES* rows = mysql_store_result(mysql);
790 // Make sure we got 1 and only 1 row.
791 if (!rows || mysql_num_rows(rows) != 1) {
792 m_cache->log->error("select returned wrong number of rows");
793 if (rows) mysql_free_result(rows);
797 MYSQL_ROW row=mysql_fetch_row(rows);
799 m_responseId=XMLString::transcode(row[0]);
800 mysql_free_result(rows);
803 // Compare it with what we have now.
804 if (m_responseId && !XMLString::compareString(m_responseId,r.unfiltered->getId()))
807 // No match, so we need to update our copy.
808 if (m_responseId) XMLString::release(&m_responseId);
809 m_responseId = XMLString::replicate(r.unfiltered->getId());
810 auto_ptr_char id(m_responseId);
813 q << "UPDATE state SET response_id='" << id.get() << "',response='" << *r.unfiltered << "' WHERE cookie='" << m_key << "'";
814 m_cache->log->debug("Query: %s", q.str().c_str());
816 MYSQL* mysql = m_cache->getMYSQL();
817 if (mysql_query(mysql, q.str().c_str())) {
818 const char* err=mysql_error(mysql);
819 m_cache->log->error("Error updating response for %s: %s", m_key.c_str(), err);
820 if (isCorrupt(err) && m_cache->repairTable(mysql,"state")) {
822 if (mysql_query(mysql, q.str().c_str()))
823 m_cache->log->error("Error updating response for %s: %s", m_key.c_str(), mysql_error(mysql));
830 /*************************************************************************
831 * The registration functions here...
834 IPlugIn* new_mysql_ccache(const DOMElement* e)
836 return new ShibMySQLCCache(e);
839 IPlugIn* new_mysql_replay(const DOMElement* e)
844 #define REPLAYPLUGINTYPE "edu.internet2.middleware.shibboleth.sp.provider.MySQLReplayCacheProvider"
845 #define SESSIONPLUGINTYPE "edu.internet2.middleware.shibboleth.sp.provider.MySQLSessionCacheProvider"
847 extern "C" int SHIBMYSQL_EXPORTS saml_extension_init(void*)
849 // register this ccache type
850 SAMLConfig::getConfig().getPlugMgr().regFactory(REPLAYPLUGINTYPE, &new_mysql_replay);
851 SAMLConfig::getConfig().getPlugMgr().regFactory(SESSIONPLUGINTYPE, &new_mysql_ccache);
855 extern "C" void SHIBMYSQL_EXPORTS saml_extension_term()
857 SAMLConfig::getConfig().getPlugMgr().unregFactory(REPLAYPLUGINTYPE);
858 SAMLConfig::getConfig().getPlugMgr().unregFactory(SESSIONPLUGINTYPE);
861 /*************************************************************************
865 extern "C" void shib_mysql_destroy_handle(void* data)
867 MYSQL* mysql = (MYSQL*) data;