/* This file is loosely based off the Shibboleth Credential Cache.
* This plug-in is designed as a two-layer cache. Layer 1, the
- * long-term cache, stores data in a MySQL embedded database. The
- * data stored in layer 1 is only the session id (cookie), the
- * "posted" SAML statement (expanded into an XML string), and usage
- * timestamps.
+ * long-term cache, stores data in a MySQL embedded database.
*
* Short-term data is cached in memory as SAML objects in the layer 2
- * cache. Data like Attribute Authority assertions are stored in
- * the layer 2 cache.
+ * cache.
*/
// eventually we might be able to support autoconf via cygwin...
"response TEXT," \
"statement TEXT)"
+#define REPLAY_TABLE \
+ "CREATE TABLE replay (id VARCHAR(255) PRIMARY KEY, " \
+ "expires TIMESTAMP, " \
+ "INDEX (expires))"
+
static const XMLCh Argument[] =
{ chLatin_A, chLatin_r, chLatin_g, chLatin_u, chLatin_m, chLatin_e, chLatin_n, chLatin_t, chNull };
static const XMLCh cleanupInterval[] =
static const XMLCh storeAttributes[] =
{ 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 };
-class ShibMySQLCCache;
-class ShibMySQLCCacheEntry : public ISessionCacheEntry
+class MySQLBase
{
public:
- ShibMySQLCCacheEntry(const char* key, ISessionCacheEntry* entry, ShibMySQLCCache* cache)
- : m_cacheEntry(entry), m_key(key), m_cache(cache), m_responseId(NULL) {}
- ~ShibMySQLCCacheEntry() {if (m_responseId) XMLString::release(&m_responseId);}
-
- virtual void lock() {}
- virtual void unlock() { m_cacheEntry->unlock(); delete this; }
- virtual bool isValid(time_t lifetime, time_t timeout) const;
- virtual const char* getClientAddress() const { return m_cacheEntry->getClientAddress(); }
- virtual ShibProfile getProfile() const { return m_cacheEntry->getProfile(); }
- virtual const char* getProviderId() const { return m_cacheEntry->getProviderId(); }
- virtual const SAMLAuthenticationStatement* getAuthnStatement() const { return m_cacheEntry->getAuthnStatement(); }
- virtual CachedResponse getResponse();
+ MySQLBase(const DOMElement* e);
+ virtual ~MySQLBase();
-private:
- bool touch() const;
+ void thread_init();
+ void thread_end() {}
- ShibMySQLCCache* m_cache;
- ISessionCacheEntry* m_cacheEntry;
- string m_key;
- XMLCh* m_responseId;
-};
-
-class ShibMySQLCCache : public ISessionCache
-{
-public:
- ShibMySQLCCache(const DOMElement* e);
- virtual ~ShibMySQLCCache();
-
- virtual void thread_init();
- virtual void thread_end() {}
-
- virtual string generateKey() const {return m_cache->generateKey();}
- virtual ISessionCacheEntry* find(const char* key, const IApplication* application);
- virtual void insert(
- const char* key,
- const IApplication* application,
- const char* client_addr,
- ShibProfile profile,
- const char* providerId,
- saml::SAMLAuthenticationStatement* s,
- saml::SAMLResponse* r=NULL,
- const shibboleth::IRoleDescriptor* source=NULL,
- time_t created=0,
- time_t accessed=0
- );
- virtual void remove(const char* key);
-
- void cleanup();
MYSQL* getMYSQL() const;
bool repairTable(MYSQL*&, const char* table);
log4cpp::Category* log;
- bool m_storeAttributes;
-private:
- ISessionCache* m_cache;
+protected:
ThreadKey* m_mysql;
const DOMElement* m_root; // can only use this during initialization
- static void* cleanup_fcn(void*); // XXX Assumed an ShibMySQLCCache
- CondWait* shutdown_wait;
- bool shutdown;
- Thread* cleanup_thread;
-
bool initialized;
void createDatabase(MYSQL*, int major, int minor);
};
// Forward declarations
-extern "C" void shib_mysql_destroy_handle(void* data);
void mysqlInit(const DOMElement* e, Category& log);
-/*************************************************************************
- * The CCache here talks to a MySQL database. The database stores
- * three items: the cookie (session key index), the lastAccess time, and
- * the SAMLAuthenticationStatement. All other access is performed
- * through the memory cache provided by shibboleth.
- */
+extern "C" void shib_mysql_destroy_handle(void* data)
+{
+ MYSQL* mysql = (MYSQL*) data;
+ if (mysql) mysql_close(mysql);
+}
-MYSQL* ShibMySQLCCache::getMYSQL() const
+MySQLBase::MySQLBase(const DOMElement* e) : m_root(e)
+{
+#ifdef _DEBUG
+ saml::NDC ndc("MySQLBase");
+#endif
+ log = &(Category::getInstance("shibmysql.MySQLBase"));
+
+ m_mysql = ThreadKey::create(&shib_mysql_destroy_handle);
+
+ initialized = false;
+ mysqlInit(e,*log);
+ thread_init();
+ initialized = true;
+}
+
+MySQLBase::~MySQLBase()
+{
+ thread_end();
+
+ delete m_mysql;
+}
+
+MYSQL* MySQLBase::getMYSQL() const
{
return (MYSQL*)m_mysql->getData();
}
-void ShibMySQLCCache::thread_init()
+void MySQLBase::thread_init()
{
#ifdef _DEBUG
saml::NDC ndc("thread_init");
if (!mysql) {
log->error("mysql_init failed");
mysql_close(mysql);
- throw SAMLException("ShibMySQLCCache::thread_init(): mysql_init() failed");
+ throw SAMLException("MySQLBase::thread_init(): mysql_init() failed");
}
if (!mysql_real_connect(mysql, NULL, NULL, NULL, "shar", 0, NULL, 0)) {
if (initialized) {
log->crit("mysql_real_connect failed: %s", mysql_error(mysql));
mysql_close(mysql);
- throw SAMLException("ShibMySQLCCache::thread_init(): mysql_real_connect() failed");
+ throw SAMLException("MySQLBase::thread_init(): mysql_real_connect() failed");
} else {
log->info("mysql_real_connect failed: %s. Trying to create", mysql_error(mysql));
- // This will throw an exception if it fails.
- createDatabase(mysql, PLUGIN_VER_MAJOR, PLUGIN_VER_MINOR);
- }
- }
+ // This will throw an exception if it fails.
+ createDatabase(mysql, PLUGIN_VER_MAJOR, PLUGIN_VER_MINOR);
+ }
+ }
+
+ int major = -1, minor = -1;
+ getVersion (mysql, &major, &minor);
+
+ // Make sure we've got the right version
+ if (major != PLUGIN_VER_MAJOR || minor != PLUGIN_VER_MINOR) {
+
+ // If we're capable, try upgrading on the fly...
+ if (major == 0 || major == 1) {
+ upgradeDatabase(mysql);
+ }
+ else {
+ mysql_close(mysql);
+ log->crit("Unknown database version: %d.%d", major, minor);
+ throw SAMLException("MySQLBase::thread_init(): Unknown database version");
+ }
+ }
+
+ // We're all set.. Save off the handle for this thread.
+ m_mysql->setData(mysql);
+}
+
+bool MySQLBase::repairTable(MYSQL*& mysql, const char* table)
+{
+ string q = string("REPAIR TABLE ") + table;
+ if (mysql_query(mysql, q.c_str())) {
+ log->error("Error repairing table %s: %s", table, mysql_error(mysql));
+ return false;
+ }
+
+ // seems we have to recycle the connection to get the thread to keep working
+ // other threads seem to be ok, but we should monitor that
+ mysql_close(mysql);
+ m_mysql->setData(NULL);
+ thread_init();
+ mysql=getMYSQL();
+ return true;
+}
+
+void MySQLBase::createDatabase(MYSQL* mysql, int major, int minor)
+{
+ log->info("Creating database.");
+
+ MYSQL* ms = NULL;
+ try {
+ ms = mysql_init(NULL);
+ if (!ms) {
+ log->crit("mysql_init failed");
+ throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_init failed");
+ }
+
+ if (!mysql_real_connect(ms, NULL, NULL, NULL, NULL, 0, NULL, 0)) {
+ log->crit("cannot open DB file to create DB: %s", mysql_error(ms));
+ throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_real_connect failed");
+ }
+
+ if (mysql_query(ms, "CREATE DATABASE shar")) {
+ log->crit("cannot create shar database: %s", mysql_error(ms));
+ throw SAMLException("ShibMySQLCCache::createDatabase(): create db cmd failed");
+ }
+
+ if (!mysql_real_connect(mysql, NULL, NULL, NULL, "shar", 0, NULL, 0)) {
+ log->crit("cannot open SHAR database");
+ throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_real_connect to plugin db failed");
+ }
+
+ mysql_close(ms);
+
+ }
+ catch (SAMLException&) {
+ if (ms)
+ mysql_close(ms);
+ mysql_close(mysql);
+ throw;
+ }
+
+ // Now create the tables if they don't exist
+ log->info("Creating database tables");
+
+ if (mysql_query(mysql, "CREATE TABLE version (major INT, minor INT)")) {
+ log->error ("Error creating version: %s", mysql_error(mysql));
+ throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
+ }
+
+ if (mysql_query(mysql,STATE_TABLE)) {
+ log->error ("Error creating state table: %s", mysql_error(mysql));
+ throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
+ }
+
+ if (mysql_query(mysql,REPLAY_TABLE)) {
+ log->error ("Error creating replay table: %s", mysql_error(mysql));
+ throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
+ }
+
+ ostringstream q;
+ q << "INSERT INTO version VALUES(" << major << "," << minor << ")";
+ if (mysql_query(mysql, q.str().c_str())) {
+ log->error ("Error setting version: %s", mysql_error(mysql));
+ throw SAMLException("ShibMySQLCCache::createDatabase(): version insert failed");
+ }
+}
+
+void MySQLBase::upgradeDatabase(MYSQL* mysql)
+{
+ if (mysql_query(mysql, "DROP TABLE state")) {
+ log->error("Error dropping old session state table: %s", mysql_error(mysql));
+ }
+
+ if (mysql_query(mysql,STATE_TABLE)) {
+ log->error ("Error creating state table: %s", mysql_error(mysql));
+ throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error creating state table");
+ }
+
+ if (mysql_query(mysql,REPLAY_TABLE)) {
+ log->error ("Error creating replay table: %s", mysql_error(mysql));
+ throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error creating replay table");
+ }
+
+ ostringstream q;
+ q << "UPDATE version SET major = " << PLUGIN_VER_MAJOR;
+ if (mysql_query(mysql, q.str().c_str())) {
+ log->error ("Error updating version: %s", mysql_error(mysql));
+ throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error updating version");
+ }
+}
+
+void MySQLBase::getVersion(MYSQL* mysql, int* major_p, int* minor_p)
+{
+ // grab the version number from the database
+ if (mysql_query(mysql, "SELECT * FROM version"))
+ log->error ("Error reading version: %s", mysql_error(mysql));
+
+ MYSQL_RES* rows = mysql_store_result(mysql);
+ if (rows) {
+ if (mysql_num_rows(rows) == 1 && mysql_num_fields(rows) == 2) {
+ MYSQL_ROW row = mysql_fetch_row(rows);
+
+ int major = row[0] ? atoi(row[0]) : -1;
+ int minor = row[1] ? atoi(row[1]) : -1;
+ log->debug("opening database version %d.%d", major, minor);
+
+ mysql_free_result (rows);
+
+ *major_p = major;
+ *minor_p = minor;
+ return;
+
+ } else {
+ // Wrong number of rows or wrong number of fields...
+
+ log->crit("Houston, we've got a problem with the database...");
+ mysql_free_result (rows);
+ throw SAMLException("ShibMySQLCCache::getVersion(): version verification failed");
+ }
+ }
+ log->crit("MySQL Read Failed in version verificatoin");
+ throw SAMLException("ShibMySQLCCache::getVersion(): error reading version");
+}
+
+static void mysqlInit(const DOMElement* e, Category& log)
+{
+ static bool done = false;
+ if (done) {
+ log.info("MySQL embedded server already initialized");
+ return;
+ }
+ log.info("initializing MySQL embedded server");
+
+ // Setup the argument array
+ vector<string> arg_array;
+ arg_array.push_back("shibboleth");
+
+ // grab any MySQL parameters from the config file
+ e=saml::XML::getFirstChildElement(e,shibtarget::XML::SHIBTARGET_NS,Argument);
+ while (e) {
+ auto_ptr_char arg(e->getFirstChild()->getNodeValue());
+ if (arg.get())
+ arg_array.push_back(arg.get());
+ e=saml::XML::getNextSiblingElement(e,shibtarget::XML::SHIBTARGET_NS,Argument);
+ }
+
+ // Compute the argument array
+ int arg_count = arg_array.size();
+ const char** args=new const char*[arg_count];
+ for (int i = 0; i < arg_count; i++)
+ args[i] = arg_array[i].c_str();
+
+ // Initialize MySQL with the arguments
+ mysql_server_init(arg_count, (char **)args, NULL);
+
+ delete[] args;
+ done = true;
+}
+
+class ShibMySQLCCache;
+class ShibMySQLCCacheEntry : public ISessionCacheEntry
+{
+public:
+ ShibMySQLCCacheEntry(const char* key, ISessionCacheEntry* entry, ShibMySQLCCache* cache)
+ : m_cacheEntry(entry), m_key(key), m_cache(cache), m_responseId(NULL) {}
+ ~ShibMySQLCCacheEntry() {if (m_responseId) XMLString::release(&m_responseId);}
+
+ virtual void lock() {}
+ virtual void unlock() { m_cacheEntry->unlock(); delete this; }
+ virtual bool isValid(time_t lifetime, time_t timeout) const;
+ virtual const char* getClientAddress() const { return m_cacheEntry->getClientAddress(); }
+ virtual ShibProfile getProfile() const { return m_cacheEntry->getProfile(); }
+ virtual const char* getProviderId() const { return m_cacheEntry->getProviderId(); }
+ virtual const SAMLAuthenticationStatement* getAuthnStatement() const { return m_cacheEntry->getAuthnStatement(); }
+ virtual CachedResponse getResponse();
+
+private:
+ bool touch() const;
+
+ ShibMySQLCCache* m_cache;
+ ISessionCacheEntry* m_cacheEntry;
+ string m_key;
+ XMLCh* m_responseId;
+};
+
+class ShibMySQLCCache : public MySQLBase, virtual public ISessionCache
+{
+public:
+ ShibMySQLCCache(const DOMElement* e);
+ virtual ~ShibMySQLCCache();
+
+ virtual void thread_init() {MySQLBase::thread_init();}
+ virtual void thread_end() {MySQLBase::thread_end();}
+
+ virtual string generateKey() const {return m_cache->generateKey();}
+ virtual ISessionCacheEntry* find(const char* key, const IApplication* application);
+ virtual void insert(
+ const char* key,
+ const IApplication* application,
+ const char* client_addr,
+ ShibProfile profile,
+ const char* providerId,
+ saml::SAMLAuthenticationStatement* s,
+ saml::SAMLResponse* r=NULL,
+ const shibboleth::IRoleDescriptor* source=NULL,
+ time_t created=0,
+ time_t accessed=0
+ );
+ virtual void remove(const char* key);
+
+ virtual void cleanup();
- int major = -1, minor = -1;
- getVersion (mysql, &major, &minor);
+ bool m_storeAttributes;
- // Make sure we've got the right version
- if (major != PLUGIN_VER_MAJOR || minor != PLUGIN_VER_MINOR) {
-
- // If we're capable, try upgrading on the fly...
- if (major == 0 || major == 1) {
- upgradeDatabase(mysql);
- }
- else {
- mysql_close(mysql);
- log->crit("Unknown database version: %d.%d", major, minor);
- throw SAMLException("ShibMySQLCCache::thread_init(): Unknown database version");
- }
- }
+private:
+ ISessionCache* m_cache;
+ CondWait* shutdown_wait;
+ bool shutdown;
+ Thread* cleanup_thread;
- // We're all set.. Save off the handle for this thread.
- m_mysql->setData(mysql);
-}
+ static void* cleanup_fcn(void*); // XXX Assumed an ShibMySQLCCache
+};
-ShibMySQLCCache::ShibMySQLCCache(const DOMElement* e) : m_root(e), m_storeAttributes(false)
+ShibMySQLCCache::ShibMySQLCCache(const DOMElement* e) : MySQLBase(e), m_storeAttributes(false)
{
#ifdef _DEBUG
- saml::NDC ndc("shibmysql::ShibMySQLCCache");
+ saml::NDC ndc("ShibMySQLCCache");
#endif
- m_mysql = ThreadKey::create(&shib_mysql_destroy_handle);
- log = &(Category::getInstance("shibmysql::ShibMySQLCCache"));
+ log = &(Category::getInstance("shibmysql.SessionCache"));
- initialized = false;
- mysqlInit(e,*log);
- thread_init();
- initialized = true;
+ shutdown_wait = CondWait::create();
+ shutdown = false;
m_cache = dynamic_cast<ISessionCache*>(
SAMLConfig::getConfig().getPlugMgr().newPlugin(
m_storeAttributes=true;
// Initialize the cleanup thread
- shutdown_wait = CondWait::create();
- shutdown = false;
cleanup_thread = Thread::create(&cleanup_fcn, (void*)this);
}
shutdown_wait->signal();
cleanup_thread->join(NULL);
- thread_end();
delete m_cache;
- delete m_mysql;
-
- // Shutdown MySQL
- mysql_server_end();
}
ISessionCacheEntry* ShibMySQLCCache::find(const char* key, const IApplication* application)
{
#ifdef _DEBUG
- saml::NDC ndc("ShibMySQLCCache::find");
+ saml::NDC ndc("find");
#endif
ISessionCacheEntry* res = m_cache->find(key, application);
)
{
#ifdef _DEBUG
- saml::NDC ndc("ShibMySQLCCache::insert");
+ saml::NDC ndc("insert");
#endif
ostringstream q;
log->error("Error inserting %s: %s", key, err);
if (isCorrupt(err) && repairTable(mysql,"state")) {
// Try again...
- if (mysql_query(mysql, q.str().c_str()))
+ if (mysql_query(mysql, q.str().c_str())) {
log->error("Error inserting %s: %s", key, mysql_error(mysql));
- throw SAMLException("ShibMySQLCCache::insert(): inset failed");
+ throw SAMLException("ShibMySQLCCache::insert(): insertion failed");
+ }
}
+ else
+ throw SAMLException("ShibMySQLCCache::insert(): insertion failed");
}
// Add it to the memory cache
void ShibMySQLCCache::remove(const char* key)
{
#ifdef _DEBUG
- saml::NDC ndc("ShibMySQLCCache::remove");
+ saml::NDC ndc("remove");
#endif
// Remove the cached version
void ShibMySQLCCache::cleanup()
{
#ifdef _DEBUG
- saml::NDC ndc("ShibMySQLCCache::cleanup");
+ saml::NDC ndc("cleanup");
#endif
Mutex* mutex = Mutex::create();
- thread_init();
+ MySQLBase::thread_init();
int rerun_timer = 0;
int timeout_life = 0;
mutex->unlock();
delete mutex;
- thread_end();
+ MySQLBase::thread_end();
Thread::exit(NULL);
}
return NULL;
}
-bool ShibMySQLCCache::repairTable(MYSQL*& mysql, const char* table)
-{
- string q = string("REPAIR TABLE ") + table;
- if (mysql_query(mysql, q.c_str())) {
- log->error("Error repairing table %s: %s", table, mysql_error(mysql));
- return false;
- }
-
- // seems we have to recycle the connection to get the thread to keep working
- // other threads seem to be ok, but we should monitor that
- mysql_close(mysql);
- m_mysql->setData(NULL);
- thread_init();
- mysql=getMYSQL();
- return true;
-}
-
-void ShibMySQLCCache::createDatabase(MYSQL* mysql, int major, int minor)
-{
- log->info("Creating database.");
-
- MYSQL* ms = NULL;
- try {
- ms = mysql_init(NULL);
- if (!ms) {
- log->crit("mysql_init failed");
- throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_init failed");
- }
-
- if (!mysql_real_connect(ms, NULL, NULL, NULL, NULL, 0, NULL, 0)) {
- log->crit("cannot open DB file to create DB: %s", mysql_error(ms));
- throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_real_connect failed");
- }
-
- if (mysql_query(ms, "CREATE DATABASE shar")) {
- log->crit("cannot create shar database: %s", mysql_error(ms));
- throw SAMLException("ShibMySQLCCache::createDatabase(): create db cmd failed");
- }
-
- if (!mysql_real_connect(mysql, NULL, NULL, NULL, "shar", 0, NULL, 0)) {
- log->crit("cannot open SHAR database");
- throw SAMLException("ShibMySQLCCache::createDatabase(): mysql_real_connect to shar db failed");
- }
-
- mysql_close(ms);
-
- }
- catch (SAMLException&) {
- if (ms)
- mysql_close(ms);
- mysql_close(mysql);
- throw;
- }
-
- // Now create the tables if they don't exist
- log->info("Creating database tables");
-
- if (mysql_query(mysql, "CREATE TABLE version (major INT, minor INT)")) {
- log->error ("Error creating version: %s", mysql_error(mysql));
- throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
- }
-
- if (mysql_query(mysql,STATE_TABLE)) {
- log->error ("Error creating state: %s", mysql_error(mysql));
- throw SAMLException("ShibMySQLCCache::createDatabase(): create table cmd failed");
- }
-
- ostringstream q;
- q << "INSERT INTO version VALUES(" << major << "," << minor << ")";
- if (mysql_query(mysql, q.str().c_str())) {
- log->error ("Error setting version: %s", mysql_error(mysql));
- throw SAMLException("ShibMySQLCCache::createDatabase(): version insert failed");
- }
-}
-
-void ShibMySQLCCache::upgradeDatabase(MYSQL* mysql)
-{
- if (mysql_query(mysql, "DROP TABLE state")) {
- log->error("Error dropping old session state table: %s", mysql_error(mysql));
- }
-
- if (mysql_query(mysql,STATE_TABLE)) {
- log->error ("Error creating state table: %s", mysql_error(mysql));
- throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error creating state table");
- }
-
- ostringstream q;
- q << "UPDATE version SET major = " << PLUGIN_VER_MAJOR;
- if (mysql_query(mysql, q.str().c_str())) {
- log->error ("Error updating version: %s", mysql_error(mysql));
- throw SAMLException("ShibMySQLCCache::upgradeDatabase(): error updating version");
- }
-}
-
-void ShibMySQLCCache::getVersion(MYSQL* mysql, int* major_p, int* minor_p)
-{
- // grab the version number from the database
- if (mysql_query(mysql, "SELECT * FROM version"))
- log->error ("Error reading version: %s", mysql_error(mysql));
-
- MYSQL_RES* rows = mysql_store_result(mysql);
- if (rows) {
- if (mysql_num_rows(rows) == 1 && mysql_num_fields(rows) == 2) {
- MYSQL_ROW row = mysql_fetch_row(rows);
-
- int major = row[0] ? atoi(row[0]) : -1;
- int minor = row[1] ? atoi(row[1]) : -1;
- log->debug("opening database version %d.%d", major, minor);
-
- mysql_free_result (rows);
-
- *major_p = major;
- *minor_p = minor;
- return;
-
- } else {
- // Wrong number of rows or wrong number of fields...
-
- log->crit("Houston, we've got a problem with the database...");
- mysql_free_result (rows);
- throw SAMLException("ShibMySQLCCache::getVersion(): version verification failed");
- }
- }
- log->crit("MySQL Read Failed in version verificatoin");
- throw SAMLException("ShibMySQLCCache::getVersion(): error reading version");
-}
-
-void mysqlInit(const DOMElement* e, Category& log)
-{
- static bool done = false;
- if (done) {
- log.info("MySQL embedded server already initialized");
- return;
- }
- log.info("initializing MySQL embedded server");
-
- // Setup the argument array
- vector<string> arg_array;
- arg_array.push_back("shibboleth");
-
- // grab any MySQL parameters from the config file
- e=saml::XML::getFirstChildElement(e,shibtarget::XML::SHIBTARGET_NS,Argument);
- while (e) {
- auto_ptr_char arg(e->getFirstChild()->getNodeValue());
- if (arg.get())
- arg_array.push_back(arg.get());
- e=saml::XML::getNextSiblingElement(e,shibtarget::XML::SHIBTARGET_NS,Argument);
- }
-
- // Compute the argument array
- int arg_count = arg_array.size();
- const char** args=new const char*[arg_count];
- for (int i = 0; i < arg_count; i++)
- args[i] = arg_array[i].c_str();
-
- // Initialize MySQL with the arguments
- mysql_server_init(arg_count, (char **)args, NULL);
-
- delete[] args;
- done = true;
-}
-
/*************************************************************************
* The CCacheEntry here is mostly a wrapper around the "memory"
* cacheentry provided by shibboleth. The only difference is that we
MYSQL* mysql = m_cache->getMYSQL();
if (mysql_query(mysql, q.c_str())) {
- m_cache->log->info("Error updating timestamp on %s: %s",
- m_key.c_str(), mysql_error(mysql));
+ m_cache->log->info("Error updating timestamp on %s: %s", m_key.c_str(), mysql_error(mysql));
return false;
}
return true;
return r;
}
+class MySQLReplayCache : public MySQLBase, virtual public IReplayCache
+{
+public:
+ MySQLReplayCache(const DOMElement* e);
+ virtual ~MySQLReplayCache() {}
+
+ void thread_init() {MySQLBase::thread_init();}
+ void thread_end() {MySQLBase::thread_end();}
+
+ bool check(const XMLCh* str, time_t expires) {auto_ptr_XMLCh temp(str); return check(temp.get(),expires);}
+ bool check(const char* str, time_t expires);
+};
+
+MySQLReplayCache::MySQLReplayCache(const DOMElement* e) : MySQLBase(e)
+{
+#ifdef _DEBUG
+ saml::NDC ndc("MySQLReplayCache");
+#endif
+
+ log = &(Category::getInstance("shibmysql.ReplayCache"));
+}
+
+bool MySQLReplayCache::check(const char* str, time_t expires)
+{
+#ifdef _DEBUG
+ saml::NDC ndc("check");
+#endif
+
+ // Remove expired entries
+ string q = string("DELETE FROM replay WHERE expires < NOW()");
+ MYSQL* mysql = getMYSQL();
+ if (mysql_query(mysql, q.c_str())) {
+ const char* err=mysql_error(mysql);
+ log->error("Error deleting expired entries: %s", err);
+ if (isCorrupt(err) && repairTable(mysql,"replay")) {
+ // Try again...
+ if (mysql_query(mysql, q.c_str()))
+ log->error("Error deleting expired entries: %s", mysql_error(mysql));
+ }
+ }
+
+ string q2 = string("SELECT id FROM replay WHERE id='") + str + "'";
+ if (mysql_query(mysql, q2.c_str())) {
+ const char* err=mysql_error(mysql);
+ log->error("Error searching for %s: %s", str, err);
+ if (isCorrupt(err) && repairTable(mysql,"replay")) {
+ if (mysql_query(mysql, q2.c_str())) {
+ log->error("Error retrying search for %s: %s", str, mysql_error(mysql));
+ throw SAMLException("Replay cache failed, please inform application support staff.");
+ }
+ }
+ else
+ throw SAMLException("Replay cache failed, please inform application support staff.");
+ }
+
+ // Did we find it?
+ MYSQL_RES* rows = mysql_store_result(mysql);
+ if (rows && mysql_num_rows(rows)>0) {
+ mysql_free_result(rows);
+ return false;
+ }
+
+ ostringstream q3;
+ q3 << "INSERT INTO replay VALUES('" << str << "'," << "FROM_UNIXTIME(" << expires << "))";
+
+ // then add it to the database
+ if (mysql_query(mysql, q3.str().c_str())) {
+ const char* err=mysql_error(mysql);
+ log->error("Error inserting %s: %s", str, err);
+ if (isCorrupt(err) && repairTable(mysql,"state")) {
+ // Try again...
+ if (mysql_query(mysql, q3.str().c_str())) {
+ log->error("Error inserting %s: %s", str, mysql_error(mysql));
+ throw SAMLException("Replay cache failed, please inform application support staff.");
+ }
+ }
+ else
+ throw SAMLException("Replay cache failed, please inform application support staff.");
+ }
+
+ return true;
+}
+
/*************************************************************************
* The registration functions here...
*/
IPlugIn* new_mysql_replay(const DOMElement* e)
{
- return NULL;
+ return new MySQLReplayCache(e);
}
#define REPLAYPLUGINTYPE "edu.internet2.middleware.shibboleth.sp.provider.MySQLReplayCacheProvider"
extern "C" void SHIBMYSQL_EXPORTS saml_extension_term()
{
+ // Shutdown MySQL
+ mysql_server_end();
SAMLConfig::getConfig().getPlugMgr().unregFactory(REPLAYPLUGINTYPE);
SAMLConfig::getConfig().getPlugMgr().unregFactory(SESSIONPLUGINTYPE);
}
-
-/*************************************************************************
- * Local Functions
- */
-
-extern "C" void shib_mysql_destroy_handle(void* data)
-{
- MYSQL* mysql = (MYSQL*) data;
- mysql_close(mysql);
-}