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