Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache location config #120

Merged
merged 14 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
85 changes: 78 additions & 7 deletions src/scitokens.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
#include <atomic>
#include <exception>
#include <string.h>

#include <sys/stat.h>

#include "scitokens.h"
#include "scitokens_internal.h"

/**
* GLOBALS
*/

// Cache timeout config
std::atomic_int configurer::Configuration::m_next_update_delta{600};
std::atomic_int configurer::Configuration::m_expiry_delta{4 * 24 * 3600};

// SciTokens cache home config
std::shared_ptr<std::string> configurer::Configuration::m_cache_home =
std::make_shared<std::string>("");

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 @@ -950,6 +956,10 @@ int keycache_set_jwks(const char *issuer, const char *jwks, char **err_msg) {
}

int config_set_int(const char *key, int value, char **err_msg) {
return scitokens_config_set_int(key, value, err_msg);
}

int scitokens_config_set_int(const char *key, int value, char **err_msg) {
if (!key) {
if (err_msg) {
*err_msg = strdup("A key must be provided.");
Expand All @@ -958,7 +968,6 @@ int config_set_int(const char *key, int value, char **err_msg) {
}

std::string _key = key;

if (_key == "keycache.update_interval_s") {
if (value < 0) {
if (err_msg) {
Expand All @@ -970,8 +979,8 @@ int config_set_int(const char *key, int value, char **err_msg) {
return 0;
}

if (_key == "keycache.expiration_interval_s") {
if (value < 0 ) {
else if (_key == "keycache.expiration_interval_s") {
if (value < 0) {
if (err_msg) {
*err_msg = strdup("Expiry interval must be positive.");
}
Expand All @@ -990,6 +999,10 @@ int config_set_int(const char *key, int value, char **err_msg) {
}

int config_get_int(const char *key, char **err_msg) {
return scitokens_config_get_int(key, err_msg);
}

int scitokens_config_get_int(const char *key, char **err_msg) {
if (!key) {
if (err_msg) {
*err_msg = strdup("A key must be provided.");
Expand All @@ -998,12 +1011,11 @@ int config_get_int(const char *key, char **err_msg) {
}

std::string _key = key;

if (_key == "keycache.update_interval_s") {
return configurer::Configuration::get_next_update_delta();
}

if (_key == "keycache.expiration_interval_s") {
else if (_key == "keycache.expiration_interval_s") {
return configurer::Configuration::get_expiry_delta();
}

Expand All @@ -1013,4 +1025,63 @@ int config_get_int(const char *key, char **err_msg) {
}
return -1;
}
}
}

int config_set_str(const char *key, const char *value, char **err_msg) {
return scitokens_config_set_str(key, value, err_msg);
}

int scitokens_config_set_str(const char *key, const char *value,
char **err_msg) {
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
if (!key) {
if (err_msg) {
*err_msg = strdup("A key must be provided.");
}
return -1;
}

std::string _key = key;
if (_key == "keycache.cache_home") {
auto rp = configurer::Configuration::set_cache_home(value);
if (!rp.first) { // There was an error, pass rp.second to err_msg
if (err_msg) {
*err_msg = strdup(rp.second.c_str());
}
return -1;
}
}

else {
if (err_msg) {
*err_msg = strdup("Key not recognized.");
}
return -1;
}
return 0;
}

int config_get_str(const char *key, char **output, char **err_msg) {
return scitokens_config_get_str(key, output, err_msg);
}

int scitokens_config_get_str(const char *key, char **output, char **err_msg) {
if (!key) {
if (err_msg) {
*err_msg = strdup("A key must be provided.");
}
return -1;
}

std::string _key = key;
if (_key == "keycache.cache_home") {
*output = strdup(configurer::Configuration::get_cache_home().c_str());
}

else {
if (err_msg) {
*err_msg = strdup("Key not recognized.");
}
return -1;
}
return 0;
}
30 changes: 28 additions & 2 deletions src/scitokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,21 +295,47 @@ int keycache_set_jwks(const char *issuer, const char *jwks, char **err_msg);
*/

/**
* Update scitokens parameters.
* Update scitokens int parameters.
* 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.
*/
int config_set_int(const char *key, int value, char **err_msg);

// Prefixed version of the same API to avoid potential symbol collisions
int scitokens_config_set_int(const char *key, int value, char **err_msg);

/**
* Get current scitokens parameters.
* Get current scitokens int parameters.
* 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.
*/
int config_get_int(const char *key, char **err_msg);

// Prefixed version of the same API to avoid potential symbol collisions
int scitokens_config_get_int(const char *key, char **err_msg);

/**
* Set current scitokens str parameters.
* Returns 0 on success, nonzero on failure
*/
int config_set_str(const char *key, const char *value, char **err_msg);
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved

// Prefixed version of the same API to avoid potential symbol collisions
int scitokens_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.
*/
int config_get_str(const char *key, char **output, char **err_msg);

// Prefixed version of the same API to avoid potential symbol collisions
int scitokens_config_get_str(const char *key, char **output, char **err_msg);

#ifdef __cplusplus
}
#endif
17 changes: 13 additions & 4 deletions src/scitokens_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ void initialize_cachedb(const std::string &keycache_file) {

/**
* Get the Cache file location
*
* 1. $XDG_CACHE_HOME
* 2. .cache subdirectory of home directory as returned by the password
* 1. User-defined through config api
* 2. $XDG_CACHE_HOME
* 3. .cache subdirectory of home directory as returned by the password
* database
*/
std::string get_cache_file() {
Expand All @@ -64,7 +64,16 @@ std::string get_cache_file() {
home_dir += "/.cache";
}

std::string cache_dir(xdg_cache_home ? xdg_cache_home : home_dir.c_str());
// Figure out where to plop the cache based on priority
std::string cache_dir;
std::string configured_cache_dir =
configurer::Configuration::get_cache_home();
if (configured_cache_dir.length() > 0) { // The variable has been configured
cache_dir = configured_cache_dir;
} else {
cache_dir = xdg_cache_home ? xdg_cache_home : home_dir.c_str();
}

if (cache_dir.size() == 0) {
return "";
}
Expand Down
87 changes: 87 additions & 0 deletions src/scitokens_internal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <functional>
#include <memory>
#include <sstream>
#include <sys/stat.h>

#include <jwt-cpp/base.h>
#include <jwt-cpp/jwt.h>
Expand Down Expand Up @@ -1064,3 +1065,89 @@ bool scitokens::Enforcer::scope_validator(const jwt::claim &claim,

return me->m_test_authz.empty();
}

// Configuration class functions
std::pair<bool, std::string>
configurer::Configuration::set_cache_home(const std::string dir_path) {
// If setting to "", then we should treat as though it is unsetting the
// config
if (dir_path.length() == 0) { // User is configuring to empty string
m_cache_home = std::make_shared<std::string>(dir_path);
return std::make_pair(true, "");
}

std::vector<std::string> path_components =
path_split(dir_path); // cleans any extraneous /'s
std::string cleaned_dir_path;
for (const auto &component :
path_components) { // add the / back to the path components
cleaned_dir_path += "/" + component;
}

// Check that the cache_home exists, and if not try to create it
auto rp = mkdir_and_parents_if_needed(
cleaned_dir_path); // Structured bindings not introduced until cpp 17
if (!rp.first) { //
std::string err_prefix{
"An issue was encountered with the provided cache home path: "};
return std::make_pair(false, err_prefix + rp.second);
}

// Now it exists and we can write to it, set the value and let
// scitokens_cache handle the rest
m_cache_home = std::make_shared<std::string>(cleaned_dir_path);
return std::make_pair(true, "");
}

std::string configurer::Configuration::get_cache_home() {
return *m_cache_home;
}

// bool configurer::Configuration::check_dir(const std::string dir_path) {
// struct stat info;
// return stat(dir_path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
// }

std::pair<bool, std::string>
configurer::Configuration::mkdir_and_parents_if_needed(
const std::string dir_path) {
// SciTokens-cpp already makes assumptions about using Linux file paths,
// so making that assumption here as well.

// Using these perms because that's what the actual cache file uses in
// scitokens_cache
mode_t mode = 0700; // Maybe these permissions should be configurable?

int result;
std::string currentLevel;
std::vector<std::string> path_components = path_split(dir_path);
for (const auto &component : path_components) {
currentLevel += "/" + component;
result = mkdir(currentLevel.c_str(), mode);
if ((result < 0) && errno != EEXIST) {
std::string err_prefix{"There was an error while creating/checking "
"the directory: mkdir error: "};
return std::make_pair(false, err_prefix + strerror(errno));
}
}

return std::make_pair(true, "");
}

std::vector<std::string>
configurer::Configuration::path_split(std::string path) {
std::vector<std::string> path_components;
std::stringstream ss(path);
std::string component;

while (std::getline(ss, component, '/')) {
if (!component.empty()) {
path_components.push_back(component);
}
}

if (path_components[0] == "") {
path_components.erase(path_components.begin());
}
return path_components;
}
8 changes: 8 additions & 0 deletions src/scitokens_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,17 @@ class Configuration {
m_expiry_delta = _expiry_delta;
}
static int get_expiry_delta() { return m_expiry_delta; }
static std::pair<bool, std::string> set_cache_home(const std::string cache_home);
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
static std::string get_cache_home();

private:
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
static std::atomic_int m_next_update_delta;
static std::atomic_int m_expiry_delta;
static std::shared_ptr<std::string> m_cache_home;
// static bool check_dir(const std::string dir_path);
static std::pair<bool, std::string>
mkdir_and_parents_if_needed(const std::string dir_path);
static std::vector<std::string> path_split(const std::string dir_path);
};
} // namespace configurer

Expand Down
38 changes: 38 additions & 0 deletions test/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <gtest/gtest.h>
#include <memory>
#include <unistd.h>

namespace {

Expand Down Expand Up @@ -165,6 +166,43 @@ TEST_F(KeycacheTest, SetGetTest) {
EXPECT_EQ(demo_scitokens2, jwks_str);
}

TEST_F(KeycacheTest, SetGetConfiguredCacheHome) {
// Set cache home
char cache_path[FILENAME_MAX];
ASSERT_TRUE(getcwd(cache_path, sizeof(cache_path)) != nullptr); // Side effect gets cwd
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
char *err_msg;
std::string key = "keycache.cache_home";

auto rv = config_set_str(key.c_str(), cache_path, &err_msg);
ASSERT_TRUE(rv == 0) << err_msg;

// Set the jwks at the new cache home
rv = keycache_set_jwks(demo_scitokens_url.c_str(), demo_scitokens2.c_str(),
&err_msg);
ASSERT_TRUE(rv == 0) << err_msg;

// Fetch the cached jwks from the new cache home
char *jwks;
rv = keycache_get_cached_jwks(demo_scitokens_url.c_str(), &jwks, &err_msg);
ASSERT_TRUE(rv == 0);
ASSERT_TRUE(jwks != nullptr);
std::string jwks_str(jwks);
free(jwks);

EXPECT_EQ(demo_scitokens2, jwks_str);

// Check that cache home is still what was set
char *output;
rv = config_get_str(key.c_str(), &output, &err_msg);
ASSERT_TRUE(rv == 0) << err_msg;
EXPECT_EQ(*output, *cache_path);
free(output);

// Reset cache home to whatever it was before by setting empty config
rv = config_set_str(key.c_str(), "", &err_msg);
ASSERT_TRUE(rv == 0) << err_msg;
}

TEST_F(KeycacheTest, InvalidConfigKeyTest) {
char *err_msg;
int new_update_interval = 400;
Expand Down