You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
proxysql/test/tap/tests/reg_test_3504-change_user_h...

316 lines
7.5 KiB

/**
* @file reg_test_3504-change_user_helper.cpp
* @brief This is a helper file to connect and execute a 'COM_CHANGE_USER' using
* 'libmariadb'/'libmysql' client library. The library election should be
* performed by means of the macro 'LIBMYSQL_HELPER', when specified, the file
* should be compiled against 'libmysql' library, when not, against 'libmariadb'.
* It receives the inputs parameters for making the connection as a JSON from
* the calling test and also returns it's output as a JSON.
*
* Success JSON format:
* {
* "def_auth_plugin": Default auth plugin that was used by the
* client for the connection.
* "switching_auth_type": The 'switching_auth_type' that was required in the
* connection, obtained via ProxySQL internal session.
* "ssl_enabled": Confirmation that SSL is enabled in ProxySQL connection,
* obtained via ProxySQL internal session.
* }
*
* Failure JSON format:
* {
* "err_msg": Error message holding the reason for the failed execution.
* }
*/
#include <cstring>
#include <vector>
#include <string>
#include <stdio.h>
#include <numeric>
#include <tuple>
#include <iostream>
#include "mysql.h"
#include "mysqld_error.h"
#include "proxysql_utils.h"
#include "json.hpp"
#include "tap.h"
#include "utils.h"
using nlohmann::json;
using std::vector;
using std::string;
void parse_result_json_column(MYSQL_RES *result, json& j) {
if(!result) return;
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
j = json::parse(row[0]);
}
}
bool check_present_and_type(
const json& j, const std::vector<std::string>& path, const json::value_t& type
) {
bool res = false;
json cur_j {};
cur_j = j;
for (const auto& step : path) {
bool cont_res = cur_j.contains(step);
if (cont_res) {
cur_j = cur_j.at(step);
if (&step == &path.back()) {
return type == cur_j.type();
}
} else {
break;
}
}
return res;
}
json extract_nested_elem(
const json& j, const std::vector<std::string>& path
) {
json cur_j {};
cur_j = j;
for (const auto& step : path) {
if (cur_j.contains(step)) {
cur_j = cur_j.at(step);
if (&step == &path.back()) {
return cur_j;
}
} else {
break;
}
}
return cur_j;
}
int get_session_user_info(MYSQL* proxysql, std::string& user_info) {
int res = EXIT_FAILURE;
json j_status;
int query_res = mysql_query(proxysql, "PROXYSQL INTERNAL SESSION");
if (query_res) {
return query_res;
}
MYSQL_RES* tr_res = mysql_store_result(proxysql);
parse_result_json_column(tr_res, j_status);
mysql_free_result(tr_res);
std::string tmp_user_info {};
std::vector<std::string> info_path { "client", "userinfo", "username" };
json::value_t info_type = json::value_t::string;
if (check_present_and_type(j_status, info_path, info_type)) {
json j_user = extract_nested_elem(j_status, info_path);
if (!j_user.empty()) {
user_info = j_user.get<std::string>();
res = EXIT_SUCCESS;
}
}
return res;
}
int main(int argc, char** argv) {
nlohmann::json output {};
std::string err_msg {};
int res = EXIT_SUCCESS;
// Extract options
std::string user {};
std::string pass {};
std::string ch_user {};
std::string ch_pass {};
std::string auth {};
std::string charset {};
int port;
bool SSL;
bool CHANGE_USER;
// MySQL handle
MYSQL mysql;
mysql_init(&mysql);
// Real 'AUTH' after connection
char* default_auth = nullptr;
// MySQL connection attempt result
MYSQL* conn_res = nullptr;
if (argc != 2) {
output["err_msg"] = "Invalid number of paramenters. Argc: '"
+ std::to_string(argc) + "'";
res = EXIT_FAILURE;
goto exit;
} else {
try {
nlohmann::json input = json::parse(argv[1]);
user = input.at("user");
pass = input.at("pass");
ch_user = input.at("ch_user");
ch_pass = input.at("ch_pass");
auth = input.at("auth");
charset = input.at("charset");
port = input.at("port");
SSL = input.at("SSL");
CHANGE_USER = input.at("CHANGE_USER");
} catch (std::exception& ex) {
output["err_msg"] =
std::string { "Exception while parsing input parameter: '" } +
ex.what() + "'";
res = EXIT_FAILURE;
goto exit;
}
}
// Default options if not set
if (port == 0) {
port = 6033;
}
// options
mysql_options(&mysql, MYSQL_DEFAULT_AUTH, auth.c_str());
if (auth == "mysql_clear_password") {
bool enable_cleartext = true;
mysql_options(&mysql, MYSQL_ENABLE_CLEARTEXT_PLUGIN, &enable_cleartext);
}
if (charset != "") {
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, "utf8mb4");
}
#ifdef LIBMYSQL_HELPER
if (SSL == false) {
enum mysql_ssl_mode ssl_mode = SSL_MODE_DISABLED;
mysql_options(&mysql, MYSQL_OPT_SSL_MODE, &ssl_mode);
}
if (
!mysql_real_connect(
&mysql, "127.0.0.1", user.c_str(), pass.c_str(), "information_schema",
port, NULL, 0
)
) {
string_format("Failed to connect to database: Error: %s", err_msg, mysql_error(&mysql));
output["err_msg"] = err_msg;
res = EXIT_FAILURE;
goto exit;
}
#else
if (SSL == true) {
mysql_ssl_set(&mysql, NULL, NULL, NULL, NULL, NULL);
conn_res = mysql_real_connect(
&mysql, "127.0.0.1", user.c_str(), pass.c_str(), "information_schema",
port, NULL, CLIENT_SSL
);
} else {
conn_res = mysql_real_connect(
&mysql, "127.0.0.1", user.c_str(), pass.c_str(), "information_schema",
port, NULL, 0
);
}
if (!conn_res) {
string_format("Failed to connect to database: Error: %s", err_msg, mysql_error(&mysql));
output["err_msg"] = err_msg;
res = EXIT_FAILURE;
goto exit;
}
#endif
mysql_get_option(&mysql, MYSQL_DEFAULT_AUTH, &default_auth);
output["def_auth_plugin"] = std::string { default_auth };
{
json j_status;
MYSQL_QUERY(&mysql, "PROXYSQL INTERNAL SESSION");
MYSQL_RES* tr_res = mysql_store_result(&mysql);
parse_result_json_column(tr_res, j_status);
mysql_free_result(tr_res);
int auth_selected = -1;
bool ssl_enabled = false;
try {
auth_selected = j_status.at("client").at("switching_auth_type");
ssl_enabled = j_status.at("client").at("encrypted");
} catch (const std::exception& ex) {
string_format(
"Exception getting fields from 'PROXYSQL INTERNAL SESSION': '%s'",
err_msg, ex.what()
);
output["err_msg"] = err_msg;
res = EXIT_FAILURE;
goto exit;
}
output["switching_auth_type"] = auth_selected;
output["ssl_enabled"] = ssl_enabled;
}
{
const auto change_user_and_check = [&](const std::string user, const std::string pass, int num) -> int {
int tmp_res = EXIT_SUCCESS;
if (CHANGE_USER) {
if (mysql_change_user(&mysql, user.c_str(), pass.c_str(), "information_schema")) {
string_format("Failed to change user. Error: %s", err_msg, mysql_error(&mysql));
output["err_msg"] = err_msg;
tmp_res = EXIT_FAILURE;
}
}
if (tmp_res == EXIT_SUCCESS) {
std::string username {};
int info_err = get_session_user_info(&mysql, username);
if (info_err) {
output["err_msg"] = "Unable to get client user info from 'PROXYSQL INTERNAL SESSION'";
tmp_res = EXIT_FAILURE;
} else {
output["client_com_change_user_" + std::to_string(num)] = username;
}
}
return tmp_res;
};
/* Check: Change to first time user used in the connection */
if ((res=change_user_and_check(ch_user.c_str(), ch_pass.c_str(), 1))) { goto exit; }
/* Check: Already known user */
if ((res=change_user_and_check(user.c_str(), pass.c_str(), 2))) { goto exit; }
/* Check: Go back to already known user */
if ((res=change_user_and_check(ch_user.c_str(), ch_pass.c_str(), 3))) { goto exit; }
}
exit:
mysql_close(&mysql);
std::cout << output.dump();
return res;
}