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 3 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
67 changes: 60 additions & 7 deletions src/scitokens.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
#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::string configurer::Configuration::m_cache_home{""};

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 @@ -958,7 +963,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 +974,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 @@ -998,12 +1002,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 +1016,54 @@ 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) {
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) {
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;
}
17 changes: 15 additions & 2 deletions src/scitokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,21 +295,34 @@ 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);

/**
* 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);

/**
* 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

/**
* 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);

#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 =
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
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
73 changes: 73 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,75 @@ bool scitokens::Enforcer::scope_validator(const jwt::claim &claim,

return me->m_test_authz.empty();
}

// Configuration class functions
std::pair<bool, std::string>
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
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 = dir_path;
return std::make_pair(true, "");
}

// Check that the cache_home exists, and if not try to create it
if (!check_dir(dir_path)) {
if (!mkdir_and_parents_if_needed(dir_path)) {
return std::make_pair(false, "The provided cache home path did not "
"exist and could not be created.");
}
}

// Now it exists and we can write to it, set the value and let
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
// scitokens_cache handle the rest
m_cache_home = 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);
}

bool 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
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
// scitokens_cache
mode_t mode = 0700; // Maybe these permissions should be configurable?

int result;
std::string currentLevel;
std::vector<std::string> pathComponents = path_split(dir_path);
for (const auto &component : pathComponents) {
currentLevel += "/" + component;
if (!check_dir(currentLevel)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't check the dir first; just create it and inspect the mkdir return code (if it's EEXIST, then the directory already exists and move on).

For error message creation, it would be better to have an output variable that's the error message so you can say precisely the file operation that failed. The caller of this function currently has something generic that won't be useful for debugging.

result = mkdir(currentLevel.c_str(), mode);
if (!result == 0) {
return false;
}
}
}

return result == 0;
}

std::vector<std::string>
jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
configurer::Configuration::path_split(std::string path) {
std::vector<std::string> pathComponents;
std::stringstream ss(path);
std::string component;

while (std::getline(ss, component, '/')) {
pathComponents.push_back(component);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if someone provides a path of ///foo//bar? May need an if (!component.empty()) here to avoid inserting unnecessary items.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You caught my sinful shortcut. Since ultimately this output only gets passed to mkdir, the POSIX standard should suppress the extra slashes (so that the cache would be dumped at /foo/bar even if it was specified as ///foo//bar). But I agree, I should add the check in case this function ever gets used in another spot where someone might not know about that peculiarity.

}

if (pathComponents[0] == "") {
pathComponents.erase(pathComponents.begin());
}
return pathComponents;
}
7 changes: 7 additions & 0 deletions src/scitokens_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,16 @@ 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();

jhiemstrawisc marked this conversation as resolved.
Show resolved Hide resolved
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::string m_cache_home;
static bool check_dir(const std::string dir_path);
static bool 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
36 changes: 36 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,41 @@ TEST_F(KeycacheTest, SetGetTest) {
EXPECT_EQ(demo_scitokens2, jwks_str);
}

TEST_F(KeycacheTest, SetGetConfiguredCacheHome) {
// Set cache home and jwks
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;

rv = keycache_set_jwks(demo_scitokens_url.c_str(), demo_scitokens2.c_str(),
&err_msg);
ASSERT_TRUE(rv == 0);

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);

// Get cache home
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 cnfg
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