Skip to content

Commit 37e2daa

Browse files
committed
PS-11220 Add MySQL instrumentation to memory
1 parent 1022df9 commit 37e2daa

8 files changed

Lines changed: 186 additions & 48 deletions

File tree

mysql-test/suite/auth_openid_connect/r/auth.result

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ auth_openid_connect_users_authenticated 5
5858
auth_openid_connect_users_denied 16
5959
auth_openid_connect_users_proxied 0
6060
auth_openid_connect_users_with_roles 0
61+
SELECT COUNT_ALLOC>0, SUM_NUMBER_OF_BYTES_ALLOC>0, CURRENT_NUMBER_OF_BYTES_USED>0 FROM performance_schema.memory_summary_global_by_event_name WHERE EVENT_NAME LIKE 'memory/auth_openid_connect/%';
62+
COUNT_ALLOC>0 SUM_NUMBER_OF_BYTES_ALLOC>0 CURRENT_NUMBER_OF_BYTES_USED>0
63+
1 1 1
6164

6265
### CLEANUP
6366
DROP USER 'mysql_oidc_user'@'%';

mysql-test/suite/auth_openid_connect/t/auth.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ DROP USER invalid_user;
194194

195195
SHOW GLOBAL STATUS LIKE 'auth_openid_connect%';
196196

197+
# ensure memory instrumentation works
198+
SELECT COUNT_ALLOC>0, SUM_NUMBER_OF_BYTES_ALLOC>0, CURRENT_NUMBER_OF_BYTES_USED>0 FROM performance_schema.memory_summary_global_by_event_name WHERE EVENT_NAME LIKE 'memory/auth_openid_connect/%';
197199
###################### CLEANUP #######################
198200
--echo
199201
--echo ### CLEANUP

plugin/auth_openid_connect/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ IF(WITH_AUTH_OPENID_CONNECT)
2020
src/config.cc
2121
src/jwk.cc
2222
src/jwks.cc
23+
src/psi_openid_connect.cc
2324
src/udf.cc
2425
src/id_token.cc
2526
)

