Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 11 additions & 0 deletions include/MySQL_Authentication.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ class MySQL_Authentication {
std::unique_ptr<SQLite3_result> mysql_users_resultset { nullptr };
creds_group_t creds_backends;
creds_group_t creds_frontends;
/**
* @brief Scope holding 'admin-admin_credentials' and 'admin-stats_credentials'.
* @details Separate from 'creds_frontends' so an Admin credential and a
* 'mysql_users' row of the same name cannot overwrite each other. Only
* populated when PROXYSQL31 is defined (see ADMIN_CRED_SCOPE); on the stable
* tier it stays empty and admin credentials remain in 'creds_frontends'.
* Never included in 'dump_all_users()', so 'runtime_mysql_users' and the
* cluster checksum are unaffected.
*/
creds_group_t creds_admins;
creds_group_t& creds_for(enum cred_username_type usertype);
bool _reset(enum cred_username_type usertype);
uint64_t _get_runtime_checksum(enum cred_username_type usertype);
public:
Expand Down
10 changes: 10 additions & 0 deletions include/PgSQL_Authentication.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ class PgSQL_Authentication {
std::unique_ptr<SQLite3_result> pgsql_users_resultset { nullptr };
creds_group_t creds_backends;
creds_group_t creds_frontends;
/**
* @brief Scope holding 'admin-admin_credentials' / 'admin-stats_credentials'.
* @details Mirrors MySQL_Authentication::creds_admins. Only populated when
* PROXYSQL31 is defined (see ADMIN_CRED_SCOPE in MySQL_Authentication.hpp);
* on the stable tier admin credentials stay in 'creds_frontends' alongside
* 'pgsql_users' and behaviour is unchanged. Never walked by
* dump_all_users(), so 'runtime_pgsql_users' and the checksum are unaffected.
*/
creds_group_t creds_admins;
creds_group_t& creds_for(enum cred_username_type usertype);
bool _reset(enum cred_username_type usertype);
uint64_t _get_runtime_checksum(enum cred_username_type usertype);
public:
Expand Down
42 changes: 41 additions & 1 deletion include/proxysql_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ enum log_event_type {
PROXYSQL_METADATA
};

enum cred_username_type { USERNAME_BACKEND, USERNAME_FRONTEND, USERNAME_NONE };
// USERNAME_ADMIN is a scope for 'admin-admin_credentials' / 'admin-stats_credentials'.
// It is compiled unconditionally, but only *used* when PROXYSQL31 is defined --
// see ADMIN_CRED_SCOPE in MySQL_Authentication.hpp. On the stable tier those
// credentials continue to live in USERNAME_FRONTEND alongside mysql_users.
enum cred_username_type { USERNAME_BACKEND, USERNAME_FRONTEND, USERNAME_NONE, USERNAME_ADMIN };

#define PROXYSQL_USE_RESULT

Expand Down Expand Up @@ -771,6 +775,42 @@ enum proxysql_session_type {
PROXYSQL_SESSION_NONE
};

/**
* @brief The credential scope holding 'admin-admin_credentials' and
* 'admin-stats_credentials'.
*
* @details Historically these shared USERNAME_FRONTEND with mysql_users /
* pgsql_users -- one flat map keyed by username -- so an Admin credential and
* a row of the same name overwrote each other. That is the only reason the
* documentation states those users cannot also appear in mysql_users.
*
* From the Innovative tier onward they get their own scope, removing the
* collision rather than policing it. This is an INCOMPATIBLE change (a
* colliding name currently resolves to one entry; afterwards the two are
* independent), so it is gated to PROXYSQL31. On the stable tier this is
* USERNAME_FRONTEND, every call site passes what it always passed, and
* behaviour is unchanged. See issue #5987.
*/
#ifdef PROXYSQL31
#define ADMIN_CRED_SCOPE USERNAME_ADMIN
#else
#define ADMIN_CRED_SCOPE USERNAME_FRONTEND
#endif /* PROXYSQL31 */

/**
* @brief Credential scope to resolve a username in, for a given session type.
* @details ADMIN and STATS sessions use ADMIN_CRED_SCOPE; every other session
* type (MySQL/PgSQL frontend, SQLite server, ClickHouse) uses
* USERNAME_FRONTEND. Shared by both protocol implementations so the policy
* exists once.
*/
static inline enum cred_username_type cred_scope_for_session(enum proxysql_session_type session_type) {
if (session_type == PROXYSQL_SESSION_ADMIN || session_type == PROXYSQL_SESSION_STATS) {
return ADMIN_CRED_SCOPE;
}
return USERNAME_FRONTEND;
}

#endif /* PROXYSQL_ENUMS */


Expand Down
44 changes: 35 additions & 9 deletions lib/MySQL_Authentication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,30 @@
#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_init(&creds_backends.lock, NULL);
pthread_rwlock_init(&creds_frontends.lock, NULL);
pthread_rwlock_init(&creds_admins.lock, NULL);
#else
spinlock_rwlock_init(&creds_backends.lock);
spinlock_rwlock_init(&creds_frontends.lock);
spinlock_rwlock_init(&creds_admins.lock);
#endif
creds_backends.cred_array = new PtrArray();
creds_frontends.cred_array = new PtrArray();
creds_admins.cred_array = new PtrArray();

Check failure on line 68 in lib/MySQL_Authentication.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace the use of "new" with an operation that automatically manages the memory.

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZ_czLUqNNBN74OSsSB6&open=AZ_czLUqNNBN74OSsSB6&pullRequest=5993
};

MySQL_Authentication::~MySQL_Authentication() {
reset();
delete creds_backends.cred_array;
delete creds_frontends.cred_array;
delete creds_admins.cred_array;

Check failure on line 75 in lib/MySQL_Authentication.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rewrite the code so that you no longer need this "delete".

See more on https://sonarcloud.io/project/issues?id=sysown_proxysql&issues=AZ_czLUqNNBN74OSsSB7&open=AZ_czLUqNNBN74OSsSB7&pullRequest=5993
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

void MySQL_Authentication::print_version() {
fprintf(stderr,"Standard MySQL Authentication rev. %s -- %s -- %s\n", MYSQL_AUTHENTICATION_VERSION, __FILE__, __TIMESTAMP__);
};

void MySQL_Authentication::set_all_inactive(enum cred_username_type usertype) {
creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends);
creds_group_t &cg = creds_for(usertype);
#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_wrlock(&cg.lock);
#else
Expand All @@ -95,7 +99,7 @@
}

void MySQL_Authentication::remove_inactives(enum cred_username_type usertype) {
creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends);
creds_group_t &cg = creds_for(usertype);
#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_wrlock(&cg.lock);
#else
Expand All @@ -117,14 +121,31 @@
#endif
}

