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
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ find_package( OpenSSL REQUIRED )
find_package( Sqlite3 REQUIRED )
set(LIBCRYPTO_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR})
set(LIBCRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
set(CMAKE_MACOSX_RPATH ON)

elseif( UNIX )

Expand Down Expand Up @@ -60,6 +61,9 @@ target_link_libraries(scitokens-test-access SciTokens)
add_executable(scitokens-list-access src/list_access.cpp)
target_link_libraries(scitokens-list-access SciTokens)

add_executable(scitokens-create src/create.cpp)
target_link_libraries(scitokens-create SciTokens)

if (NOT DEFINED LIB_INSTALL_DIR)
SET(LIB_INSTALL_DIR "lib")
endif()
Expand Down
198 changes: 198 additions & 0 deletions src/create.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@

#include "scitokens.h"

#include <stdlib.h>
#include <getopt.h>

#include <cstdio>
#include <string>
#include <vector>
#include <fstream>

namespace {

const char usage[] = \
"\n"
"Syntax: %s [--cred cred_file] [--key key_file] [--keyid kid]\n"
" [--claim key=val] ...\n"
"\n"
" Options\n"
" -h | --help Display usage\n"
" -c | --cred <cred_file> File containing signing credential.\n"
" -k | --key <key_file> File containing the signing private key.\n"
" -K | --keyid <kid> Name of the token key.\n"
" -i | --issuer <issuer> Issuer for the token.\n"
" -p | --profile <profile> Token profile (wlcg, scitokens1, scitokens2).\n"
"\n";

const struct option long_options[] =
{
{"help", no_argument, NULL, 'h'},
{"cred", required_argument, NULL, 'c'},
{"key", required_argument, NULL, 'k'},
{"keyid", required_argument, NULL, 'K'},
{"issuer", required_argument, NULL, 'i'},
{"claim", required_argument, NULL, 'C'},
{"profile", required_argument, NULL, 'p'},
{0, 0, 0, 0}
};

const char short_options[] = "hc:k:K:i:C:p:";

std::string g_cred, g_key, g_kid, g_issuer, g_profile;
std::vector<std::string> g_claims;

int init_arguments(int argc, char *argv[]) {

int arg;
while((arg = getopt_long(argc, argv, short_options, long_options, nullptr)) != -1)
{
switch (arg)
{
case 'h':
printf(usage, argv[0]);
exit(0);
break;
case 'c':
g_cred = optarg;
break;
case 'k':
g_key = optarg;
break;
case 'K':
g_kid = optarg;
break;
case 'i':
g_issuer = optarg;
break;
case 'C':
g_claims.emplace_back(optarg);
break;
case 'p':
g_profile = optarg;
break;
default:
fprintf(stderr, usage, argv[0]);
exit(1);
break;
}
}

if (optind != argc) {
fprintf(stderr, "%s: invalid option -- %s\n", argv[0], argv[optind]);
fprintf(stderr, usage, argv[0]);
exit(1);
}

if (g_cred.empty()) {
fprintf(stderr, "%s: missing --cred option\n", argv[0]);
fprintf(stderr, usage, argv[0]);
exit(1);
}

if (g_key.empty()) {
fprintf(stderr, "%s: missing --key option\n", argv[0]);
fprintf(stderr, usage, argv[0]);
exit(1);
}

if (g_kid.empty()) {
fprintf(stderr, "%s: missing --keyid option\n", argv[0]);
fprintf(stderr, usage, argv[0]);
exit(1);
}

if (g_issuer.empty()) {
fprintf(stderr, "%s: missing --issuer option\n", argv[0]);
fprintf(stderr, usage, argv[0]);
exit(1);
}

return 0;
}

}

int main(int argc, char *argv[]) {

int rv = init_arguments(argc, argv);
if (rv) {
return rv;
}

std::ifstream priv_ifs(g_key);
std::string private_contents( (std::istreambuf_iterator<char>(priv_ifs)),
(std::istreambuf_iterator<char>())
);
std::ifstream pub_ifs(g_cred);
std::string public_contents( (std::istreambuf_iterator<char>(pub_ifs)),
(std::istreambuf_iterator<char>())
);

char *err_msg;
auto key_raw = scitoken_key_create(g_kid.c_str(), "ES256", public_contents.c_str(),
private_contents.c_str(), &err_msg);
std::unique_ptr<void, decltype(&scitoken_key_destroy)>
key(key_raw, scitoken_key_destroy);
if (key_raw == nullptr) {
fprintf(stderr, "Failed to generate a key: %s\n", err_msg);
free(err_msg);
return 1;
}

std::unique_ptr<void, decltype(&scitoken_destroy)>
token(scitoken_create(key_raw), scitoken_destroy);
if (token.get() == nullptr) {
fprintf(stderr, "Failed to generate a new token.\n");
return 1;
}

rv = scitoken_set_claim_string(token.get(), "iss", g_issuer.c_str(), &err_msg);
if (rv) {
fprintf(stderr, "Failed to set issuer: %s\n", err_msg);
free(err_msg);
return 1;
}

for (const auto &claim : g_claims) {
auto pos = claim.find("=");
if (pos == std::string::npos) {
fprintf(stderr, "Claim must contain a '=' character: %s\n", claim.c_str());
return 1;
}
auto key = claim.substr(0, pos);
auto val = claim.substr(pos + 1);

rv = scitoken_set_claim_string(token.get(), key.c_str(), val.c_str(), &err_msg);
if (rv) {
fprintf(stderr, "Failed to set claim (%s=%s): %s\n", key.c_str(), val.c_str(), err_msg);
free(err_msg);
return 1;
}
}

if (!g_profile.empty()) {
SciTokenProfile profile;
if (g_profile == "wlcg") {
profile = SciTokenProfile::WLCG_1_0;
} else if (g_profile == "scitokens1") {
profile = SciTokenProfile::SCITOKENS_1_0;
} else if (g_profile == "scitokens2") {
profile = SciTokenProfile::SCITOKENS_2_0;
} else {
fprintf(stderr, "Unknown token profile: %s\n", g_profile.c_str());
return 1;
}
scitoken_set_serialize_mode(token.get(), profile);
}

char *value;
rv = scitoken_serialize(token.get(), &value, &err_msg);
if (rv) {
fprintf(stderr, "Failed to serialize the token: %s\n", err_msg);
free(err_msg);
return 1;
}

printf("%s\n", value);
}
45 changes: 45 additions & 0 deletions src/scitokens.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ int scitoken_set_claim_string(SciToken token, const char *key, const char *value
}


void scitoken_set_serialize_mode(SciToken token, SciTokenProfile profile) {
scitokens::SciToken *real_token = reinterpret_cast<scitokens::SciToken*>(token);
if (real_token == nullptr) {return;}

real_token->set_serialize_mode(static_cast<scitokens::SciToken::Profile>(profile));
}


int scitoken_get_claim_string(const SciToken token, const char *key, char **value, char **err_msg) {
scitokens::SciToken *real_token = reinterpret_cast<scitokens::SciToken*>(token);
std::string claim_str;
Expand Down Expand Up @@ -161,10 +169,39 @@ int scitoken_deserialize(const char *value, SciToken *token, char const* const*
return 0;
}

int scitoken_store_public_ec_key(const char *issuer, const char *keyid, const char *key, char **err_msg)
{
bool success;
try {
success = scitokens::Validator::store_public_ec_key(issuer, keyid, key);
} catch (std::exception &exc) {
if (err_msg) {
*err_msg = strdup(exc.what());
}
return -1;
}

return success ? 0 : -1;
}

Validator validator_create() {
return new Validator();
}


void validator_destroy(Validator validator) {
scitokens::Validator *real_validator =
reinterpret_cast<scitokens::Validator*>(validator);
delete real_validator;
}


void validator_set_token_profile(Validator validator, SciTokenProfile profile) {
if (validator == nullptr) {return;}
auto real_validator = reinterpret_cast<scitokens::Validator*>(validator);
real_validator->set_validate_profile(static_cast<scitokens::SciToken::Profile>(profile));
}

int validator_add(Validator validator, const char *claim, StringValidatorFunction validator_func, char **err_msg) {
if (validator == nullptr) {
if (err_msg) {*err_msg = strdup("Validator may not be a null pointer");}
Expand Down Expand Up @@ -257,6 +294,14 @@ void enforcer_acl_free(Acl *acls) {
}


void enforcer_set_validate_profile(Enforcer enf, SciTokenProfile profile) {
if (enf == nullptr) {return;}

auto real_enf = reinterpret_cast<scitokens::Enforcer*>(enf);
real_enf->set_validate_profile(static_cast<scitokens::SciToken::Profile>(profile));
}


int enforcer_generate_acls(const Enforcer enf, const SciToken scitoken, Acl **acls, char **err_msg) {
if (enf == nullptr) {
if (err_msg) {*err_msg = strdup("Enforcer may not be a null pointer");}
Expand Down
40 changes: 40 additions & 0 deletions src/scitokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ typedef struct Acl_s {
}
Acl;

/**
* Determine the mode we will use to validate tokens.
* - COMPAT mode (default) indicates any supported token format
* is acceptable. Where possible, the scope names are translated into
* equivalent SciTokens 1.0 claim names (i.e., storage.read -> read; storage.write -> write).
* - SCITOKENS_1_0, SCITOKENS_2_0, WLCG_1_0: only accept these specific profiles.
* No automatic translation is performed.
*/
typedef enum _profile {
COMPAT = 0,
SCITOKENS_1_0,
SCITOKENS_2_0,
WLCG_1_0
} SciTokenProfile;

SciTokenKey scitoken_key_create(const char *key_id, const char *algorithm, const char *public_contents, const char *private_contents, char **err_msg);

void scitoken_key_destroy(SciTokenKey private_key);
Expand All @@ -38,20 +53,45 @@ void scitoken_set_lifetime(SciToken token, int lifetime);

int scitoken_serialize(const SciToken token, char **value, char **err_msg);

/**
* Set the profile used for serialization; if COMPAT mode is used, then
* the library default is utilized (currently, scitokens 1.0).
*/
void scitoken_set_serialize_mode(SciToken token, SciTokenProfile profile);

int scitoken_deserialize(const char *value, SciToken *token, char const* const* allowed_issuers, char **err_msg);

int scitoken_store_public_ec_key(const char *issuer, const char *keyid, const char *value, char **err_msg);

Validator validator_create();

/**
* Set the profile used for validating the tokens; COMPAT (default) will accept any known token
* type while others will only support that specific profile.
*/
void validator_set_token_profile(Validator, SciTokenProfile profile);

int validator_add(Validator validator, const char *claim, StringValidatorFunction validator_func, char **err_msg);

int validator_add_critical_claims(Validator validator, const char **claims, char **err_msg);

int validator_validate(Validator validator, SciToken scitoken, char **err_msg);

/**
* Destroy a validator object.
*/
void validator_destroy(Validator);

Enforcer enforcer_create(const char *issuer, const char **audience, char **err_msg);

void enforcer_destroy(Enforcer);

/**
* Set the profile used for enforcing ACLs; when set to COMPAT (default), then the authorizations
* will be converted to SciTokens 1.0-style authorizations (so, WLCG's storage.read becomes read).
*/
void enforcer_set_validate_profile(Enforcer, SciTokenProfile profile);

int enforcer_generate_acls(const Enforcer enf, const SciToken scitokens, Acl **acls, char **err_msg);

void enforcer_acl_free(Acl *acls);
Expand Down
Loading