Skip to content

Commit a5253a6

Browse files
authored
Merge pull request #19 from bbockelm/wlcg_profile
Add support for the WLCG profile
2 parents 3db6d4a + 7d2178d commit a5253a6

File tree

7 files changed

+626
-20
lines changed

7 files changed

+626
-20
lines changed

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ find_package( OpenSSL REQUIRED )
2828
find_package( Sqlite3 REQUIRED )
2929
set(LIBCRYPTO_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR})
3030
set(LIBCRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY})
31+
set(CMAKE_MACOSX_RPATH ON)
3132

3233
elseif( UNIX )
3334

@@ -62,6 +63,9 @@ target_link_libraries(scitokens-test-access SciTokens)
6263
add_executable(scitokens-list-access src/list_access.cpp)
6364
target_link_libraries(scitokens-list-access SciTokens)
6465

66+
add_executable(scitokens-create src/create.cpp)
67+
target_link_libraries(scitokens-create SciTokens)
68+
6569
if (NOT DEFINED LIB_INSTALL_DIR)
6670
SET(LIB_INSTALL_DIR "lib")
6771
endif()

src/create.cpp

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
2+
#include "scitokens.h"
3+
4+
#include <stdlib.h>
5+
#include <getopt.h>
6+
7+
#include <cstdio>
8+
#include <string>
9+
#include <vector>
10+
#include <fstream>
11+
12+
namespace {
13+
14+
const char usage[] = \
15+
"\n"
16+
"Syntax: %s [--cred cred_file] [--key key_file] [--keyid kid]\n"
17+
" [--claim key=val] ...\n"
18+
"\n"
19+
" Options\n"
20+
" -h | --help Display usage\n"
21+
" -c | --cred <cred_file> File containing signing credential.\n"
22+
" -k | --key <key_file> File containing the signing private key.\n"
23+
" -K | --keyid <kid> Name of the token key.\n"
24+
" -i | --issuer <issuer> Issuer for the token.\n"
25+
" -p | --profile <profile> Token profile (wlcg, scitokens1, scitokens2).\n"
26+
"\n";
27+
28+
const struct option long_options[] =
29+
{
30+
{"help", no_argument, NULL, 'h'},
31+
{"cred", required_argument, NULL, 'c'},
32+
{"key", required_argument, NULL, 'k'},
33+
{"keyid", required_argument, NULL, 'K'},
34+
{"issuer", required_argument, NULL, 'i'},
35+
{"claim", required_argument, NULL, 'C'},
36+
{"profile", required_argument, NULL, 'p'},
37+
{0, 0, 0, 0}
38+
};
39+
40+
const char short_options[] = "hc:k:K:i:C:p:";
41+
42+
std::string g_cred, g_key, g_kid, g_issuer, g_profile;
43+
std::vector<std::string> g_claims;
44+
45+
int init_arguments(int argc, char *argv[]) {
46+
47+
int arg;
48+
while((arg = getopt_long(argc, argv, short_options, long_options, nullptr)) != -1)
49+
{
50+
switch (arg)
51+
{
52+
case 'h':
53+
printf(usage, argv[0]);
54+
exit(0);
55+
break;
56+
case 'c':
57+
g_cred = optarg;
58+
break;
59+
case 'k':
60+
g_key = optarg;
61+
break;
62+
case 'K':
63+
g_kid = optarg;
64+
break;
65+
case 'i':
66+
g_issuer = optarg;
67+
break;
68+
case 'C':
69+
g_claims.emplace_back(optarg);
70+
break;
71+
case 'p':
72+
g_profile = optarg;
73+
break;
74+
default:
75+
fprintf(stderr, usage, argv[0]);
76+
exit(1);
77+
break;
78+
}
79+
}
80+
81+
if (optind != argc) {
82+
fprintf(stderr, "%s: invalid option -- %s\n", argv[0], argv[optind]);
83+
fprintf(stderr, usage, argv[0]);
84+
exit(1);
85+
}
86+
87+
if (g_cred.empty()) {
88+
fprintf(stderr, "%s: missing --cred option\n", argv[0]);
89+
fprintf(stderr, usage, argv[0]);
90+
exit(1);
91+
}
92+
93+
if (g_key.empty()) {
94+
fprintf(stderr, "%s: missing --key option\n", argv[0]);
95+
fprintf(stderr, usage, argv[0]);
96+
exit(1);
97+
}
98+
99+
if (g_kid.empty()) {
100+
fprintf(stderr, "%s: missing --keyid option\n", argv[0]);
101+
fprintf(stderr, usage, argv[0]);
102+
exit(1);
103+
}
104+
105+
if (g_issuer.empty()) {
106+
fprintf(stderr, "%s: missing --issuer option\n", argv[0]);
107+
fprintf(stderr, usage, argv[0]);
108+
exit(1);
109+
}
110+
111+
return 0;
112+
}
113+
114+
}
115+
116+
int main(int argc, char *argv[]) {
117+
118+
int rv = init_arguments(argc, argv);
119+
if (rv) {
120+
return rv;
121+
}
122+
123+
std::ifstream priv_ifs(g_key);
124+
std::string private_contents( (std::istreambuf_iterator<char>(priv_ifs)),
125+
(std::istreambuf_iterator<char>())
126+
);
127+
std::ifstream pub_ifs(g_cred);
128+
std::string public_contents( (std::istreambuf_iterator<char>(pub_ifs)),
129+
(std::istreambuf_iterator<char>())
130+
);
131+
132+
char *err_msg;
133+
auto key_raw = scitoken_key_create(g_kid.c_str(), "ES256", public_contents.c_str(),
134+
private_contents.c_str(), &err_msg);
135+
std::unique_ptr<void, decltype(&scitoken_key_destroy)>
136+
key(key_raw, scitoken_key_destroy);
137+
if (key_raw == nullptr) {
138+
fprintf(stderr, "Failed to generate a key: %s\n", err_msg);
139+
free(err_msg);
140+
return 1;
141+
}
142+
143+
std::unique_ptr<void, decltype(&scitoken_destroy)>
144+
token(scitoken_create(key_raw), scitoken_destroy);
145+
if (token.get() == nullptr) {
146+
fprintf(stderr, "Failed to generate a new token.\n");
147+
return 1;
148+
}
149+
150+
rv = scitoken_set_claim_string(token.get(), "iss", g_issuer.c_str(), &err_msg);
151+
if (rv) {
152+
fprintf(stderr, "Failed to set issuer: %s\n", err_msg);
153+
free(err_msg);
154+
return 1;
155+
}
156+
157+
for (const auto &claim : g_claims) {
158+
auto pos = claim.find("=");
159+
if (pos == std::string::npos) {
160+
fprintf(stderr, "Claim must contain a '=' character: %s\n", claim.c_str());
161+
return 1;
162+
}
163+
auto key = claim.substr(0, pos);
164+
auto val = claim.substr(pos + 1);
165+
166+
rv = scitoken_set_claim_string(token.get(), key.c_str(), val.c_str(), &err_msg);
167+
if (rv) {
168+
fprintf(stderr, "Failed to set claim (%s=%s): %s\n", key.c_str(), val.c_str(), err_msg);
169+
free(err_msg);
170+
return 1;
171+
}
172+
}
173+
174+
if (!g_profile.empty()) {
175+
SciTokenProfile profile;
176+
if (g_profile == "wlcg") {
177+
profile = SciTokenProfile::WLCG_1_0;
178+
} else if (g_profile == "scitokens1") {
179+
profile = SciTokenProfile::SCITOKENS_1_0;
180+
} else if (g_profile == "scitokens2") {
181+
profile = SciTokenProfile::SCITOKENS_2_0;
182+
} else {
183+
fprintf(stderr, "Unknown token profile: %s\n", g_profile.c_str());
184+
return 1;
185+
}
186+
scitoken_set_serialize_mode(token.get(), profile);
187+
}
188+
189+
char *value;
190+
rv = scitoken_serialize(token.get(), &value, &err_msg);
191+
if (rv) {
192+
fprintf(stderr, "Failed to serialize the token: %s\n", err_msg);
193+
free(err_msg);
194+
return 1;
195+
}
196+
197+
printf("%s\n", value);
198+
}

