Skip to content

Commit 9b07612

Browse files
Copilotbbockelm
andcommitted
Merge master and resolve conflicts
- Merge commit cb1463f (monitoring API) into environment config branch - Resolve conflicts in src/scitokens.cpp and test/CMakeLists.txt - Add monitoring configuration keys to environment variable loader: * SCITOKEN_CONFIG_MONITORING_FILE (string) * SCITOKEN_CONFIG_MONITORING_FILE_INTERVAL_S (int) - Keep both environment config tests and monitoring tests - All tests pass successfully Co-authored-by: bbockelm <1093447+bbockelm@users.noreply.github.com>
1 parent 87b727b commit 9b07612

File tree

10 files changed

+2266
-115
lines changed

10 files changed

+2266
-115
lines changed

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pkg_check_modules(SQLITE REQUIRED sqlite3)
4444

4545
endif()
4646

47-
add_library(SciTokens SHARED src/scitokens.cpp src/scitokens_internal.cpp src/scitokens_cache.cpp)
47+
add_library(SciTokens SHARED src/scitokens.cpp src/scitokens_internal.cpp src/scitokens_cache.cpp src/scitokens_monitoring.cpp)
4848
target_compile_features(SciTokens PUBLIC cxx_std_11) # Use at least C++11 for building and when linking to scitokens
4949
target_include_directories(SciTokens PUBLIC ${JWT_CPP_INCLUDES} "${PROJECT_SOURCE_DIR}/src" PRIVATE ${CURL_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} ${LIBCRYPTO_INCLUDE_DIRS} ${SQLITE_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS})
5050

@@ -75,6 +75,7 @@ target_link_libraries(scitokens-list-access SciTokens)
7575
add_executable(scitokens-create src/create.cpp)
7676
target_link_libraries(scitokens-create SciTokens)
7777

78+
7879
add_executable(scitokens-generate-jwks src/generate_jwks.cpp)
7980
target_include_directories(scitokens-generate-jwks PRIVATE ${OPENSSL_INCLUDE_DIRS} ${LIBCRYPTO_INCLUDE_DIRS})
8081
target_link_libraries(scitokens-generate-jwks ${OPENSSL_LIBRARIES} ${LIBCRYPTO_LIBRARIES})

src/scitokens.cpp

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,14 @@ void load_config_from_environment() {
4343
bool is_int;
4444
};
4545

46-
const std::array<ConfigMapping, 4> known_configs = {
46+
const std::array<ConfigMapping, 6> known_configs = {
4747
{{"keycache.update_interval_s", "KEYCACHE_UPDATE_INTERVAL_S", true},
4848
{"keycache.expiration_interval_s", "KEYCACHE_EXPIRATION_INTERVAL_S",
4949
true},
5050
{"keycache.cache_home", "KEYCACHE_CACHE_HOME", false},
51-
{"tls.ca_file", "TLS_CA_FILE", false}}};
51+
{"tls.ca_file", "TLS_CA_FILE", false},
52+
{"monitoring.file", "MONITORING_FILE", false},
53+
{"monitoring.file_interval_s", "MONITORING_FILE_INTERVAL_S", true}}};
5254

5355
const char *prefix = "SCITOKEN_CONFIG_";
5456

@@ -97,6 +99,35 @@ __attribute__((constructor)) void init_scitokens_config() {
9799

98100
} // anonymous namespace
99101

