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
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pkg_check_modules(SQLITE REQUIRED sqlite3)

endif()

add_library(SciTokens SHARED src/scitokens.cpp src/scitokens_internal.cpp src/scitokens_cache.cpp)
add_library(SciTokens SHARED src/scitokens.cpp src/scitokens_internal.cpp src/scitokens_cache.cpp src/scitokens_monitoring.cpp)
target_compile_features(SciTokens PUBLIC cxx_std_11) # Use at least C++11 for building and when linking to scitokens
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})

Expand Down Expand Up @@ -75,6 +75,7 @@ target_link_libraries(scitokens-list-access SciTokens)
add_executable(scitokens-create src/create.cpp)
target_link_libraries(scitokens-create SciTokens)


add_executable(scitokens-generate-jwks src/generate_jwks.cpp)
target_include_directories(scitokens-generate-jwks PRIVATE ${OPENSSL_INCLUDE_DIRS} ${LIBCRYPTO_INCLUDE_DIRS})
target_link_libraries(scitokens-generate-jwks ${OPENSSL_LIBRARIES} ${LIBCRYPTO_LIBRARIES})
Expand Down
94 changes: 89 additions & 5 deletions src/scitokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,35 @@ std::shared_ptr<std::string> configurer::Configuration::m_cache_home =
std::shared_ptr<std::string> configurer::Configuration::m_tls_ca_file =
std::make_shared<std::string>("");

// Monitoring file config (empty string means disabled)
// Protected by mutex; atomic flag for fast-path check
std::string configurer::Configuration::m_monitoring_file;
std::mutex configurer::Configuration::m_monitoring_file_mutex;
std::atomic<bool> configurer::Configuration::m_monitoring_file_configured{
false};
std::atomic_int configurer::Configuration::m_monitoring_file_interval{60};

void configurer::Configuration::set_monitoring_file(const std::string &path) {
std::lock_guard<std::mutex> lock(m_monitoring_file_mutex);
m_monitoring_file = path;
// Update the atomic flag after setting the string
m_monitoring_file_configured.store(!path.empty(),
std::memory_order_release);
}

std::string configurer::Configuration::get_monitoring_file() {
std::lock_guard<std::mutex> lock(m_monitoring_file_mutex);
return m_monitoring_file;
}

void configurer::Configuration::set_monitoring_file_interval(int seconds) {
m_monitoring_file_interval = seconds;
}

int configurer::Configuration::get_monitoring_file_interval() {
return m_monitoring_file_interval;
}

