Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/MySQL_Authentication.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ class MySQL_Authentication {
void print_version();
bool exists(char *username);
account_details_t lookup(char* username, enum cred_username_type usertype, const dup_account_details_t& dup_details);
/**
* @brief Lookup backend credentials for a specific hostgroup.
* @details Searches for a backend user (backend=1) that has the specified hostgroup as its default_hostgroup.
* Only one backend user per hostgroup is allowed, ensuring unambiguous credential mapping.
* @param hostgroup_id The hostgroup ID to lookup backend credentials for
* @return account_details_t containing the backend user credentials, or an empty struct if none found
*/
account_details_t lookup_backend_for_hostgroup(int hostgroup_id);
int dump_all_users(account_details_t ***, bool _complete=true);
int increase_frontend_user_connections(char *username, PASSWORD_TYPE::E passtype, int *mc=NULL);
void decrease_frontend_user_connections(char *username, PASSWORD_TYPE::E passtype);
Expand Down
14 changes: 14 additions & 0 deletions include/PgSQL_Authentication.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ class PgSQL_Authentication {
void print_version();
bool exists(char *username);
char * lookup(char *username, enum cred_username_type usertype, bool *use_ssl, int *default_hostgroup, bool *transaction_persistent, bool *fast_forward, int *max_connections, void **sha1_pass, char **attributes);
/**
* @brief Lookup backend credentials for a specific hostgroup.
* @details Searches for a backend user (backend=1) that has the specified hostgroup as its default_hostgroup.
* Only one backend user per hostgroup is allowed, ensuring unambiguous credential mapping.
* @param hostgroup_id The hostgroup ID to lookup backend credentials for
* @return Pointer to allocated pgsql_account_details_t containing the backend user credentials, or NULL if none found
* Caller is responsible for freeing the returned structure using free_account_details().
*/
pgsql_account_details_t* lookup_backend_for_hostgroup(int hostgroup_id);
/**
* @brief Free a pgsql_account_details_t structure returned by lookup_backend_for_hostgroup.
* @param acct Pointer to the account details structure to free (can be NULL)
*/
void free_account_details(pgsql_account_details_t* acct);
int dump_all_users(pgsql_account_details_t***, bool _complete=true);
int increase_frontend_user_connections(char *username, int *mc=NULL);
void decrease_frontend_user_connections(char *username);
Expand Down
67 changes: 67 additions & 0 deletions lib/Admin_Bootstrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,73 @@ bool ProxySQL_Admin::init(const bootstrap_info_t& bootstrap_info) {
check_and_build_standard_tables(configdb, tables_defs_config);
check_and_build_standard_tables(statsdb, tables_defs_stats);

// Create triggers to enforce one backend user per hostgroup constraint
// This ensures hostgroup-based backend credential mapping is unambiguous
const char* mysql_users_trigger = R"(
CREATE TRIGGER IF NOT EXISTS tr_mysql_users_backend_hostgroup_unique
BEFORE INSERT ON mysql_users
WHEN NEW.backend = 1 AND EXISTS (
SELECT 1 FROM mysql_users
WHERE backend = 1
AND default_hostgroup = NEW.default_hostgroup
AND username != NEW.username
)
BEGIN
SELECT RAISE(ABORT, 'Only one backend user allowed per hostgroup');
END;
)";

const char* mysql_users_trigger_update = R"(
CREATE TRIGGER IF NOT EXISTS tr_mysql_users_backend_hostgroup_unique_update
BEFORE UPDATE ON mysql_users
WHEN NEW.backend = 1 AND EXISTS (
SELECT 1 FROM mysql_users
WHERE backend = 1
AND default_hostgroup = NEW.default_hostgroup
AND username != NEW.username
)
BEGIN
SELECT RAISE(ABORT, 'Only one backend user allowed per hostgroup');
END;
)";

const char* pgsql_users_trigger = R"(
CREATE TRIGGER IF NOT EXISTS tr_pgsql_users_backend_hostgroup_unique
BEFORE INSERT ON pgsql_users
WHEN NEW.backend = 1 AND EXISTS (
SELECT 1 FROM pgsql_users
WHERE backend = 1
AND default_hostgroup = NEW.default_hostgroup
AND username != NEW.username
)
BEGIN
SELECT RAISE(ABORT, 'Only one backend user allowed per hostgroup');
END;
)";

const char* pgsql_users_trigger_update = R"(
CREATE TRIGGER IF NOT EXISTS tr_pgsql_users_backend_hostgroup_unique_update
BEFORE UPDATE ON pgsql_users
WHEN NEW.backend = 1 AND EXISTS (
SELECT 1 FROM pgsql_users
WHERE backend = 1
AND default_hostgroup = NEW.default_hostgroup
AND username != NEW.username
)
BEGIN
SELECT RAISE(ABORT, 'Only one backend user allowed per hostgroup');
END;
)";