/**
* @brief Select the credential scope for a username type.
* @details USERNAME_ADMIN is only ever passed when PROXYSQL31 is defined; on the
* stable tier ADMIN_CRED_SCOPE is USERNAME_FRONTEND so this never returns
* 'creds_admins' and behaviour is unchanged.
*/
creds_group_t& MySQL_Authentication::creds_for(enum cred_username_type usertype) {
switch (usertype) {
case USERNAME_BACKEND:
return creds_backends;
case USERNAME_ADMIN:
return creds_admins;
default:
return creds_frontends;
}
}

bool MySQL_Authentication::add(char * username, char * password, enum cred_username_type usertype, bool use_ssl, int default_hostgroup, char *default_schema, bool schema_locked, bool transaction_persistent, bool fast_forward, int max_connections, char* attributes, char *comment) {
uint64_t hash1, hash2;
SpookyHash myhash;
myhash.Init(1,2);
myhash.Update(username,strlen(username));
myhash.Final(&hash1,&hash2);

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

#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_wrlock(&cg.lock);
Expand Down Expand Up @@ -483,7 +504,7 @@
myhash->Final(&hash1,&hash2);
delete myhash;

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

if (set_lock)
#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
Expand Down Expand Up @@ -526,7 +547,7 @@
myhash->Final(&hash1,&hash2);
delete myhash;

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

#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_wrlock(&cg.lock);
Expand Down Expand Up @@ -563,7 +584,7 @@
myhash->Final(&hash1,&hash2);
delete myhash;

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

#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_wrlock(&cg.lock);
Expand Down Expand Up @@ -631,7 +652,7 @@
myhash.Update(username,strlen(username));
myhash.Final(&hash1,&hash2);

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

#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_rdlock(&cg.lock);
Expand Down Expand Up @@ -688,7 +709,7 @@
}

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

#ifdef PROXYSQL_AUTH_PTHREAD_MUTEX
pthread_rwlock_wrlock(&cg.lock);
Expand Down Expand Up @@ -733,6 +754,11 @@
bool MySQL_Authentication::reset() {
_reset(USERNAME_BACKEND);
_reset(USERNAME_FRONTEND);
// creds_admins is a distinct scope from PROXYSQL31 onward; without this its
// accounts are handed to setAllInactive() without ever being released and the
// account_details strings leak. On the stable tier ADMIN_CRED_SCOPE is
// USERNAME_FRONTEND so the scope is empty and this is a no-op.
_reset(USERNAME_ADMIN);
return true;
}

Expand Down Expand Up @@ -780,7 +806,7 @@
}

uint64_t MySQL_Authentication::_get_runtime_checksum(enum cred_username_type usertype) {
creds_group_t &cg=(usertype==USERNAME_BACKEND ? creds_backends : creds_frontends);
creds_group_t &cg = creds_for(usertype);
uint64_t accs_hash = compute_accounts_hash(cg.bt_map);

return accs_hash;
Expand Down
Loading
Loading