Per-user default transaction isolation level

See issue #3465

If a user (configurable) connects to proxysql without specifying a session transaction isolation level, proxysql will automatically assigns one to that user connection. Of course, said user can override such default session transaction isolation level running `SET SESSION TRANSACTION ISOLATION LEVEL` on a per-connection basis.

This feature can be configured extending `mysql_users.attributes` , adding an object named `default-transaction_isolation` .

An example of configuration could be:
```
UPDATE mysql_users SET attributes='{"default-transaction_isolation":"READ COMMITTED"}' WHERE username='sbtest';
```

This feature also requires input validation making sure that only valid isolation levels are used.
Furthermore, both dash and space can be used.
If the isolation level specified is not valid, it should be automatically removed from runtime.
pull/3466/head
René Cannaò 5 years ago
parent 5a6095cd14
commit 52f1d0f1ec

@ -122,7 +122,45 @@ bool MySQL_Authentication::add(char * username, char * password, enum cred_usern
// NOTE: add() is only place where we do input validation
try {
nlohmann::json valid=nlohmann::json::parse(attributes);
ad->attributes=strdup(attributes);
// we do further input validation here, and possibly transforming the JSON itself
bool json_rewritten = false;
auto default_transaction_isolation = valid.find("default-transaction_isolation");
if (default_transaction_isolation != valid.end()) {
std::string dti = valid["default-transaction_isolation"].get<std::string>();
for (unsigned int i = 0; i < dti.length(); ++i) {
if (dti[i] == '-') {
dti[i] = ' ';
json_rewritten = true; // the json needs to be rewritten
}
}
// input validation
if (
(strcasecmp(dti.c_str(), "READ UNCOMMITTED")==0 )
|| (strcasecmp(dti.c_str(), "READ COMMITTED")==0 )
|| (strcasecmp(dti.c_str(), "REPEATABLE READ")==0 )
|| (strcasecmp(dti.c_str(), "SERIALIZABLE")==0 )
) {
if (json_rewritten) {
valid["default-transaction_isolation"]=dti;
}
} else {
std::string dti_orig = valid["default-transaction_isolation"].get<std::string>();
proxy_error("Invalid default-transaction_isolation for user %s : %s . Removing it from runtime\n", username, dti_orig.c_str());
valid.erase("default-transaction_isolation");
json_rewritten = true; // the json was rewritten
}
}
if (json_rewritten) {
std::string d = valid.dump();
if (d.length()==2) { // empty json
ad->attributes=strdup(""); // empty string
} else {
ad->attributes=strdup(d.c_str());
}
} else {
// the JSON wasn't rewritten for the purpose of input validation, therefore we copy the original value
ad->attributes=strdup(attributes);
}
}
catch(nlohmann::json::exception& e) {
ad->attributes=strdup("");

@ -2336,6 +2336,11 @@ bool MySQL_Protocol::verify_user_attributes(int calling_line, const char *callin
proxy_error("%d:%s(): SPIFFE Authentication error for user %s . spiffed_id expected : %s , received: %s\n", calling_line, calling_func, user, spiffe_val.c_str(), ((*myds)->x509_subject_alt_name ? (*myds)->x509_subject_alt_name : "none"));
}
}
auto default_transaction_isolation = j.find("default-transaction_isolation");
if (default_transaction_isolation != j.end()) {
std::string default_transaction_isolation_value = j["default-transaction_isolation"].get<std::string>();
mysql_variables.client_set_value((*myds)->sess, SQL_ISOLATION_LEVEL, default_transaction_isolation_value.c_str());
}
}
}
return ret;

Loading…
Cancel
Save