admindb->execute(mysql_users_trigger);
admindb->execute(mysql_users_trigger_update);
admindb->execute(pgsql_users_trigger);
admindb->execute(pgsql_users_trigger_update);
configdb->execute(mysql_users_trigger);
configdb->execute(mysql_users_trigger_update);
configdb->execute(pgsql_users_trigger);
configdb->execute(pgsql_users_trigger_update);

__attach_db(admindb, configdb, (char *)"disk");
__attach_db(admindb, statsdb, (char *)"stats");
__attach_db(admindb, monitordb, (char *)"monitor");
Expand Down
79 changes: 72 additions & 7 deletions lib/MySQL_Authentication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
#endif

void free_account_details(account_details_t& ad) {
if (ad.username) {
free(ad.username);

Check failure on line 19 in lib/MySQL_Authentication.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "free".

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZq6AiFqxhOzSCfENCbJ&open=AZq6AiFqxhOzSCfENCbJ&pullRequest=5227
ad.username = nullptr;
}
if (ad.password) {
free(ad.password);
ad.password = nullptr;
}
if (ad.sha1_pass) {
free(ad.sha1_pass);
ad.sha1_pass=NULL;
}
if (ad.password) {
free(ad.password);
ad.password = nullptr;
ad.sha1_pass = nullptr;
}
if (ad.clear_text_password[PASSWORD_TYPE::PRIMARY]) {
free(ad.clear_text_password[PASSWORD_TYPE::PRIMARY]);
Expand Down Expand Up @@ -129,7 +129,7 @@
myhash.Final(&hash1,&hash2);

creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends);

#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_wrlock(&cg.lock);
#else
Expand Down Expand Up @@ -691,6 +691,71 @@
return ret;
}

account_details_t MySQL_Authentication::lookup_backend_for_hostgroup(int hostgroup_id) {
account_details_t ret {};

creds_group_t &cg = creds_backends;

#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_rdlock(&cg.lock);
#else
spin_rdlock(&cg.lock);
#endif

// Iterate through backend users to find the one for this hostgroup
for (const auto& pair : cg.bt_map) {
account_details_t* ad = pair.second;

if (ad->default_hostgroup == hostgroup_id) {
// Found the backend user for this hostgroup

ret.username = strdup(ad->username);
ret.password = strdup(ad->password);

if (ad->clear_text_password[PASSWORD_TYPE::PRIMARY]) {
ret.clear_text_password[PASSWORD_TYPE::PRIMARY] =
strdup(ad->clear_text_password[PASSWORD_TYPE::PRIMARY]);
}
if (ad->clear_text_password[PASSWORD_TYPE::ADDITIONAL]) {
ret.clear_text_password[PASSWORD_TYPE::ADDITIONAL] =
strdup(ad->clear_text_password[PASSWORD_TYPE::ADDITIONAL]);
}

ret.use_ssl = ad->use_ssl;
ret.default_hostgroup = ad->default_hostgroup;

if (ad->default_schema) {
ret.default_schema = strdup(ad->default_schema);
}

ret.schema_locked = ad->schema_locked;
ret.transaction_persistent = ad->transaction_persistent;
ret.fast_forward = ad->fast_forward;
ret.max_connections = ad->max_connections;

if (ad->sha1_pass) {
ret.sha1_pass = malloc(SHA_DIGEST_LENGTH);

Check failure on line 737 in lib/MySQL_Authentication.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "malloc".

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZq6AiFqxhOzSCfENCbK&open=AZq6AiFqxhOzSCfENCbK&pullRequest=5227
memcpy(ret.sha1_pass, ad->sha1_pass, SHA_DIGEST_LENGTH);
}

if (ad->attributes) {
ret.attributes = strdup(ad->attributes);
}

// Only one backend user per hostgroup is allowed, so we can break
break;
}
}

#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_unlock(&cg.lock);
#else
spin_rdunlock(&cg.lock);
#endif

return ret;
}

bool MySQL_Authentication::_reset(enum cred_username_type usertype) {
creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends);

Expand Down Expand Up @@ -800,7 +865,7 @@
if (resultset == nullptr) { return { umap_auth {}, umap_auth {} }; }

// The following order is assumed for the resulset received fields:
// - username, password, active, use_ssl, default_hostgroup, default_schema, schema_locked,
// - username, password, active, use_ssl, default_hostgroup, default_schema, schema_locked,
// transaction_persistent, fast_forward, backend, frontend, max_connections, attributes, comment.
umap_auth f_accs_map {};
umap_auth b_accs_map {};
Expand Down
Loading