testing_api_cmd_instance_token.c (10350B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2025 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as 7 published by the Free Software Foundation; either version 3, or 8 (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, see 17 <http://www.gnu.org/licenses/> 18 */ 19 /** 20 * @file testing_api_cmd_instance_token.c 21 * @brief command to test /private/token POSTing 22 * @author Martin Schanzenbach 23 */ 24 #include "platform.h" 25 #include <taler/taler_exchange_service.h> 26 #include <taler/taler_testing_lib.h> 27 #include "taler_merchant_service.h" 28 #include "taler_merchant_testing_lib.h" 29 30 31 /** 32 * State of a "POST /instances/$ID/private/token" CMD. 33 */ 34 struct TokenInstanceState 35 { 36 37 /** 38 * Handle for a "POST token" request. 39 */ 40 struct TALER_MERCHANT_InstanceTokenPostHandle *itph; 41 42 /** 43 * Handle for a "DELETE token" request. 44 */ 45 struct TALER_MERCHANT_InstanceTokenDeleteHandle *itdh; 46 47 /** 48 * The interpreter state. 49 */ 50 struct TALER_TESTING_Interpreter *is; 51 52 /** 53 * Base URL of the merchant serving the request. 54 */ 55 const char *merchant_url; 56 57 /** 58 * ID of the instance to run GET for. 59 */ 60 const char *instance_id; 61 62 /** 63 * The received token (if any). 64 */ 65 char *token; 66 67 /** 68 * Desired scope. Can be NULL 69 */ 70 const char *scope; 71 72 /** 73 * Desired duration. 74 */ 75 struct GNUNET_TIME_Relative duration; 76 77 /** 78 * Refreshable? 79 */ 80 bool refreshable; 81 82 /** 83 * Expected HTTP response code. 84 */ 85 unsigned int http_status; 86 87 /** 88 * DELETE or POST. 89 */ 90 unsigned int is_delete; 91 92 }; 93 94 /** 95 * Callback for a POST /instances/$ID/private/token operation. 96 * 97 * @param cls closure for this function 98 * @param hr response being processed 99 */ 100 static void 101 token_instance_cb (void *cls, 102 const struct TALER_MERCHANT_HttpResponse *hr) 103 { 104 struct TokenInstanceState *tis = cls; 105 const char *scope; 106 struct GNUNET_TIME_Timestamp duration; 107 bool refreshable; 108 const char *error_name; 109 unsigned int error_line; 110 111 112 tis->itph = NULL; 113 if (tis->http_status != hr->http_status) 114 { 115 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 116 "Unexpected response code %u (%d) to command %s\n", 117 hr->http_status, 118 (int) hr->ec, 119 TALER_TESTING_interpreter_get_current_label (tis->is)); 120 TALER_TESTING_interpreter_fail (tis->is); 121 return; 122 } 123 switch (hr->http_status) 124 { 125 case MHD_HTTP_NO_CONTENT: 126 GNUNET_assert (GNUNET_YES == tis->is_delete); 127 break; 128 case MHD_HTTP_OK: 129 { 130 /* Get token */ 131 struct GNUNET_JSON_Specification spec[] = { 132 GNUNET_JSON_spec_string_copy ("access_token", 133 &tis->token), 134 GNUNET_JSON_spec_string ("scope", 135 &scope), 136 GNUNET_JSON_spec_bool ("refreshable", 137 &refreshable), 138 GNUNET_JSON_spec_timestamp ("expiration", 139 &duration), 140 GNUNET_JSON_spec_end () 141 }; 142 143 GNUNET_assert (GNUNET_NO == tis->is_delete); 144 if (GNUNET_OK != 145 GNUNET_JSON_parse (hr->reply, 146 spec, 147 &error_name, 148 &error_line)) 149 { 150 char *js; 151 152 js = json_dumps (hr->reply, 153 JSON_INDENT (1)); 154 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 155 "Parser failed on %s:%u for input `%s'\n", 156 error_name, 157 error_line, 158 js); 159 free (js); 160 TALER_TESTING_FAIL (tis->is); 161 } 162 break; 163 } 164 case MHD_HTTP_BAD_REQUEST: 165 /* likely invalid auth value, we do not check client-side */ 166 break; 167 case MHD_HTTP_FORBIDDEN: 168 break; 169 default: 170 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 171 "Unhandled HTTP status %u (%d) returned from /private/token operation.\n", 172 hr->http_status, 173 hr->ec); 174 } 175 176 177 TALER_TESTING_interpreter_next (tis->is); 178 } 179 180 181 /** 182 * set a token 183 * 184 * 185 * @param cls closure. 186 * @param cmd command being run now. 187 * @param is interpreter state. 188 */ 189 static void 190 set_token_instance_run (void *cls, 191 const struct TALER_TESTING_Command *cmd, 192 struct TALER_TESTING_Interpreter *is) 193 { 194 const char *token_job_label = cls; 195 const char *token; 196 const struct TALER_TESTING_Command *tok_cmd; 197 struct GNUNET_CURL_Context *cctx; 198 char *authorization; 199 200 cctx = TALER_TESTING_interpreter_get_context (is); 201 GNUNET_assert (NULL != cctx); 202 tok_cmd = TALER_TESTING_interpreter_lookup_command ( 203 is, 204 token_job_label); 205 TALER_TESTING_get_trait_bearer_token (tok_cmd, 206 &token); 207 GNUNET_assert (NULL != token); 208 209 GNUNET_asprintf (&authorization, 210 "%s: Bearer %s", 211 MHD_HTTP_HEADER_AUTHORIZATION, 212 token); 213 GNUNET_assert (GNUNET_OK == 214 GNUNET_CURL_append_header (cctx, 215 authorization)); 216 GNUNET_free (authorization); 217 TALER_TESTING_interpreter_next (is); 218 } 219 220 221 /** 222 * Run the "token /instances/$ID" CMD. 223 * 224 * 225 * @param cls closure. 226 * @param cmd command being run now. 227 * @param is interpreter state. 228 */ 229 static void 230 token_instance_run (void *cls, 231 const struct TALER_TESTING_Command *cmd, 232 struct TALER_TESTING_Interpreter *is) 233 { 234 struct TokenInstanceState *tis = cls; 235 236 tis->is = is; 237 if (GNUNET_NO == tis->is_delete) 238 tis->itph = TALER_MERCHANT_instance_token_post ( 239 TALER_TESTING_interpreter_get_context (is), 240 tis->merchant_url, 241 tis->instance_id, 242 tis->scope, 243 tis->duration, 244 tis->refreshable, 245 &token_instance_cb, 246 tis); 247 else 248 tis->itdh = TALER_MERCHANT_instance_token_delete ( 249 TALER_TESTING_interpreter_get_context (is), 250 tis->merchant_url, 251 tis->instance_id, 252 &token_instance_cb, 253 tis); 254 GNUNET_assert ((NULL != tis->itph) || (NULL != tis->itdh)); 255 } 256 257 258 /** 259 * Free the state of a "POST instance token" CMD, and possibly 260 * cancel a pending operation thereof. 261 * 262 * @param cls closure. 263 * @param cmd command being run. 264 */ 265 static void 266 token_instance_cleanup (void *cls, 267 const struct TALER_TESTING_Command *cmd) 268 { 269 struct TokenInstanceState *tis = cls; 270 271 if (NULL != tis->itph) 272 { 273 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 274 "%s /instance/$ID/token operation did not complete\n", 275 (GNUNET_NO == tis->is_delete) ? "DELETE" : "POST"); 276 if (GNUNET_NO == tis->is_delete) 277 TALER_MERCHANT_instance_token_post_cancel (tis->itph); 278 else 279 TALER_MERCHANT_instance_token_delete_cancel (tis->itdh); 280 } 281 GNUNET_free (tis); 282 } 283 284 285 /** 286 * Offer internal data to other commands. 287 * 288 * @param cls closure 289 * @param[out] ret result (could be anything) 290 * @param trait name of the trait 291 * @param index index number of the object to extract. 292 * @return #GNUNET_OK on success 293 */ 294 static enum GNUNET_GenericReturnValue 295 token_instance_traits (void *cls, 296 const void **ret, 297 const char *trait, 298 unsigned int index) 299 { 300 struct TokenInstanceState *ais = cls; 301 struct TALER_TESTING_Trait traits[] = { 302 TALER_TESTING_make_trait_bearer_token (ais->token), 303 TALER_TESTING_trait_end () 304 }; 305 306 return TALER_TESTING_get_trait (traits, 307 ret, 308 trait, 309 index); 310 } 311 312 313 struct TALER_TESTING_Command 314 TALER_TESTING_cmd_merchant_delete_instance_token (const char *label, 315 const char *merchant_url, 316 const char *instance_id, 317 unsigned int http_status) 318 { 319 struct TokenInstanceState *tis; 320 321 tis = GNUNET_new (struct TokenInstanceState); 322 tis->merchant_url = merchant_url; 323 tis->instance_id = instance_id; 324 tis->is_delete = GNUNET_YES; 325 tis->http_status = http_status; 326 327 { 328 struct TALER_TESTING_Command cmd = { 329 .cls = tis, 330 .label = label, 331 .run = &token_instance_run, 332 .cleanup = &token_instance_cleanup, 333 .traits = &token_instance_traits 334 }; 335 336 return cmd; 337 } 338 } 339 340 341 struct TALER_TESTING_Command 342 TALER_TESTING_cmd_merchant_set_instance_token (const char *label, 343 const char *token_job_label) 344 { 345 { 346 struct TALER_TESTING_Command cmd = { 347 .cls = (void*) token_job_label, // FIXME scope 348 .label = label, 349 .run = &set_token_instance_run, 350 .cleanup = NULL, 351 .traits = NULL 352 }; 353 354 return cmd; 355 } 356 } 357 358 359 struct TALER_TESTING_Command 360 TALER_TESTING_cmd_merchant_post_instance_token (const char *label, 361 const char *merchant_url, 362 const char *instance_id, 363 const char *scope, 364 struct GNUNET_TIME_Relative 365 duration, 366 bool refreshable, 367 unsigned int http_status) 368 { 369 struct TokenInstanceState *tis; 370 371 tis = GNUNET_new (struct TokenInstanceState); 372 tis->merchant_url = merchant_url; 373 tis->instance_id = instance_id; 374 tis->scope = scope; 375 tis->duration = duration; 376 tis->refreshable = refreshable; 377 tis->is_delete = GNUNET_NO; 378 tis->http_status = http_status; 379 380 { 381 struct TALER_TESTING_Command cmd = { 382 .cls = tis, 383 .label = label, 384 .run = &token_instance_run, 385 .cleanup = &token_instance_cleanup, 386 .traits = &token_instance_traits 387 }; 388 389 return cmd; 390 } 391 } 392 393 394 /* end of testing_api_cmd_token_instance.c */