testing_api_cmd_instance_token.c (15739B)
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 src/testing/testing_api_cmd_instance_token.c 21 * @brief command to test /private/token POSTing 22 * @author Martin Schanzenbach 23 */ 24 #include "platform.h" 25 struct TokenInstanceState; 26 #define TALER_MERCHANT_DELETE_PRIVATE_TOKEN_RESULT_CLOSURE struct TokenInstanceState 27 #define TALER_MERCHANT_POST_PRIVATE_TOKEN_RESULT_CLOSURE struct TokenInstanceState 28 #include <taler/taler_exchange_service.h> 29 #include <taler/taler_testing_lib.h> 30 #include "taler/taler_merchant_service.h" 31 #include "taler/taler_merchant_testing_lib.h" 32 #include <taler/merchant/delete-private-tokens-SERIAL.h> 33 #include <taler/merchant/post-private-token.h> 34 35 36 /** 37 * State of a "POST /instances/$ID/private/token" CMD. 38 */ 39 struct TokenInstanceState 40 { 41 42 /** 43 * Handle for a "POST token" request. 44 */ 45 struct TALER_MERCHANT_PostPrivateTokenHandle *itph; 46 47 /** 48 * Handle for a "DELETE token" request. 49 */ 50 struct TALER_MERCHANT_DeletePrivateTokenHandle *itdh; 51 52 /** 53 * The interpreter state. 54 */ 55 struct TALER_TESTING_Interpreter *is; 56 57 /** 58 * Base URL of the merchant serving the request. 59 */ 60 const char *merchant_url; 61 62 /** 63 * ID of the instance to run GET for. 64 */ 65 const char *instance_id; 66 67 /** 68 * The received token (if any). 69 */ 70 char *token; 71 72 /** 73 * Desired scope. Can be NULL 74 */ 75 const char *scope; 76 77 /** 78 * Desired duration. 79 */ 80 struct GNUNET_TIME_Relative duration; 81 82 /** 83 * Refreshable? 84 */ 85 bool refreshable; 86 87 /** 88 * Expected HTTP response code. 89 */ 90 unsigned int http_status; 91 92 /** 93 * DELETE or POST. 94 */ 95 unsigned int is_delete; 96 97 }; 98 99 /** 100 * Callback for a POST /instances/$ID/private/token operation. 101 * 102 * @param cls closure for this function 103 * @param ptr response being processed 104 */ 105 static void 106 token_instance_post_cb (struct TokenInstanceState *tis, 107 const struct TALER_MERCHANT_PostPrivateTokenResponse * 108 ptr) 109 { 110 111 tis->itph = NULL; 112 if (tis->http_status != ptr->hr.http_status) 113 { 114 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 115 "Unexpected response code %u (%d) to command %s\n", 116 ptr->hr.http_status, 117 (int) ptr->hr.ec, 118 TALER_TESTING_interpreter_get_current_label (tis->is)); 119 TALER_TESTING_interpreter_fail (tis->is); 120 return; 121 } 122 switch (ptr->hr.http_status) 123 { 124 case MHD_HTTP_OK: 125 if (NULL != ptr->details.ok.access_token) 126 tis->token = GNUNET_strdup (ptr->details.ok.access_token); 127 break; 128 case MHD_HTTP_BAD_REQUEST: 129 /* likely invalid auth value, we do not check client-side */ 130 break; 131 case MHD_HTTP_FORBIDDEN: 132 break; 133 default: 134 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 135 "Unhandled HTTP status %u (%d) returned from /private/token operation.\n", 136 ptr->hr.http_status, 137 ptr->hr.ec); 138 } 139 TALER_TESTING_interpreter_next (tis->is); 140 } 141 142 143 /** 144 * Callback for a DELETE /private/tokens/$SERIAL operation. 145 * 146 * @param cls closure for this function 147 * @param dtr response being processed 148 */ 149 static void 150 token_instance_delete_cb ( 151 struct TokenInstanceState *tis, 152 const struct TALER_MERCHANT_DeletePrivateTokenResponse *dtr) 153 { 154 155 tis->itdh = NULL; 156 if (tis->http_status != dtr->hr.http_status) 157 { 158 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 159 "Unexpected response code %u (%d) to command %s\n", 160 dtr->hr.http_status, 161 (int) dtr->hr.ec, 162 TALER_TESTING_interpreter_get_current_label (tis->is)); 163 TALER_TESTING_interpreter_fail (tis->is); 164 return; 165 } 166 switch (dtr->hr.http_status) 167 { 168 case MHD_HTTP_NO_CONTENT: 169 break; 170 case MHD_HTTP_FORBIDDEN: 171 break; 172 default: 173 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 174 "Unhandled HTTP status %u (%d) returned from DELETE /private/tokens operation.\n", 175 dtr->hr.http_status, 176 dtr->hr.ec); 177 } 178 TALER_TESTING_interpreter_next (tis->is); 179 } 180 181 182 /** 183 * set a token 184 * 185 * 186 * @param cls closure. 187 * @param cmd command being run now. 188 * @param is interpreter state. 189 */ 190 static void 191 set_token_instance_run (void *cls, 192 const struct TALER_TESTING_Command *cmd, 193 struct TALER_TESTING_Interpreter *is) 194 { 195 const char *token_job_label = cls; 196 const char *token; 197 const struct TALER_TESTING_Command *tok_cmd; 198 struct GNUNET_CURL_Context *cctx; 199 char *authorization; 200 201 cctx = TALER_TESTING_interpreter_get_context (is); 202 GNUNET_assert (NULL != cctx); 203 tok_cmd = TALER_TESTING_interpreter_lookup_command ( 204 is, 205 token_job_label); 206 if (NULL == tok_cmd) 207 { 208 GNUNET_break (0); 209 TALER_TESTING_interpreter_fail (is); 210 return; 211 } 212 if (GNUNET_OK != 213 TALER_TESTING_get_trait_bearer_token (tok_cmd, 214 &token)) 215 { 216 GNUNET_break (0); 217 TALER_TESTING_interpreter_fail (is); 218 return; 219 } 220 GNUNET_assert (NULL != token); 221 222 GNUNET_asprintf (&authorization, 223 "%s: Bearer %s", 224 MHD_HTTP_HEADER_AUTHORIZATION, 225 token); 226 GNUNET_assert (GNUNET_OK == 227 GNUNET_CURL_append_header (cctx, 228 authorization)); 229 GNUNET_free (authorization); 230 TALER_TESTING_interpreter_next (is); 231 } 232 233 234 /** 235 * Run the "token /instances/$ID" CMD. 236 * 237 * @param cls closure. 238 * @param cmd command being run now. 239 * @param is interpreter state. 240 */ 241 static void 242 token_instance_run (void *cls, 243 const struct TALER_TESTING_Command *cmd, 244 struct TALER_TESTING_Interpreter *is) 245 { 246 struct TokenInstanceState *tis = cls; 247 248 tis->is = is; 249 if (GNUNET_NO == tis->is_delete) 250 { 251 tis->itph = TALER_MERCHANT_post_private_token_create ( 252 TALER_TESTING_interpreter_get_context (is), 253 tis->merchant_url, 254 tis->instance_id, 255 tis->scope); 256 GNUNET_assert (GNUNET_OK == 257 TALER_MERCHANT_post_private_token_set_options ( 258 tis->itph, 259 TALER_MERCHANT_post_private_token_option_duration ( 260 tis->duration), 261 TALER_MERCHANT_post_private_token_option_refreshable ( 262 tis->refreshable))); 263 { 264 enum TALER_ErrorCode ec; 265 266 ec = TALER_MERCHANT_post_private_token_start ( 267 tis->itph, 268 &token_instance_post_cb, 269 tis); 270 GNUNET_assert (TALER_EC_NONE == ec); 271 } 272 } 273 else 274 { 275 tis->itdh = TALER_MERCHANT_delete_private_token_create ( 276 TALER_TESTING_interpreter_get_context (is), 277 tis->merchant_url, 278 tis->instance_id); 279 { 280 enum TALER_ErrorCode ec; 281 282 ec = TALER_MERCHANT_delete_private_token_start ( 283 tis->itdh, 284 &token_instance_delete_cb, 285 tis); 286 GNUNET_assert (TALER_EC_NONE == ec); 287 } 288 } 289 } 290 291 292 /** 293 * Free the state of a "POST instance token" CMD, and possibly 294 * cancel a pending operation thereof. 295 * 296 * @param cls closure. 297 * @param cmd command being run. 298 */ 299 static void 300 token_instance_cleanup (void *cls, 301 const struct TALER_TESTING_Command *cmd) 302 { 303 struct TokenInstanceState *tis = cls; 304 305 if (NULL != tis->itph) 306 { 307 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 308 "POST /instance/$ID/token operation did not complete\n"); 309 TALER_MERCHANT_post_private_token_cancel (tis->itph); 310 } 311 if (NULL != tis->itdh) 312 { 313 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 314 "DELETE /instance/$ID/token operation did not complete\n"); 315 TALER_MERCHANT_delete_private_token_cancel (tis->itdh); 316 } 317 GNUNET_free (tis->token); 318 GNUNET_free (tis); 319 } 320 321 322 /** 323 * Offer internal data to other commands. 324 * 325 * @param cls closure 326 * @param[out] ret result (could be anything) 327 * @param trait name of the trait 328 * @param index index number of the object to extract. 329 * @return #GNUNET_OK on success 330 */ 331 static enum GNUNET_GenericReturnValue 332 token_instance_traits (void *cls, 333 const void **ret, 334 const char *trait, 335 unsigned int index) 336 { 337 struct TokenInstanceState *ais = cls; 338 struct TALER_TESTING_Trait traits[] = { 339 TALER_TESTING_make_trait_bearer_token (ais->token), 340 TALER_TESTING_trait_end () 341 }; 342 343 return TALER_TESTING_get_trait (traits, 344 ret, 345 trait, 346 index); 347 } 348 349 350 struct TALER_TESTING_Command 351 TALER_TESTING_cmd_merchant_delete_instance_token (const char *label, 352 const char *merchant_url, 353 const char *instance_id, 354 unsigned int http_status) 355 { 356 struct TokenInstanceState *tis; 357 358 tis = GNUNET_new (struct TokenInstanceState); 359 tis->merchant_url = merchant_url; 360 tis->instance_id = instance_id; 361 tis->is_delete = GNUNET_YES; 362 tis->http_status = http_status; 363 364 { 365 struct TALER_TESTING_Command cmd = { 366 .cls = tis, 367 .label = label, 368 .run = &token_instance_run, 369 .cleanup = &token_instance_cleanup, 370 .traits = &token_instance_traits 371 }; 372 373 return cmd; 374 } 375 } 376 377 378 struct TALER_TESTING_Command 379 TALER_TESTING_cmd_merchant_set_instance_token (const char *label, 380 const char *token_job_label) 381 { 382 { 383 struct TALER_TESTING_Command cmd = { 384 .cls = (void*) token_job_label, // FIXME scope 385 .label = label, 386 .run = &set_token_instance_run, 387 .cleanup = NULL, 388 .traits = NULL 389 }; 390 391 return cmd; 392 } 393 } 394 395 396 struct TALER_TESTING_Command 397 TALER_TESTING_cmd_merchant_post_instance_token (const char *label, 398 const char *merchant_url, 399 const char *instance_id, 400 const char *scope, 401 struct GNUNET_TIME_Relative 402 duration, 403 bool refreshable, 404 unsigned int http_status) 405 { 406 struct TokenInstanceState *tis; 407 408 tis = GNUNET_new (struct TokenInstanceState); 409 tis->merchant_url = merchant_url; 410 tis->instance_id = instance_id; 411 tis->scope = scope; 412 tis->duration = duration; 413 tis->refreshable = refreshable; 414 tis->is_delete = GNUNET_NO; 415 tis->http_status = http_status; 416 417 { 418 struct TALER_TESTING_Command cmd = { 419 .cls = tis, 420 .label = label, 421 .run = &token_instance_run, 422 .cleanup = &token_instance_cleanup, 423 .traits = &token_instance_traits 424 }; 425 426 return cmd; 427 } 428 } 429 430 431 /** 432 * State of a login batch, owned by its last command. 433 */ 434 struct LoginState 435 { 436 /** 437 * "Basic" authorization header value with the instance password. 438 */ 439 char *basic_auth; 440 441 /** 442 * Label of the command that sets @e basic_auth. 443 */ 444 char *set_basic_label; 445 446 /** 447 * Label of the command that requests the login token. 448 */ 449 char *token_label; 450 451 /** 452 * Label of the command that drops the Authorization header. 453 */ 454 char *unset_label; 455 456 /** 457 * Label of the command that installs the login token. 458 */ 459 char *set_token_label; 460 461 /** 462 * State of the command that requests the login token. Referenced 463 * directly: commands in nested batches cannot be found by label. 464 */ 465 const struct TokenInstanceState *tis; 466 }; 467 468 469 /** 470 * Send the login token with all further requests. 471 * 472 * @param cls a `struct LoginState` 473 * @param cmd command being run now 474 * @param is interpreter state 475 */ 476 static void 477 login_set_token_run (void *cls, 478 const struct TALER_TESTING_Command *cmd, 479 struct TALER_TESTING_Interpreter *is) 480 { 481 const struct LoginState *ls = cls; 482 struct GNUNET_CURL_Context *cctx; 483 char *authorization; 484 485 (void) cmd; 486 if (NULL == ls->tis->token) 487 { 488 GNUNET_break (0); 489 TALER_TESTING_interpreter_fail (is); 490 return; 491 } 492 cctx = TALER_TESTING_interpreter_get_context (is); 493 GNUNET_assert (NULL != cctx); 494 GNUNET_asprintf (&authorization, 495 "%s: Bearer %s", 496 MHD_HTTP_HEADER_AUTHORIZATION, 497 ls->tis->token); 498 GNUNET_assert (GNUNET_OK == 499 GNUNET_CURL_append_header (cctx, 500 authorization)); 501 GNUNET_free (authorization); 502 TALER_TESTING_interpreter_next (is); 503 } 504 505 506 /** 507 * Free the state of a login batch. 508 * 509 * @param cls a `struct LoginState` 510 * @param cmd command being cleaned up 511 */ 512 static void 513 login_set_token_cleanup (void *cls, 514 const struct TALER_TESTING_Command *cmd) 515 { 516 struct LoginState *ls = cls; 517 518 (void) cmd; 519 GNUNET_free (ls->basic_auth); 520 GNUNET_free (ls->set_basic_label); 521 GNUNET_free (ls->token_label); 522 GNUNET_free (ls->unset_label); 523 GNUNET_free (ls->set_token_label); 524 GNUNET_free (ls); 525 } 526 527 528 struct TALER_TESTING_Command 529 TALER_TESTING_cmd_merchant_login (const char *label, 530 const char *merchant_url, 531 const char *instance_id, 532 const char *password) 533 { 534 struct LoginState *ls; 535 struct TALER_TESTING_Command token_cmd; 536 537 ls = GNUNET_new (struct LoginState); 538 { 539 char *userpass; 540 char *b64; 541 542 GNUNET_asprintf (&userpass, 543 "%s:%s", 544 instance_id, 545 password); 546 GNUNET_STRINGS_base64_encode (userpass, 547 strlen (userpass), 548 &b64); 549 GNUNET_asprintf (&ls->basic_auth, 550 "Basic %s", 551 b64); 552 GNUNET_free (b64); 553 GNUNET_free (userpass); 554 } 555 GNUNET_asprintf (&ls->set_basic_label, 556 "%s-set-basic-auth", 557 label); 558 GNUNET_asprintf (&ls->token_label, 559 "%s-token", 560 label); 561 GNUNET_asprintf (&ls->unset_label, 562 "%s-unset-basic-auth", 563 label); 564 GNUNET_asprintf (&ls->set_token_label, 565 "%s-set-token", 566 label); 567 token_cmd = TALER_TESTING_cmd_merchant_post_instance_token ( 568 ls->token_label, 569 merchant_url, 570 instance_id, 571 "all", 572 GNUNET_TIME_UNIT_DAYS, 573 false, 574 MHD_HTTP_OK); 575 ls->tis = token_cmd.cls; 576 { 577 struct TALER_TESTING_Command batch[] = { 578 TALER_TESTING_cmd_set_authorization (ls->set_basic_label, 579 ls->basic_auth), 580 token_cmd, 581 TALER_TESTING_cmd_set_authorization (ls->unset_label, 582 NULL), 583 { 584 .cls = ls, 585 .label = ls->set_token_label, 586 .run = &login_set_token_run, 587 .cleanup = &login_set_token_cleanup 588 }, 589 TALER_TESTING_cmd_end () 590 }; 591 592 return TALER_TESTING_cmd_batch (label, 593 batch); 594 } 595 } 596 597 598 /* end of testing_api_cmd_token_instance.c */