diff --git a/server/modules/authenticator/MySQLAuth/dbusers.c b/server/modules/authenticator/MySQLAuth/dbusers.c index 106686773..3c04c3839 100644 --- a/server/modules/authenticator/MySQLAuth/dbusers.c +++ b/server/modules/authenticator/MySQLAuth/dbusers.c @@ -42,6 +42,7 @@ /** MySQL 5.7 password column name */ #define MYSQL57_PASSWORD "authentication_string" +// Query used with 10.0 or older #define NEW_LOAD_DBUSERS_QUERY "SELECT u.user, u.host, d.db, u.select_priv, u.%s \ FROM mysql.user AS u LEFT JOIN mysql.db AS d \ ON (u.user = d.user AND u.host = d.host) WHERE u.plugin IN ('', 'mysql_native_password') %s \ @@ -50,7 +51,32 @@ FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t \ ON (u.user = t.user AND u.host = t.host) WHERE u.plugin IN ('', 'mysql_native_password') %s" - // Query used with MariaDB 10.1 and newer, supports roles +// Used with 10.2 or newer, supports composite roles +const char* mariadb_102_users_query = + // `t` is users that are not roles + "WITH RECURSIVE t AS ( " + " SELECT u.user, u.host, d.db, u.select_priv, u.password AS password, u.is_role, u.default_role" + " FROM mysql.user AS u LEFT JOIN mysql.db AS d " + " ON (u.user = d.user AND u.host = d.host) " + " UNION " + " SELECT u.user, u.host, t.db, u.select_priv, u.password AS password, u.is_role, u.default_role " + " FROM mysql.user AS u LEFT JOIN mysql.tables_priv AS t " + " ON (u.user = t.user AND u.host = t.host)" + "), users AS (" + // Select the root row, the actual user + " SELECT t.user, t.host, t.db, t.select_priv, t.password, t.default_role AS role FROM t" + " WHERE t.is_role <> 'Y'" + " UNION" + // Recursively select all roles for the users + " SELECT u.user, u.host, t.db, t.select_priv, u.password, r.role FROM t" + " JOIN users AS u" + " ON (t.user = u.role)" + " LEFT JOIN mysql.roles_mapping AS r" + " ON (t.user = r.user)" + ")" + "SELECT DISTINCT t.user, t.host, t.db, t.select_priv, t.password FROM users AS t %s"; + +// Query used with MariaDB 10.1, supports basic roles const char* mariadb_users_query = // First, select all users "SELECT t.user, t.host, t.db, t.select_priv, t.password FROM " @@ -101,6 +127,18 @@ static int gw_mysql_set_timeouts(MYSQL* handle); static char *mysql_format_user_entry(void *data); static bool get_hostname(DCB *dcb, char *client_hostname, size_t size); +static char* get_mariadb_102_users_query(bool include_root) +{ + const char *root = include_root ? "" : " WHERE t.user <> 'root'"; + + size_t n_bytes = snprintf(NULL, 0, mariadb_102_users_query, root); + char *rval = MXS_MALLOC(n_bytes + 1); + MXS_ABORT_IF_NULL(rval); + snprintf(rval, n_bytes + 1, mariadb_102_users_query, root); + + return rval; +} + static char* get_mariadb_users_query(bool include_root) { const char *root = include_root ? "" : " AND t.user NOT IN ('root')"; @@ -113,11 +151,13 @@ static char* get_mariadb_users_query(bool include_root) return rval; } -static char* get_users_query(const char *server_version, bool include_root, bool is_mariadb) +static char* get_users_query(const char *server_version, int version, bool include_root, bool is_mariadb) { if (is_mariadb) // 10.1.1 or newer, supports default roles { - return get_mariadb_users_query(include_root); + return version >= 100202 ? + get_mariadb_102_users_query(include_root) : + get_mariadb_users_query(include_root); } // Either an older MariaDB version or a MySQL variant, use the legacy query @@ -827,7 +867,9 @@ int get_users_from_server(MYSQL *con, SERVER_REF *server_ref, SERVICE *service, mxs_mysql_set_server_version(con, server_ref->server); } - char *query = get_users_query(server_ref->server->version_string, service->enable_root, + char *query = get_users_query(server_ref->server->version_string, + server_ref->server->version, + service->enable_root, roles_are_available(con, service, server_ref->server)); MYSQL_AUTH *instance = (MYSQL_AUTH*)listener->auth_instance; @@ -837,7 +879,8 @@ int get_users_from_server(MYSQL *con, SERVER_REF *server_ref, SERVICE *service, if (query) { - if (mxs_mysql_query(con, query) == 0) + if (mxs_mysql_query(con, "USE mysql") == 0 && // Set default database in case we use CTEs + mxs_mysql_query(con, query) == 0) { MYSQL_RES *result = mysql_store_result(con);