SciTokenKey scitoken_key_create(const char *key_id, const char *alg,
const char *public_contents,
const char *private_contents, char **err_msg) {
Expand Down Expand Up @@ -246,10 +275,12 @@ int scitoken_get_expiration(const SciToken token, long long *expiry,
// Float value - convert to integer (truncate)
// Float value - convert to integer using std::floor().
// This ensures expiration is not extended by fractional seconds.
result = static_cast<long long>(std::floor(claim_value.get<double>()));
result =
static_cast<long long>(std::floor(claim_value.get<double>()));
} else {
if (err_msg) {
*err_msg = strdup("'exp' claim must be a number (integer or float)");
*err_msg =
strdup("'exp' claim must be a number (integer or float)");
}
return -1;
}
Expand Down Expand Up @@ -1024,6 +1055,17 @@ int scitoken_config_set_int(const char *key, int value, char **err_msg) {
return 0;
}

else if (_key == "monitoring.file_interval_s") {
if (value < 0) {
if (err_msg) {
*err_msg = strdup("Interval cannot be negative.");
}
return -1;
}
configurer::Configuration::set_monitoring_file_interval(value);
return 0;
}

else {
if (err_msg) {
*err_msg = strdup("Key not recognized.");
Expand Down Expand Up @@ -1053,6 +1095,10 @@ int scitoken_config_get_int(const char *key, char **err_msg) {
return configurer::Configuration::get_expiry_delta();
}

else if (_key == "monitoring.file_interval_s") {
return configurer::Configuration::get_monitoring_file_interval();
}

else {
if (err_msg) {
*err_msg = strdup("Key not recognized.");
Expand Down Expand Up @@ -1080,9 +1126,12 @@ int scitoken_config_set_str(const char *key, const char *value,
return -1;
}
} else if (_key == "tls.ca_file") {
configurer::Configuration::set_tls_ca_file(value ? std::string(value) : "");
}
else {
configurer::Configuration::set_tls_ca_file(value ? std::string(value)
: "");
} else if (_key == "monitoring.file") {
configurer::Configuration::set_monitoring_file(
value ? std::string(value) : "");
} else {
if (err_msg) {
*err_msg = strdup("Key not recognized.");
}
Expand All @@ -1104,6 +1153,9 @@ int scitoken_config_get_str(const char *key, char **output, char **err_msg) {
*output = strdup(configurer::Configuration::get_cache_home().c_str());
} else if (_key == "tls.ca_file") {
*output = strdup(configurer::Configuration::get_tls_ca_file().c_str());
} else if (_key == "monitoring.file") {
*output =
strdup(configurer::Configuration::get_monitoring_file().c_str());
}

else {
Expand All @@ -1114,3 +1166,35 @@ int scitoken_config_get_str(const char *key, char **output, char **err_msg) {
}
return 0;
}

int scitoken_get_monitoring_json(char **json_out, char **err_msg) {
if (!json_out) {
if (err_msg) {
*err_msg = strdup("JSON output pointer may not be null.");
}
return -1;
}
try {
std::string json =
scitokens::internal::MonitoringStats::instance().get_json();
*json_out = strdup(json.c_str());
} catch (std::exception &exc) {
if (err_msg) {
*err_msg = strdup(exc.what());
}
return -1;
}
return 0;
}

int scitoken_reset_monitoring_stats(char **err_msg) {
try {
scitokens::internal::MonitoringStats::instance().reset();
} catch (std::exception &exc) {
if (err_msg) {
*err_msg = strdup(exc.what());
}
return -1;
}
return 0;
}
61 changes: 61 additions & 0 deletions src/scitokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,12 @@ int config_set_int(const char *key, int value, char **err_msg);
* Takes in key/value pairs and assigns the input value to whatever
* configuration variable is indicated by the key.
* Returns 0 on success, and non-zero for invalid keys or values.
*
* Supported keys:
* - "keycache.update_interval_s": Interval between key cache updates (seconds)
* - "keycache.expiration_interval_s": Key cache expiration time (seconds)
* - "monitoring.file_interval_s": Interval between monitoring file writes
* (seconds, default 60)
*/
int scitoken_config_set_int(const char *key, int value, char **err_msg);

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

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

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

/**
* Get monitoring statistics as a JSON string.
* Returns a JSON object containing per-issuer validation statistics.
*
* Per-issuer statistics (under "issuers" key):
* - successful_validations: count of successful token validations
* - unsuccessful_validations: count of failed token validations
* - expired_tokens: count of expired tokens encountered
* - sync_validations_started: count of validations started via blocking API
* - async_validations_started: count of validations started via async API
* - sync_total_time_s: time spent in blocking verify() calls (updated every
* 50ms)
* - async_total_time_s: time spent in async validations (updated on completion)
* - total_validation_time_s: sum of sync and async time
* - successful_key_lookups: count of successful JWKS web refreshes
* - failed_key_lookups: count of failed JWKS web refreshes
* - failed_key_lookup_time_s: total time spent on failed key lookups
* - expired_keys: count of times keys expired before refresh completed
* - failed_refreshes: count of failed key refresh attempts (used cached keys)
* - stale_key_uses: count of times keys were used past their next_update time
*
* Failed issuer lookups (under "failed_issuer_lookups" key):
* - Per unknown issuer: count and total_time_s of failed lookup attempts
* - Limited to 100 entries to prevent resource exhaustion from DDoS attacks
*
* The returned string must be freed by the caller using free().
* Returns 0 on success, nonzero on failure.
*/
int scitoken_get_monitoring_json(char **json_out, char **err_msg);

/**
* Reset all monitoring statistics.
* Returns 0 on success, nonzero on failure.
*/
int scitoken_reset_monitoring_stats(char **err_msg);

#ifdef __cplusplus
}
#endif
Loading
Loading