exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

testing_api_cmd_take_aml_decision.c (15362B)


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