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