src/scitokens.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ int scitoken_set_claim_string(SciToken token, const char *key, const char *value
6767
}
6868

6969

70+
void scitoken_set_serialize_mode(SciToken token, SciTokenProfile profile) {
71+
scitokens::SciToken *real_token = reinterpret_cast<scitokens::SciToken*>(token);
72+
if (real_token == nullptr) {return;}
73+
74+
real_token->set_serialize_mode(static_cast<scitokens::SciToken::Profile>(profile));
75+
}
76+
77+
7078
int scitoken_get_claim_string(const SciToken token, const char *key, char **value, char **err_msg) {
7179
scitokens::SciToken *real_token = reinterpret_cast<scitokens::SciToken*>(token);
7280
std::string claim_str;
@@ -161,10 +169,39 @@ int scitoken_deserialize(const char *value, SciToken *token, char const* const*
161169
return 0;
162170
}
163171

172+
int scitoken_store_public_ec_key(const char *issuer, const char *keyid, const char *key, char **err_msg)
173+
{
174+
bool success;
175+
try {
176+
success = scitokens::Validator::store_public_ec_key(issuer, keyid, key);
177+
} catch (std::exception &exc) {
178+
if (err_msg) {
179+
*err_msg = strdup(exc.what());
180+
}
181+
return -1;
182+
}
183+
184+
return success ? 0 : -1;
185+
}
186+
164187
Validator validator_create() {
165188
return new Validator();
166189
}
167190

191+
192+
void validator_destroy(Validator validator) {
193+
scitokens::Validator *real_validator =
194+
reinterpret_cast<scitokens::Validator*>(validator);
195+
delete real_validator;
196+
}
197+
198+
199+
void validator_set_token_profile(Validator validator, SciTokenProfile profile) {
200+
if (validator == nullptr) {return;}
201+
auto real_validator = reinterpret_cast<scitokens::Validator*>(validator);
202+
real_validator->set_validate_profile(static_cast<scitokens::SciToken::Profile>(profile));
203+
}
204+
168205
int validator_add(Validator validator, const char *claim, StringValidatorFunction validator_func, char **err_msg) {
169206
if (validator == nullptr) {
170207
if (err_msg) {*err_msg = strdup("Validator may not be a null pointer");}
@@ -257,6 +294,14 @@ void enforcer_acl_free(Acl *acls) {
257294
}
258295

259296

297+
void enforcer_set_validate_profile(Enforcer enf, SciTokenProfile profile) {
298+
if (enf == nullptr) {return;}
299+
300+
auto real_enf = reinterpret_cast<scitokens::Enforcer*>(enf);
301+
real_enf->set_validate_profile(static_cast<scitokens::SciToken::Profile>(profile));
302+
}
303+
304+
260305
int enforcer_generate_acls(const Enforcer enf, const SciToken scitoken, Acl **acls, char **err_msg) {
261306
if (enf == nullptr) {
262307
if (err_msg) {*err_msg = strdup("Enforcer may not be a null pointer");}

src/scitokens.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,21 @@ typedef struct Acl_s {
2020
}
2121
Acl;
2222

23+
/**
24+
* Determine the mode we will use to validate tokens.
25+
* - COMPAT mode (default) indicates any supported token format
26+
* is acceptable. Where possible, the scope names are translated into
27+
* equivalent SciTokens 1.0 claim names (i.e., storage.read -> read; storage.write -> write).
28+
* - SCITOKENS_1_0, SCITOKENS_2_0, WLCG_1_0: only accept these specific profiles.
29+
* No automatic translation is performed.
30+
*/
31+
typedef enum _profile {
32+
COMPAT = 0,
33+
SCITOKENS_1_0,
34+
SCITOKENS_2_0,
35+
WLCG_1_0
36+
} SciTokenProfile;
37+
2338
SciTokenKey scitoken_key_create(const char *key_id, const char *algorithm, const char *public_contents, const char *private_contents, char **err_msg);
2439

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

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

56+
/**
57+
* Set the profile used for serialization; if COMPAT mode is used, then
58+
* the library default is utilized (currently, scitokens 1.0).
59+
*/
60+
void scitoken_set_serialize_mode(SciToken token, SciTokenProfile profile);
61+
4162
int scitoken_deserialize(const char *value, SciToken *token, char const* const* allowed_issuers, char **err_msg);
4263

64+
int scitoken_store_public_ec_key(const char *issuer, const char *keyid, const char *value, char **err_msg);
65+
4366
Validator validator_create();
4467

68+
/**
69+
* Set the profile used for validating the tokens; COMPAT (default) will accept any known token
70+
* type while others will only support that specific profile.
71+
*/
72+
void validator_set_token_profile(Validator, SciTokenProfile profile);
73+
4574
int validator_add(Validator validator, const char *claim, StringValidatorFunction validator_func, char **err_msg);
4675

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

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

80+
/**
81+
* Destroy a validator object.
82+
*/
83+
void validator_destroy(Validator);
84+
5185
Enforcer enforcer_create(const char *issuer, const char **audience, char **err_msg);
5286

5387
void enforcer_destroy(Enforcer);
5488

89+
/**
90+
* Set the profile used for enforcing ACLs; when set to COMPAT (default), then the authorizations
91+
* will be converted to SciTokens 1.0-style authorizations (so, WLCG's storage.read becomes read).
92+
*/
93+
void enforcer_set_validate_profile(Enforcer, SciTokenProfile profile);
94+
5595
int enforcer_generate_acls(const Enforcer enf, const SciToken scitokens, Acl **acls, char **err_msg);
5696

5797
void enforcer_acl_free(Acl *acls);

0 commit comments

Comments
 (0)