Store SQLite tables on disk

The SQLite database users are cached on disk. This allows the binlogrouter
to authenticate users without a connection to the master server.
This commit is contained in:
Markus Mäkelä 2017-01-28 22:04:26 +02:00
parent 2f4df0c21e
commit 62763e2505
3 changed files with 120 additions and 34 deletions

View File

@ -128,8 +128,7 @@ See earlier error messages for user '%s' for more information."
UNION \
SELECT u.user, u.host, t.db, u.select_priv, u.%s \
FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t \
ON (u.user = t.user AND u.host = t.host) %s\
ORDER BY user"
ON (u.user = t.user AND u.host = t.host) %s"
static int add_databases(SERV_LISTENER *listener, MYSQL *con);
static int add_wildcard_users(USERS *users, char* name, char* host,
@ -228,7 +227,7 @@ static char* get_usercount_query(const char* server_version, bool include_root,
static char* get_new_users_query(const char *server_version, bool include_root)
{
const char* password = strstr(server_version, "5.7.") ? MYSQL57_PASSWORD : MYSQL_PASSWORD;
const char *with_root = include_root ? "user.user NOT IN ('root')" : "";
const char *with_root = include_root ? "WHERE u.user NOT IN ('root')" : "";
size_t n_bytes = snprintf(NULL, 0, NEW_LOAD_DBUSERS_QUERY, password, with_root, password, with_root);
char *rval = MXS_MALLOC(n_bytes + 1);
@ -1374,8 +1373,8 @@ static void delete_mysql_users(sqlite3 *handle)
* @param db Database
* @param anydb Global access to databases
*/
static void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
const char *db, bool anydb, const char *pw)
void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
const char *db, bool anydb, const char *pw)
{
size_t dblen = db && *db ? strlen(db) + 2 : sizeof(null_token); /** +2 for single quotes */
char dbstr[dblen + 1];
@ -2688,30 +2687,106 @@ dbusers_valueread(int fd)
return (void *) value;
}
/**
* Save the dbusers data to a hashtable file
*
* @param users The hashtable that stores the user data
* @param filename The filename to save the data in
* @return The number of entries saved
*/
int
dbusers_save(USERS *users, const char *filename)
int dump_user_cb(void *data, int fields, char **row, char **field_names)
{
return hashtable_save(users->data, filename, dbusers_keywrite, dbusers_valuewrite);
sqlite3 *handle = (sqlite3*)data;
add_mysql_user(handle, row[0], row[1], row[2], row[3] && strcmp(row[3], "1"), row[4]);
return 0;
}
int dump_database_cb(void *data, int fields, char **row, char **field_names)
{
sqlite3 *handle = (sqlite3*)data;
add_database(handle, row[0]);
return 0;
}
static bool transfer_table_contents(sqlite3 *src, sqlite3 *dest)
{
bool rval = true;
char *err;
/** Make sure the tables exist in both databases */
if (sqlite3_exec(src, users_create_sql, NULL, NULL, &err) != SQLITE_OK ||
sqlite3_exec(dest, users_create_sql, NULL, NULL, &err) != SQLITE_OK ||
sqlite3_exec(src, databases_create_sql, NULL, NULL, &err) != SQLITE_OK ||
sqlite3_exec(dest, databases_create_sql, NULL, NULL, &err) != SQLITE_OK)
{
MXS_ERROR("Failed to create tables: %s", err);
sqlite3_free(err);
rval = false;
}
if (sqlite3_exec(dest, "BEGIN", NULL, NULL, &err) != SQLITE_OK)
{
MXS_ERROR("Failed to start transaction: %s", err);
sqlite3_free(err);
rval = false;
}
/** Replace the data */
if (sqlite3_exec(src, dump_users_query, dump_user_cb, dest, &err) != SQLITE_OK ||
sqlite3_exec(src, dump_databases_query, dump_database_cb, dest, &err) != SQLITE_OK)
{
MXS_ERROR("Failed to load database contents: %s", err);
sqlite3_free(err);
rval = false;
}
if (sqlite3_exec(dest, "COMMIT", NULL, NULL, &err) != SQLITE_OK)
{
MXS_ERROR("Failed to commit transaction: %s", err);
sqlite3_free(err);
rval = false;
}
return rval;
}
/**
* Load the dbusers data from a saved hashtable file
* Load users from persisted database
*
* @param users The hashtable that stores the user data
* @param filename The filename to laod the data from
* @return The number of entries loaded
* @param dest Open SQLite handle where contents are loaded
*
* @return True on success
*/
int
dbusers_load(USERS *users, const char *filename)
bool dbusers_load(sqlite3 *dest, const char *filename)
{
return hashtable_load(users->data, filename, dbusers_keyread, dbusers_valueread);
sqlite3 *src;
if (sqlite3_open_v2(filename, &src, db_flags, NULL) != SQLITE_OK)
{
MXS_ERROR("Failed to open persisted SQLite3 database.");
return false;
}
bool rval = transfer_table_contents(src, dest);
sqlite3_close_v2(src);
return rval;
}
/**
* Save users to persisted database
*
* @param dest Open SQLite handle where contents are stored
*
* @return True on success
*/
bool dbusers_save(sqlite3 *src, const char *filename)
{
sqlite3 *dest;
if (sqlite3_open_v2(filename, &dest, db_flags, NULL) != SQLITE_OK)
{
MXS_ERROR("Failed to open persisted SQLite3 database.");
return -1;
}
bool rval = transfer_table_contents(src, dest);
sqlite3_close_v2(dest);
return rval;
}
/**
@ -2861,11 +2936,12 @@ static bool check_server_permissions(SERVICE *service, SERVER* server,
server_set_version_string(server, server_string);
}
char query[MAX_QUERY_STR_LEN];
const char *template = "SELECT user, host, %s, Select_priv FROM mysql.user limit 1";
const char* query_pw = strstr(server->server_string, "5.7.") ?
MYSQL57_PASSWORD : MYSQL_PASSWORD;
MYSQL57_PASSWORD : MYSQL_PASSWORD;
char query[strlen(template) + strlen(query_pw) + 1];
bool rval = true;
snprintf(query, sizeof(query), "SELECT user, host, %s, Select_priv FROM mysql.user limit 1", query_pw);
sprintf(query, template, query_pw);
if (mysql_query(mysql, query) != 0)
{

View File

@ -878,8 +878,9 @@ static bool add_service_user(SERV_LISTENER *port)
if (newpw)
{
add_mysql_users_with_host_ipv4(port->users, user, "%", newpw, "Y", "");
add_mysql_users_with_host_ipv4(port->users, user, "localhost", newpw, "Y", "");
MYSQL_AUTH *inst = (MYSQL_AUTH*)port->auth_instance;
add_mysql_user(inst->handle, user, "%", newpw, "Y", "");
add_mysql_user(inst->handle, user, "localhost", newpw, "Y", "");
MXS_FREE(newpw);
rval = true;
}
@ -937,7 +938,7 @@ static int mysql_auth_load_users(SERV_LISTENER *port)
strcat(path, DBUSERS_FILE);
if ((loaded = dbusers_load(port->users, path)) == -1)
if (!dbusers_load(instance->handle, path))
{
MXS_ERROR("[%s] Failed to load cached users from '%s'.", service->name, path);
rc = MXS_AUTH_LOADUSERS_ERROR;
@ -963,7 +964,7 @@ static int mysql_auth_load_users(SERV_LISTENER *port)
if (mxs_mkdir_all(path, 0777))
{
strcat(path, DBUSERS_FILE);
dbusers_save(port->users, path);
dbusers_save(instance->handle, path);
MXS_INFO("[%s] Storing cached credential information at '%s'.", service->name, path);
}
}

View File

@ -39,7 +39,7 @@ MXS_BEGIN_DECLS
/** Cache directory and file names */
static const char DBUSERS_DIR[] = "cache";
static const char DBUSERS_FILE[] = "dbusers";
static const char DBUSERS_FILE[] = "dbusers.db";
#define MYSQLAUTH_DATABASE_NAME "file:mysqlauth.db?mode=memory&cache=shared"
@ -76,11 +76,17 @@ static const char delete_databases_query[] = "DELETE FROM " MYSQLAUTH_DATABASES_
/** The insert query template which adds users to the mysqlauth_users table */
static const char insert_user_query[] =
"INSERT INTO " MYSQLAUTH_USERS_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)";
"INSERT OR REPLACE INTO " MYSQLAUTH_USERS_TABLE_NAME " VALUES ('%s', '%s', %s, %s, %s)";
/** The insert query template which adds the databases to the table */
static const char insert_database_query[] =
"INSERT INTO " MYSQLAUTH_DATABASES_TABLE_NAME " VALUES ('%s')";
"INSERT OR REPLACE INTO " MYSQLAUTH_DATABASES_TABLE_NAME " VALUES ('%s')";
static const char dump_users_query[] =
"SELECT user, host, db, anydb, password FROM " MYSQLAUTH_USERS_TABLE_NAME;
static const char dump_databases_query[] =
"SELECT db FROM " MYSQLAUTH_DATABASES_TABLE_NAME;
/** Used for NULL value creation in the INSERT query */
static const char null_token[] = "NULL";
@ -117,11 +123,14 @@ typedef struct mysql_user_host_key
char hostname[MYSQL_HOST_MAXLEN + 1];
} MYSQL_USER_HOST;
void add_mysql_user(sqlite3 *handle, const char *user, const char *host,
const char *db, bool anydb, const char *pw);
extern int add_mysql_users_with_host_ipv4(USERS *users, const char *user, const char *host,
char *passwd, const char *anydb, const char *db);
extern bool check_service_permissions(SERVICE* service);
extern int dbusers_load(USERS *, const char *filename);
extern int dbusers_save(USERS *, const char *filename);
extern bool dbusers_load(sqlite3 *handle, const char *filename);
extern bool dbusers_save(sqlite3 *src, const char *filename);
extern int mysql_users_add(USERS *users, MYSQL_USER_HOST *key, char *auth);
extern USERS *mysql_users_alloc();
extern char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);