1+ #include < cstdlib>
2+ #include < cstring>
3+ #include < ctime>
4+ #include < iostream>
5+ #include < fstream>
6+ #include < string>
7+ #include < unistd.h>
8+
9+ #include " scitokens.h"
10+
11+ void print_usage (const char *progname) {
12+ std::cout << " Usage: " << progname << " --cache-file <cache_file> --jwks <jwks_file> --issuer <issuer> --valid-for <seconds>\n " ;
13+ std::cout << " \n " ;
14+ std::cout << " Options:\n " ;
15+ std::cout << " --cache-file <file> Path to the keycache SQLite database file\n " ;
16+ std::cout << " --jwks <file> Path to the JWKS file to store\n " ;
17+ std::cout << " --issuer <issuer> Issuer URL for the JWKS\n " ;
18+ std::cout << " --valid-for <seconds> How long the key should be valid (in seconds)\n " ;
19+ std::cout << " --help Show this help message\n " ;
20+ std::cout << " \n " ;
21+ std::cout << " Example:\n " ;
22+ std::cout << " " << progname << " --cache-file /tmp/offline.db --jwks keys.json --issuer https://example.com --valid-for 86400\n " ;
23+ }
24+
25+ std::string read_file (const std::string &filename) {
26+ std::ifstream file (filename);
27+ if (!file.is_open ()) {
28+ throw std::runtime_error (" Cannot open file: " + filename);
29+ }
30+
31+ std::string content ((std::istreambuf_iterator<char >(file)),
32+ std::istreambuf_iterator<char >());
33+ return content;
34+ }
35+
36+ int main (int argc, char *argv[]) {
37+ std::string cache_file;
38+ std::string jwks_file;
39+ std::string issuer;
40+ long valid_for = 0 ;
41+
42+ // Parse command line arguments
43+ for (int i = 1 ; i < argc; i++) {
44+ if (strcmp (argv[i], " --help" ) == 0 ) {
45+ print_usage (argv[0 ]);
46+ return 0 ;
47+ } else if (strcmp (argv[i], " --cache-file" ) == 0 ) {
48+ if (i + 1 >= argc) {
49+ std::cerr << " Error: --cache-file requires an argument\n " ;
50+ return 1 ;
51+ }
52+ cache_file = argv[++i];
53+ } else if (strcmp (argv[i], " --jwks" ) == 0 ) {
54+ if (i + 1 >= argc) {
55+ std::cerr << " Error: --jwks requires an argument\n " ;
56+ return 1 ;
57+ }
58+ jwks_file = argv[++i];
59+ } else if (strcmp (argv[i], " --issuer" ) == 0 ) {
60+ if (i + 1 >= argc) {
61+ std::cerr << " Error: --issuer requires an argument\n " ;
62+ return 1 ;
63+ }
64+ issuer = argv[++i];
65+ } else if (strcmp (argv[i], " --valid-for" ) == 0 ) {
66+ if (i + 1 >= argc) {
67+ std::cerr << " Error: --valid-for requires an argument\n " ;
68+ return 1 ;
69+ }
70+ char *endptr;
71+ valid_for = strtol (argv[++i], &endptr, 10 );
72+ if (*endptr != ' \0 ' || valid_for <= 0 ) {
73+ std::cerr << " Error: --valid-for must be a positive integer\n " ;
74+ return 1 ;
75+ }
76+ } else {
77+ std::cerr << " Error: Unknown option " << argv[i] << " \n " ;
78+ print_usage (argv[0 ]);
79+ return 1 ;
80+ }
81+ }
82+
83+ // Validate required arguments
84+ if (cache_file.empty ()) {
85+ std::cerr << " Error: --cache-file is required\n " ;
86+ print_usage (argv[0 ]);
87+ return 1 ;
88+ }
89+ if (jwks_file.empty ()) {
90+ std::cerr << " Error: --jwks is required\n " ;
91+ print_usage (argv[0 ]);
92+ return 1 ;
93+ }
94+ if (issuer.empty ()) {
95+ std::cerr << " Error: --issuer is required\n " ;
96+ print_usage (argv[0 ]);
97+ return 1 ;
98+ }
99+ if (valid_for == 0 ) {
100+ std::cerr << " Error: --valid-for is required\n " ;
101+ print_usage (argv[0 ]);
102+ return 1 ;
103+ }
104+
105+ try {
106+ // Set the cache file environment variable
107+ if (setenv (" SCITOKENS_KEYCACHE_FILE" , cache_file.c_str (), 1 ) != 0 ) {
108+ std::cerr << " Error: Failed to set SCITOKENS_KEYCACHE_FILE environment variable\n " ;
109+ return 1 ;
110+ }
111+
112+ // Read the JWKS file
113+ std::string jwks_content = read_file (jwks_file);
114+
115+ // Calculate expiration time
116+ time_t now = time (nullptr );
117+ int64_t expires_at = static_cast <int64_t >(now) + valid_for;
118+
119+ // Store the JWKS with expiration
120+ char *err_msg = nullptr ;
121+ int result = keycache_set_jwks_with_expiry (issuer.c_str (), jwks_content.c_str (), expires_at, &err_msg);
122+
123+ if (result != 0 ) {
124+ std::cerr << " Error: Failed to store JWKS: " << (err_msg ? err_msg : " Unknown error" ) << " \n " ;
125+ if (err_msg) {
126+ free (err_msg);
127+ }
128+ return 1 ;
129+ }
130+
131+ std::cout << " Successfully stored JWKS for issuer: " << issuer << " \n " ;
132+ std::cout << " Cache file: " << cache_file << " \n " ;
133+ std::cout << " Expires at: " << ctime (&now) << " + " << valid_for << " seconds\n " ;
134+
135+ if (err_msg) {
136+ free (err_msg);
137+ }
138+
139+ } catch (const std::exception &e) {
140+ std::cerr << " Error: " << e.what () << " \n " ;
141+ return 1 ;
142+ }
143+
144+ return 0 ;
145+ }
0 commit comments