testing_api_cmd_take_aml_decision.c (15429B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023, 2024, 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it 6 under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3, or (at your 8 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 GNU 13 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/testing_api_cmd_take_aml_decision.c 21 * @brief command for testing /aml/$OFFICER_PUB/decision 22 * @author Christian Grothoff 23 */ 24 #include "taler/taler_json_lib.h" 25 #include <gnunet/gnunet_curl_lib.h> 26 27 /** 28 * State for a "take_aml_decision" CMD. 29 */ 30 struct AmlDecisionState; 31 32 #define TALER_EXCHANGE_POST_AML_DECISION_RESULT_CLOSURE \ 33 struct AmlDecisionState 34 #include "taler/exchange/post-aml-OFFICER_PUB-decision.h" 35 #include "taler/taler_testing_lib.h" 36 37 38 /** 39 * State for a "take_aml_decision" CMD. 40 */ 41 struct AmlDecisionState 42 { 43 44 /** 45 * Handle while operation is running. 46 */ 47 struct TALER_EXCHANGE_PostAmlDecisionHandle *dh; 48 49 /** 50 * Our interpreter. 51 */ 52 struct TALER_TESTING_Interpreter *is; 53 54 /** 55 * Reference to command to previous set officer command that gives 56 * us an officer_priv trait. 57 */ 58 const char *officer_ref_cmd; 59 60 /** 61 * Reference to command to previous AML-triggering event that gives 62 * us a payto-hash trait. 63 */ 64 const char *account_ref_cmd; 65 66 /** 67 * Payto hash of the account we are manipulating the AML settings for. 68 */ 69 struct TALER_NormalizedPaytoHashP h_payto; 70 71 /** 72 * Justification given. 73 */ 74 const char *justification; 75 76 /** 77 * Delay to apply to compute the expiration time 78 * for the rules. 79 */ 80 struct GNUNET_TIME_Relative expiration_delay; 81 82 /** 83 * Successor measure to activate upon expiration. 84 */ 85 const char *successor_measure; 86 87 /** 88 * True to keep AML investigation open. 89 */ 90 bool keep_investigating; 91 92 /** 93 * New rules to enforce. 94 */ 95 json_t *new_rules; 96 97 /** 98 * Account properties to set. 99 */ 100 json_t *properties; 101 102 /** 103 * Expected response code. 104 */ 105 unsigned int expected_response; 106 }; 107 108 109 /** 110 * Callback to analyze the /aml-decision/$OFFICER_PUB response, just used to 111 * check if the response code is acceptable. 112 * 113 * @param ds our state 114 * @param pr response details 115 */ 116 static void 117 take_aml_decision_cb ( 118 struct AmlDecisionState *ds, 119 const struct TALER_EXCHANGE_PostAmlDecisionResponse *pr) 120 { 121 const struct TALER_EXCHANGE_HttpResponse *hr = &pr->hr; 122 123 ds->dh = NULL; 124 if (ds->expected_response != hr->http_status) 125 { 126 TALER_TESTING_unexpected_status (ds->is, 127 hr->http_status, 128 ds->expected_response); 129 return; 130 } 131 TALER_TESTING_interpreter_next (ds->is); 132 } 133 134 135 /** 136 * Run the command. 137 * 138 * @param cls closure. 139 * @param cmd the command to execute. 140 * @param is the interpreter state. 141 */ 142 static void 143 take_aml_decision_run (void *cls, 144 const struct TALER_TESTING_Command *cmd, 145 struct TALER_TESTING_Interpreter *is) 146 { 147 struct AmlDecisionState *ds = cls; 148 struct GNUNET_TIME_Timestamp now; 149 const struct TALER_NormalizedPaytoHashP *h_payto; 150 const struct TALER_AmlOfficerPrivateKeyP *officer_priv; 151 const struct TALER_TESTING_Command *ref; 152 const char *exchange_url; 153 const json_t *jrules; 154 const json_t *jmeasures = NULL; 155 struct GNUNET_TIME_Timestamp expiration_time 156 = GNUNET_TIME_relative_to_timestamp (ds->expiration_delay); 157 const char *new_measures = NULL; 158 struct GNUNET_JSON_Specification spec[] = { 159 GNUNET_JSON_spec_array_const ("rules", 160 &jrules), 161 GNUNET_JSON_spec_mark_optional ( 162 GNUNET_JSON_spec_object_const ("custom_measures", 163 &jmeasures), 164 NULL), 165 GNUNET_JSON_spec_mark_optional ( 166 GNUNET_JSON_spec_string ("new_measures", 167 &new_measures), 168 NULL), 169 GNUNET_JSON_spec_end () 170 }; 171 unsigned int num_rules; 172 unsigned int num_measures; 173 174 (void) cmd; 175 if (GNUNET_OK != 176 GNUNET_JSON_parse (ds->new_rules, 177 spec, 178 NULL, NULL)) 179 { 180 GNUNET_break_op (0); 181 TALER_TESTING_interpreter_fail (is); 182 return; 183 } 184 185 { 186 const struct TALER_TESTING_Command *exchange_cmd; 187 188 exchange_cmd = TALER_TESTING_interpreter_get_command (is, 189 "exchange"); 190 if (NULL == exchange_cmd) 191 { 192 GNUNET_break (0); 193 TALER_TESTING_interpreter_fail (is); 194 return; 195 } 196 GNUNET_assert (GNUNET_OK == 197 TALER_TESTING_get_trait_exchange_url (exchange_cmd, 198 &exchange_url)); 199 } 200 now = GNUNET_TIME_timestamp_get (); 201 ds->is = is; 202 ref = TALER_TESTING_interpreter_lookup_command (is, 203 ds->account_ref_cmd); 204 if (NULL == ref) 205 { 206 GNUNET_break (0); 207 TALER_TESTING_interpreter_fail (is); 208 return; 209 } 210 if (GNUNET_OK != 211 TALER_TESTING_get_trait_h_normalized_payto (ref, 212 &h_payto)) 213 { 214 GNUNET_break (0); 215 TALER_TESTING_interpreter_fail (is); 216 return; 217 } 218 ref = TALER_TESTING_interpreter_lookup_command (is, 219 ds->officer_ref_cmd); 220 if (NULL == ref) 221 { 222 GNUNET_break (0); 223 TALER_TESTING_interpreter_fail (is); 224 return; 225 } 226 if (GNUNET_OK != 227 TALER_TESTING_get_trait_officer_priv (ref, 228 &officer_priv)) 229 { 230 GNUNET_break (0); 231 TALER_TESTING_interpreter_fail (is); 232 return; 233 } 234 ds->h_payto = *h_payto; 235 236 num_rules = (unsigned int) json_array_size (jrules); 237 num_measures = (unsigned int) json_object_size (jmeasures); 238 { 239 struct TALER_EXCHANGE_AccountRule rules[ 240 GNUNET_NZL (num_rules)]; 241 struct TALER_EXCHANGE_MeasureInformation measures[ 242 GNUNET_NZL (num_measures)]; 243 const json_t *jrule; 244 size_t i; 245 const json_t *jmeasure; 246 const char *mname; 247 unsigned int off; 248 249 memset (rules, 250 0, 251 sizeof (rules)); 252 memset (measures, 253 0, 254 sizeof (measures)); 255 json_array_foreach ((json_t *) jrules, i, jrule) 256 { 257 struct TALER_EXCHANGE_AccountRule *rule = &rules[i]; 258 const json_t *jameasures = NULL; 259 struct GNUNET_JSON_Specification ispec[] = { 260 GNUNET_JSON_spec_relative_time ("timeframe", 261 &rule->timeframe), 262 TALER_JSON_spec_amount_any ("threshold", 263 &rule->threshold), 264 GNUNET_JSON_spec_mark_optional ( 265 GNUNET_JSON_spec_array_const ("measures", 266 &jameasures), 267 NULL), 268 GNUNET_JSON_spec_mark_optional ( 269 GNUNET_JSON_spec_uint32 ("display_priority", 270 &rule->display_priority), 271 NULL), 272 TALER_JSON_spec_kycte ("operation_type", 273 &rule->operation_type), 274 GNUNET_JSON_spec_mark_optional ( 275 GNUNET_JSON_spec_bool ("verboten", 276 &rule->verboten), 277 NULL), 278 GNUNET_JSON_spec_mark_optional ( 279 GNUNET_JSON_spec_bool ("exposed", 280 &rule->exposed), 281 NULL), 282 GNUNET_JSON_spec_mark_optional ( 283 GNUNET_JSON_spec_bool ("is_and_combinator", 284 &rule->is_and_combinator), 285 NULL), 286 GNUNET_JSON_spec_end () 287 }; 288 const char *err_name; 289 unsigned int err_line; 290 291 if (GNUNET_OK != 292 GNUNET_JSON_parse (jrule, 293 ispec, 294 &err_name, 295 &err_line)) 296 { 297 GNUNET_break_op (0); 298 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 299 "Malformed rule #%u in field %s\n", 300 (unsigned int) i, 301 err_name); 302 TALER_TESTING_interpreter_fail (is); 303 return; 304 } 305 if (NULL != jameasures) 306 { 307 rule->num_measures 308 = (unsigned int) json_array_size (jameasures); 309 rule->measures 310 = GNUNET_new_array (rule->num_measures, 311 const char *); 312 for (unsigned int k = 0; k < rule->num_measures; k++) 313 rule->measures[k] 314 = json_string_value ( 315 json_array_get (jameasures, 316 k)); 317 } 318 } 319 320 off = 0; 321 json_object_foreach ((json_t *) jmeasures, mname, jmeasure) 322 { 323 struct TALER_EXCHANGE_MeasureInformation *mi = &measures[off++]; 324 struct GNUNET_JSON_Specification ispec[] = { 325 GNUNET_JSON_spec_mark_optional ( 326 GNUNET_JSON_spec_string ("check_name", 327 &mi->check_name), 328 NULL), 329 GNUNET_JSON_spec_mark_optional ( 330 GNUNET_JSON_spec_string ("prog_name", 331 &mi->prog_name), 332 NULL), 333 GNUNET_JSON_spec_mark_optional ( 334 GNUNET_JSON_spec_object_const ("context", 335 &mi->context), 336 NULL), 337 GNUNET_JSON_spec_mark_optional ( 338 TALER_JSON_spec_kycte ("operation_type", 339 &mi->operation_type), 340 NULL), 341 GNUNET_JSON_spec_end () 342 }; 343 const char *err_name; 344 unsigned int err_line; 345 346 mi->measure_name = mname; 347 if (GNUNET_OK != 348 GNUNET_JSON_parse (jmeasure, 349 ispec, 350 &err_name, 351 &err_line)) 352 { 353 GNUNET_break_op (0); 354 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 355 "Malformed measure %s in field %s\n", 356 mname, 357 err_name); 358 TALER_TESTING_interpreter_fail (is); 359 for (unsigned int j = 0; j < num_rules; j++) 360 GNUNET_free (rules[j].measures); 361 return; 362 } 363 } 364 GNUNET_assert (off == num_measures); 365 366 ds->dh = TALER_EXCHANGE_post_aml_decision_create ( 367 TALER_TESTING_interpreter_get_context (is), 368 exchange_url, 369 h_payto, 370 now, 371 ds->successor_measure, 372 expiration_time, 373 num_rules, 374 rules, 375 num_measures, 376 measures, 377 ds->keep_investigating, 378 ds->justification, 379 officer_priv); 380 381 for (unsigned int j = 0; j < num_rules; j++) 382 GNUNET_free (rules[j].measures); 383 384 if (NULL == ds->dh) 385 { 386 GNUNET_break (0); 387 TALER_TESTING_interpreter_fail (is); 388 return; 389 } 390 391 /* Set optional: new_measures and properties */ 392 if (NULL != new_measures) 393 TALER_EXCHANGE_post_aml_decision_set_options ( 394 ds->dh, 395 TALER_EXCHANGE_post_aml_decision_option_new_measures (new_measures)); 396 if (NULL != ds->properties) 397 TALER_EXCHANGE_post_aml_decision_set_options ( 398 ds->dh, 399 TALER_EXCHANGE_post_aml_decision_option_properties (ds->properties)); 400 401 { 402 enum TALER_ErrorCode ec; 403 404 ec = TALER_EXCHANGE_post_aml_decision_start (ds->dh, 405 &take_aml_decision_cb, 406 ds); 407 if (TALER_EC_NONE != ec) 408 { 409 GNUNET_break (0); 410 ds->dh = NULL; 411 TALER_TESTING_interpreter_fail (is); 412 return; 413 } 414 } 415 } 416 } 417 418 419 /** 420 * Free the state of a "take_aml_decision" CMD, and possibly cancel a 421 * pending operation thereof. 422 * 423 * @param cls closure, must be a `struct AmlDecisionState`. 424 * @param cmd the command which is being cleaned up. 425 */ 426 static void 427 take_aml_decision_cleanup (void *cls, 428 const struct TALER_TESTING_Command *cmd) 429 { 430 struct AmlDecisionState *ds = cls; 431 432 if (NULL != ds->dh) 433 { 434 TALER_TESTING_command_incomplete (ds->is, 435 cmd->label); 436 TALER_EXCHANGE_post_aml_decision_cancel (ds->dh); 437 ds->dh = NULL; 438 } 439 json_decref (ds->new_rules); 440 json_decref (ds->properties); 441 GNUNET_free (ds); 442 } 443 444 445 /** 446 * Offer internal data of a "AML decision" CMD state to other 447 * commands. 448 * 449 * @param cls closure 450 * @param[out] ret result (could be anything) 451 * @param trait name of the trait 452 * @param index index number of the object to offer. 453 * @return #GNUNET_OK on success 454 */ 455 static enum GNUNET_GenericReturnValue 456 take_aml_decision_traits (void *cls, 457 const void **ret, 458 const char *trait, 459 unsigned int index) 460 { 461 struct AmlDecisionState *ws = cls; 462 struct TALER_TESTING_Trait traits[] = { 463 TALER_TESTING_make_trait_h_normalized_payto (&ws->h_payto), 464 TALER_TESTING_make_trait_aml_justification (ws->justification), 465 TALER_TESTING_trait_end () 466 }; 467 468 return TALER_TESTING_get_trait (traits, 469 ret, 470 trait, 471 index); 472 } 473 474 475 struct TALER_TESTING_Command 476 TALER_TESTING_cmd_take_aml_decision ( 477 const char *label, 478 const char *ref_officer, 479 const char *ref_operation, 480 bool keep_investigating, 481 struct GNUNET_TIME_Relative expiration_delay, 482 const char *successor_measure, 483 const char *new_rules, 484 const char *properties, 485 const char *justification, 486 unsigned int expected_response) 487 { 488 struct AmlDecisionState *ds; 489 json_error_t err; 490 491 ds = GNUNET_new (struct AmlDecisionState); 492 ds->officer_ref_cmd = ref_officer; 493 ds->account_ref_cmd = ref_operation; 494 ds->keep_investigating = keep_investigating; 495 ds->expiration_delay = expiration_delay; 496 ds->successor_measure = successor_measure; 497 ds->new_rules = json_loads (new_rules, 498 JSON_DECODE_ANY, 499 &err); 500 if (NULL == ds->new_rules) 501 { 502 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 503 "Invalid JSON in new rules of %s: %s\n", 504 label, 505 err.text); 506 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 507 "Input was: `%s'\n", 508 new_rules); 509 GNUNET_assert (0); 510 } 511 GNUNET_assert (NULL != ds->new_rules); 512 ds->properties = json_loads (properties, 513 0, 514 &err); 515 if (NULL == ds->properties) 516 { 517 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 518 "Invalid JSON in properties of %s: %s\n", 519 label, 520 err.text); 521 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 522 "Input was: `%s'\n", 523 properties); 524 GNUNET_assert (0); 525 } 526 ds->justification = justification; 527 ds->expected_response = expected_response; 528 { 529 struct TALER_TESTING_Command cmd = { 530 .cls = ds, 531 .label = label, 532 .run = &take_aml_decision_run, 533 .cleanup = &take_aml_decision_cleanup, 534 .traits = &take_aml_decision_traits 535 }; 536 537 return cmd; 538 } 539 } 540 541 542 /* end of testing_api_cmd_take_aml_decision.c */