plugin/auth_openid_connect/src/config.cc

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ void Idp_configs::update(MYSQL_THD thd [[maybe_unused]],
106106
long long Idp_configs::update_keys() noexcept {
107107
long long no_updated_keys{0};
108108
try {
109-
std::vector<std::pair<std::string, Idp_config>> configs;
109+
std::vector<std::pair<Config_string, Idp_config>> configs;
110110
create_tmp_configs(configs);
111111
for (auto &[key, val] : configs) {
112112
if (!val.load_keys()) ++no_updated_keys;
@@ -129,7 +129,7 @@ long long Idp_configs::update_keys(const char *idp_name) noexcept {
129129
// effectively the keys will be removed from IDP. Note 2: during 2 the IDP
130130
// config may be removed, then 3 fails and the function returns an error.
131131
// That is not effective, but it is an edge case.
132-
const std::string jwks_url{get_safe_jwks_url(idp_name)};
132+
const Config_string jwks_url{get_safe_jwks_url(idp_name)};
133133
if (jwks_url.empty()) return 0;
134134
Idp_config config("", jwks_url, "", {}, {});
135135
const long long result = config.load_keys() ? 0 : 1;
@@ -177,11 +177,11 @@ void Idp_configs::load(const std::string &config_json) {
177177
#pragma GCC diagnostic ignored "-Wdangling-reference"
178178
#endif
179179

180-
std::map<std::string, std::string> Idp_configs::load_group_roles(
180+
Config_string_map Idp_configs::load_group_roles(
181181
const picojson::object &idp_object, const std::string &idp_name) {
182182
static constexpr std::string_view key_group_role{"group-role"};
183183

184-
std::map<std::string, std::string> roles;
184+
Config_string_map roles;
185185

186186
const picojson::array &roles_array{json_get<picojson::array>(
187187
idp_object, std::string(key_group_role), idp_name, false)};
@@ -201,14 +201,14 @@ std::map<std::string, std::string> Idp_configs::load_group_roles(
201201
return roles;
202202
}
203203

204-
std::unordered_set<std::string> Idp_configs::load_audiences(
204+
Config_string_set Idp_configs::load_audiences(
205205
const picojson::object &idp_object, const std::string &idp_name) {
206206
static constexpr std::string_view key_audiences{"audiences"};
207-
std::unordered_set<std::string> audiences{};
207+
Config_string_set audiences{};
208208
const picojson::array &audience_array{json_get<picojson::array>(
209209
idp_object, std::string(key_audiences), idp_name, false)};
210210
for (const auto &audience : audience_array) {
211-
audiences.insert(audience.get<std::string>());
211+
audiences.insert(Config_string(audience.get<std::string>()));
212212
}
213213
return audiences;
214214
}
@@ -323,7 +323,7 @@ void Idp_config::load_keys(const picojson::array &key_array,
323323

324324
const picojson::object &key_object{key_value.get<picojson::object>()};
325325
const std::string &kty{json_get<std::string>(key_object, key_kty, from)};
326-
const std::string &kid{json_get<std::string>(key_object, key_kid, from)};
326+
const Config_string &kid{json_get<std::string>(key_object, key_kid, from)};
327327

328328
std::string pem_key;
329329
if (kty == key_kty_rsa) {
@@ -478,5 +478,6 @@ std::string Idp_configs::verify_token(const Id_token &token,
478478
const std::shared_lock lock(mutex(), lock_timeout);
479479
if (!lock.owns_lock())
480480
throw std::runtime_error("failed to acquire shared lock on configuration");
481-
return token.verify(ext_user, ext_group, current()->get_idp(idp_name), roles);
481+
return token.verify(ext_user, ext_group,
482+
current()->get_idp(Config_string(idp_name)), roles);
482483
}

plugin/auth_openid_connect/src/config.h

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
1818
#ifndef AUTH_OIDC_CONFIG_H
1919
#define AUTH_OIDC_CONFIG_H
2020

21+
#include <picojson/picojson.h>
2122
#include <chrono>
2223
#include <map>
2324
#include <memory>
@@ -29,16 +30,38 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
2930

3031
#include <mysql/components/services/bits/system_variables_bits.h>
3132
#include <mysql/plugin.h>
33+
#include <mysql/psi/psi_memory.h>
3234
#include <mysql/service_thd_alloc.h>
33-
#include <picojson/picojson.h>
3435

3536
#include <shared_mutex>
3637
#include <vector>
3738

3839
#include "jwks.h"
40+
#include "psi_openid_connect.h"
3941

4042
class Id_token;
4143

44+
template <typename T>
45+
using Config_allocator =
46+
Psi_openid_connect::Allocator<T, Psi_openid_connect::config_memory_key>;
47+
48+
using Config_string =
49+
std::basic_string<char, std::char_traits<char>,
50+
Psi_openid_connect::Config_allocator<char>>;
51+
using Config_string_set =
52+
std::unordered_set<Config_string, std::hash<Config_string>,
53+
std::equal_to<Config_string>,
54+
Psi_openid_connect::Config_allocator<Config_string>>;
55+
using Config_string_map =
56+
std::map<Config_string, Config_string, std::less<Config_string>,
57+
Psi_openid_connect::Config_allocator<
58+
std::pair<const Config_string, Config_string>>>;
59+
class Idp_config;
60+
using Config_idp_map =
61+
std::map<Config_string, Idp_config, std::less<Config_string>,
62+
Psi_openid_connect::Config_allocator<
63+
std::pair<const Config_string, Idp_config>>>;
64+
4265
/**
4366
* @brief Statistics for Identity Provider (IDP) configurations.
4467
*/
@@ -56,18 +79,16 @@ struct Idp_config_statistics {
5679
*/
5780
class Idp_config {
5881
private:
59-
std::string issuer_name; ///< The token's issuer name.
60-
Jwks jwks; ///< URL for the JSON Web Key Set.
61-
std::string group_claim; ///< Name of the claim in the JWT that contains
62-
///< group information.
63-
std::unordered_set<std::string>
64-
audiences; ///< Set of allowed audiences for the token.
65-
std::map<std::string, std::string>
66-
roles; ///< Map of IDP groups to database roles.
67-
std::map<std::string, std::string>
68-
keys; ///< Map of Key ID (kid) to public key in PEM format.
82+
Config_string issuer_name; ///< The token's issuer name.
83+
Jwks jwks; ///< Object managing the JSON Web Key Set.
84+
Config_string group_claim; ///< Name of the claim in the JWT that contains
85+
///< group information.
86+
Config_string_set audiences; ///< Set of allowed audiences for the token.
87+
Config_string_map roles; ///< Map of IDP groups to database roles.
88+
Config_string_map keys; ///< Map of Key ID (kid) to public key in PEM format.
6989

7090
public:
91+
static PSI_memory_key config_memory_key;
7192
/**
7293
* @brief Constructs an Idp_config object.
7394
* @param issuer_name The token's issuer name.
@@ -76,10 +97,10 @@ class Idp_config {
7697
* @param audiences A set of allowed audiences.
7798
* @param roles A map of group-to-role mappings.
7899
*/
79-
Idp_config(const std::string &issuer_name, const std::string &jwks_url,
80-
const std::string &group_claim,
81-
std::unordered_set<std::string> &&audiences,
82-
std::map<std::string, std::string> &&roles)
100+
Idp_config(const std::string_view &issuer_name,
101+
const std::string_view &jwks_url,
102+
const std::string_view &group_claim, Config_string_set &&audiences,
103+
Config_string_map &&roles)
83104
: issuer_name(issuer_name),
84105
jwks(jwks_url),
85106
group_claim(group_claim),
@@ -108,7 +129,9 @@ class Idp_config {
108129
* @brief Gets the issuer name.
109130
* @return The issuer name string.
110131
*/
111-
const std::string &get_issuer_name() const noexcept { return issuer_name; }
132+
std::string get_issuer_name() const noexcept {
133+
return std::string(issuer_name);
134+
}
112135

113136
/**
114137
* @brief Gets the JWKS URL.
@@ -120,7 +143,7 @@ class Idp_config {
120143
* @brief Gets the name of the group claim.
121144
* @return The group claim name.
122145
*/
123-
const std::string &get_group_claim() const noexcept { return group_claim; }
146+
std::string_view get_group_claim() const noexcept { return group_claim; }
124147

125148
/**
126149
* @brief Gets the only public key.
@@ -129,7 +152,7 @@ class Idp_config {
129152
* @return The only public key in PEM format.
130153
* @throws std::runtime_error if no keys are available.
131154
*/
132-
const std::string &get_the_only_pub_key() const {
155+
std::string_view get_the_only_pub_key() const {
133156
if (keys.size() != 1) throw std::runtime_error("incorrect number of keys");
134157
const auto key{keys.cbegin()};
135158
return key->second;
@@ -141,8 +164,8 @@ class Idp_config {
141164
* @return The public key in PEM format.
142165
* @throws std::out_of_range if the Key ID is not found.
143166
*/
144-
const std::string &get_pub_key(const std::string &kid) const {
145-
return keys.at(kid);
167+
std::string_view get_pub_key(const std::string_view &kid) const {
168+
return keys.at(Config_string(kid));
146169
}
147170

148171
/**
@@ -156,18 +179,18 @@ class Idp_config {
156179
* @param audience The audience string from the token.
157180
* @return true if the audience is allowed
158181
*/
159-
bool is_audience_allowed(const std::string &audience) const noexcept {
160-
return audiences.contains(audience);
182+
bool is_audience_allowed(const std::string_view &audience) const noexcept {
183+
return audiences.contains(Config_string(audience));
161184
}
162185

163186
/**
164187
* @brief Gets the mapped database role for an IDP group.
165188
* @param group The IDP group name.
166189
* @return The mapped role name, or an empty string if no mapping exists.
167190
*/
168-
const std::string &get_role(const std::string &group) const noexcept {
169-
static std::string no_role;
170-
const auto it = roles.find(group);
191+
std::string_view get_role(const std::string_view &group) const noexcept {
192+
static Config_string no_role;
193+
const auto it = roles.find(Config_string(group));
171194
return it == roles.end() ? no_role : it->second;
172195
}
173196

@@ -190,9 +213,8 @@ class Idp_config {
190213
*/
191214
class Idp_configs {
192215
private:
193-
std::string sysvar_str{}; ///< Value of the configuration system variable.
194-
std::map<std::string, Idp_config>
195-
idp_configs{}; ///< Map of IDP names to Idp_config objects.
216+
Config_string sysvar_str{}; ///< Value of the configuration system variable.
217+
Config_idp_map idp_configs{}; ///< Map of IDP names to Idp_config objects.
196218
Idp_configs() = delete;
197219

198220
/**
@@ -255,8 +277,8 @@ class Idp_configs {
255277
* @return map of IDP group names to database role names. If no mapping is
256278
* present the returned map will be empty.
257279
*/
258-
static std::map<std::string, std::string> load_group_roles(
259-
const picojson::object &idp_object, const std::string &idp_name);
280+
static Config_string_map load_group_roles(const picojson::object &idp_object,
281+
const std::string &idp_name);
260282

261283
/**
262284
* @brief Load the set of allowed audiences from an IDP JSON object.
@@ -268,8 +290,8 @@ class Idp_configs {
268290
* @return An unordered set containing the allowed audience
269291
* strings. If no audiences are configured the set will be empty.
270292
*/
271-
static std::unordered_set<std::string> load_audiences(
272-
const picojson::object &idp_object, const std::string &idp_name);
293+
static Config_string_set load_audiences(const picojson::object &idp_object,
294+
const std::string &idp_name);
273295

274296
/**
275297
* @brief Parses the prefix of the configuration system variable.
@@ -301,10 +323,11 @@ class Idp_configs {
301323
* @return reference to the Idp_config object, or nullptr if not found.
302324
* @throws std::runtime_error if the IDP is not found in the configuration.
303325
*/
304-
Idp_config &get_idp(const std::string &idp_name) {
326+
Idp_config &get_idp(const Config_string &idp_name) {
305327
const auto it = idp_configs.find(idp_name);
306328
if (it == idp_configs.end())
307-
throw std::runtime_error("IDP not found: " + idp_name);
329+
throw std::runtime_error(std::string("IDP not found: ") +
330+
idp_name.c_str());
308331
return it->second;
309332
}
310333

@@ -315,7 +338,7 @@ class Idp_configs {
315338
* @param other_idp The second IDP config.
316339
* @throws std::runtime_error if the IDP is not found in the configuration.
317340
*/
318-
static void swap_idp_keys(const std::string &idp_name,
341+
static void swap_idp_keys(const Config_string &idp_name,
319342
Idp_config &other_idp) {
320343
std::unique_lock lock(mutex());
321344
current()->get_idp(idp_name).swap_keys(other_idp);
@@ -326,7 +349,7 @@ class Idp_configs {
326349
* @param idp_name The name of the IDP.
327350
* @return The JWKS URL for the specified IDP.
328351
*/
329-
static const std::string &get_safe_jwks_url(const std::string &idp_name) {
352+
static const std::string &get_safe_jwks_url(const Config_string &idp_name) {
330353
std::shared_lock lock(mutex(), lock_timeout);
331354
if (!lock.owns_lock())
332355
throw std::runtime_error("failed to acquire shared lock");
@@ -337,7 +360,7 @@ class Idp_configs {
337360
* @brief Gets the JWKS URL for a specific IDP in a thread-safe manner.
338361
*/
339362
static void swap_idp_keys(
340-
std::vector<std::pair<std::string, Idp_config>> &configs) {
363+
std::vector<std::pair<Config_string, Idp_config>> &configs) {
341364
std::unique_lock lock(mutex(), lock_timeout);
342365
if (!lock.owns_lock())
343366
throw std::runtime_error("failed to acquire unique lock");
@@ -353,7 +376,7 @@ class Idp_configs {
353376
* corresponding temporary Idp_config objects
354377
*/
355378
static void create_tmp_configs(
356-
std::vector<std::pair<std::string, Idp_config>> &configs) {
379+
std::vector<std::pair<Config_string, Idp_config>> &configs) {
357380
std::shared_lock lock(mutex(), lock_timeout);
358381
if (!lock.owns_lock())
359382
throw std::runtime_error("failed to acquire shared lock");

plugin/auth_openid_connect/src/plugin_openid_connect.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
2020
#include <picojson/picojson.h>
2121
#include <stddef.h>
2222
#include <cassert>
23-
#include <cstring>
2423
#include <exception>
2524
#include <map>
2625
#include <memory>
@@ -38,6 +37,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
3837

3938
#include "config.h"
4039
#include "id_token.h"
40+
#include "psi_openid_connect.h"
4141

4242
static SERVICE_TYPE(registry) * reg_srv(nullptr);
4343
SERVICE_TYPE(log_builtins) * log_bi(nullptr);
@@ -90,6 +90,7 @@ long long Connection_status::users_with_roles{0};
9090
* @return 0 for success, 1 for error.
9191
*/
9292
static int auth_oidc_init(MYSQL_PLUGIN plugin_info [[maybe_unused]]) {
93+
Psi_openid_connect::init();
9394
if (init_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs)) return 1;
9495
if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
9596
LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "curl_global_init failed");
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
(C) 2026 Percona LLC and/or its affiliates
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; version 2 of the License.
7+
8+
This program is distributed in the hope that it will be useful,
9+
but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
GNU General Public License for more details.
12+
13+
You should have received a copy of the GNU General Public License
14+
along with this program; if not, write to the Free Software
15+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
16+
*/
17+
18+
#include "psi_openid_connect.h"
19+
20+
#include <mysql/psi/mysql_memory.h>
21+
#include <template_utils.h>
22+
23+
namespace Psi_openid_connect {
24+
25+
PSI_memory_key config_memory_key;
26+
27+
static constexpr auto config_memory{"config_memory"};
28+
29+
static PSI_memory_info all_memory[] = {{&config_memory_key, config_memory, 0,
30+
PSI_VOLATILITY_UNKNOWN,
31+
PSI_DOCUMENT_ME}};
32+
33+
void init() {
34+
static constexpr auto category{"auth_openid_connect"};
35+
36+
int count = array_elements(all_memory);
37+
mysql_memory_register(category, all_memory, count);
38+
}
39+
40+
} // namespace Psi_openid_connect

0 commit comments

Comments
 (0)