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 (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 */