102+
// Monitoring file config (empty string means disabled)
103+
// Protected by mutex; atomic flag for fast-path check
104+
std::string configurer::Configuration::m_monitoring_file;
105+
std::mutex configurer::Configuration::m_monitoring_file_mutex;
106+
std::atomic<bool> configurer::Configuration::m_monitoring_file_configured{
107+
false};
108+
std::atomic_int configurer::Configuration::m_monitoring_file_interval{60};
109+
110+
void configurer::Configuration::set_monitoring_file(const std::string &path) {
111+
std::lock_guard<std::mutex> lock(m_monitoring_file_mutex);
112+
m_monitoring_file = path;
113+
// Update the atomic flag after setting the string
114+
m_monitoring_file_configured.store(!path.empty(),
115+
std::memory_order_release);
116+
}
117+
118+
std::string configurer::Configuration::get_monitoring_file() {
119+
std::lock_guard<std::mutex> lock(m_monitoring_file_mutex);
120+
return m_monitoring_file;
121+
}
122+
123+
void configurer::Configuration::set_monitoring_file_interval(int seconds) {
124+
m_monitoring_file_interval = seconds;
125+
}
126+
127+
int configurer::Configuration::get_monitoring_file_interval() {
128+
return m_monitoring_file_interval;
129+
}
130+
100131
SciTokenKey scitoken_key_create(const char *key_id, const char *alg,
101132
const char *public_contents,
102133
const char *private_contents, char **err_msg) {
@@ -1103,6 +1134,17 @@ int scitoken_config_set_int(const char *key, int value, char **err_msg) {
11031134
return 0;
11041135
}
11051136

1137+
else if (_key == "monitoring.file_interval_s") {
1138+
if (value < 0) {
1139+
if (err_msg) {
1140+
*err_msg = strdup("Interval cannot be negative.");
1141+
}
1142+
return -1;
1143+
}
1144+
configurer::Configuration::set_monitoring_file_interval(value);
1145+
return 0;
1146+
}
1147+
11061148
else {
11071149
if (err_msg) {
11081150
*err_msg = strdup("Key not recognized.");
@@ -1132,6 +1174,10 @@ int scitoken_config_get_int(const char *key, char **err_msg) {
11321174
return configurer::Configuration::get_expiry_delta();
11331175
}
11341176

1177+
else if (_key == "monitoring.file_interval_s") {
1178+
return configurer::Configuration::get_monitoring_file_interval();
1179+
}
1180+
11351181
else {
11361182
if (err_msg) {
11371183
*err_msg = strdup("Key not recognized.");
@@ -1161,6 +1207,9 @@ int scitoken_config_set_str(const char *key, const char *value,
11611207
} else if (_key == "tls.ca_file") {
11621208
configurer::Configuration::set_tls_ca_file(value ? std::string(value)
11631209
: "");
1210+
} else if (_key == "monitoring.file") {
1211+
configurer::Configuration::set_monitoring_file(
1212+
value ? std::string(value) : "");
11641213
} else {
11651214
if (err_msg) {
11661215
*err_msg = strdup("Key not recognized.");
@@ -1183,6 +1232,9 @@ int scitoken_config_get_str(const char *key, char **output, char **err_msg) {
11831232
*output = strdup(configurer::Configuration::get_cache_home().c_str());
11841233
} else if (_key == "tls.ca_file") {
11851234
*output = strdup(configurer::Configuration::get_tls_ca_file().c_str());
1235+
} else if (_key == "monitoring.file") {
1236+
*output =
1237+
strdup(configurer::Configuration::get_monitoring_file().c_str());
11861238
}
11871239

11881240
else {
@@ -1193,3 +1245,35 @@ int scitoken_config_get_str(const char *key, char **output, char **err_msg) {
11931245
}
11941246
return 0;
11951247
}
1248+
1249+
int scitoken_get_monitoring_json(char **json_out, char **err_msg) {
1250+
if (!json_out) {
1251+
if (err_msg) {
1252+
*err_msg = strdup("JSON output pointer may not be null.");
1253+
}
1254+
return -1;
1255+
}
1256+
try {
1257+
std::string json =
1258+
scitokens::internal::MonitoringStats::instance().get_json();
1259+
*json_out = strdup(json.c_str());
1260+
} catch (std::exception &exc) {
1261+
if (err_msg) {
1262+
*err_msg = strdup(exc.what());
1263+
}
1264+
return -1;
1265+
}
1266+
return 0;
1267+
}
1268+
1269+
int scitoken_reset_monitoring_stats(char **err_msg) {
1270+
try {
1271+
scitokens::internal::MonitoringStats::instance().reset();
1272+
} catch (std::exception &exc) {
1273+
if (err_msg) {
1274+
*err_msg = strdup(exc.what());
1275+
}
1276+
return -1;
1277+
}
1278+
return 0;
1279+
}

src/scitokens.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,12 @@ int config_set_int(const char *key, int value, char **err_msg);
302302
* Takes in key/value pairs and assigns the input value to whatever
303303
* configuration variable is indicated by the key.
304304
* Returns 0 on success, and non-zero for invalid keys or values.
305+
*
306+
* Supported keys:
307+
* - "keycache.update_interval_s": Interval between key cache updates (seconds)
308+
* - "keycache.expiration_interval_s": Key cache expiration time (seconds)
309+
* - "monitoring.file_interval_s": Interval between monitoring file writes
310+
* (seconds, default 60)
305311
*/
306312
int scitoken_config_set_int(const char *key, int value, char **err_msg);
307313

@@ -313,22 +319,77 @@ int config_get_int(const char *key, char **err_msg);
313319
* Returns the value associated with the supplied input key on success, and -1
314320
* on failure. This assumes there are no keys for which a negative return value
315321
* is permissible.
322+
*
323+
* Supported keys:
324+
* - "keycache.update_interval_s": Interval between key cache updates (seconds)
325+
* - "keycache.expiration_interval_s": Key cache expiration time (seconds)
326+
* - "monitoring.file_interval_s": Interval between monitoring file writes
327+
* (seconds, default 60)
316328
*/
317329
int scitoken_config_get_int(const char *key, char **err_msg);
318330

319331
/**
320332
* Set current scitokens str parameters.
321333
* Returns 0 on success, nonzero on failure
334+
*
335+
* Supported keys:
336+
* - "keycache.cache_home": Directory for the key cache
337+
* - "tls.ca_file": Path to TLS CA certificate file
338+
* - "monitoring.file": Path to write monitoring JSON (empty to disable, default
339+
* disabled) When enabled, monitoring stats are written periodically during
340+
* verify() calls. The write interval is controlled by
341+
* "monitoring.file_interval_s".
322342
*/
323343
int scitoken_config_set_str(const char *key, const char *value, char **err_msg);
324344

325345
/**
326346
* Get current scitokens str parameters.
327347
* Returns 0 on success, nonzero on failure, and populates the value associated
328348
* with the input key to output.
349+
*
350+
* Supported keys:
351+
* - "keycache.cache_home": Directory for the key cache
352+
* - "tls.ca_file": Path to TLS CA certificate file
353+
* - "monitoring.file": Path to write monitoring JSON (empty if disabled)
329354
*/
330355
int scitoken_config_get_str(const char *key, char **output, char **err_msg);
331356

357+
/**
358+
* Get monitoring statistics as a JSON string.
359+
* Returns a JSON object containing per-issuer validation statistics.
360+
*
361+
* Per-issuer statistics (under "issuers" key):
362+
* - successful_validations: count of successful token validations
363+
* - unsuccessful_validations: count of failed token validations
364+
* - expired_tokens: count of expired tokens encountered
365+
* - sync_validations_started: count of validations started via blocking API
366+
* - async_validations_started: count of validations started via async API
367+
* - sync_total_time_s: time spent in blocking verify() calls (updated every
368+
* 50ms)
369+
* - async_total_time_s: time spent in async validations (updated on completion)
370+
* - total_validation_time_s: sum of sync and async time
371+
* - successful_key_lookups: count of successful JWKS web refreshes
372+
* - failed_key_lookups: count of failed JWKS web refreshes
373+
* - failed_key_lookup_time_s: total time spent on failed key lookups
374+
* - expired_keys: count of times keys expired before refresh completed
375+
* - failed_refreshes: count of failed key refresh attempts (used cached keys)
376+
* - stale_key_uses: count of times keys were used past their next_update time
377+
*
378+
* Failed issuer lookups (under "failed_issuer_lookups" key):
379+
* - Per unknown issuer: count and total_time_s of failed lookup attempts
380+
* - Limited to 100 entries to prevent resource exhaustion from DDoS attacks
381+
*
382+
* The returned string must be freed by the caller using free().
383+
* Returns 0 on success, nonzero on failure.
384+
*/
385+
int scitoken_get_monitoring_json(char **json_out, char **err_msg);
386+
387+
/**
388+
* Reset all monitoring statistics.
389+
* Returns 0 on success, nonzero on failure.
390+
*/
391+
int scitoken_reset_monitoring_stats(char **err_msg);
392+
332393
#ifdef __cplusplus
333394
}
334395
#endif

0 commit comments

Comments
 (0)