LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_take_aml_decision.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 70.7 % 157 111
Test Date: 2026-03-07 14:54:45 Functions: 100.0 % 5 5

            Line data    Source code
       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            3 : take_aml_decision_cb (
     121              :   struct AmlDecisionState *ds,
     122              :   const struct TALER_EXCHANGE_PostAmlDecisionResponse *pr)
     123              : {
     124            3 :   const struct TALER_EXCHANGE_HttpResponse *hr = &pr->hr;
     125              : 
     126            3 :   ds->dh = NULL;
     127            3 :   if (ds->expected_response != hr->http_status)
     128              :   {
     129            0 :     TALER_TESTING_unexpected_status (ds->is,
     130              :                                      hr->http_status,
     131              :                                      ds->expected_response);
     132            0 :     return;
     133              :   }
     134            3 :   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            3 : take_aml_decision_run (void *cls,
     147              :                        const struct TALER_TESTING_Command *cmd,
     148              :                        struct TALER_TESTING_Interpreter *is)
     149              : {
     150            3 :   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            3 :   const json_t *jmeasures = NULL;
     158              :   struct GNUNET_TIME_Timestamp expiration_time
     159            3 :     = GNUNET_TIME_relative_to_timestamp (ds->expiration_delay);
     160            3 :   const char *new_measures = NULL;
     161              :   struct GNUNET_JSON_Specification spec[] = {
     162            3 :     GNUNET_JSON_spec_array_const ("rules",
     163              :                                   &jrules),
     164            3 :     GNUNET_JSON_spec_mark_optional (
     165              :       GNUNET_JSON_spec_object_const ("custom_measures",
     166              :                                      &jmeasures),
     167              :       NULL),
     168            3 :     GNUNET_JSON_spec_mark_optional (
     169              :       GNUNET_JSON_spec_string ("new_measures",
     170              :                                &new_measures),
     171              :       NULL),
     172            3 :     GNUNET_JSON_spec_end ()
     173              :   };
     174              :   unsigned int num_rules;
     175              :   unsigned int num_measures;
     176              : 
     177              :   (void) cmd;
     178            3 :   if (GNUNET_OK !=
     179            3 :       GNUNET_JSON_parse (ds->new_rules,
     180              :                          spec,
     181              :                          NULL, NULL))
     182              :   {
     183            0 :     GNUNET_break_op (0);
     184            0 :     TALER_TESTING_interpreter_fail (is);
     185            0 :     return;
     186              :   }
     187              : 
     188              :   {
     189              :     const struct TALER_TESTING_Command *exchange_cmd;
     190              : 
     191            3 :     exchange_cmd = TALER_TESTING_interpreter_get_command (is,
     192              :                                                           "exchange");
     193            3 :     if (NULL == exchange_cmd)
     194              :     {
     195            0 :       GNUNET_break (0);
     196            0 :       TALER_TESTING_interpreter_fail (is);
     197            0 :       return;
     198              :     }
     199            3 :     GNUNET_assert (GNUNET_OK ==
     200              :                    TALER_TESTING_get_trait_exchange_url (exchange_cmd,
     201              :                                                          &exchange_url));
     202              :   }
     203            3 :   now = GNUNET_TIME_timestamp_get ();
     204            3 :   ds->is = is;
     205            3 :   ref = TALER_TESTING_interpreter_lookup_command (is,
     206              :                                                   ds->account_ref_cmd);
     207            3 :   if (NULL == ref)
     208              :   {
     209            0 :     GNUNET_break (0);
     210            0 :     TALER_TESTING_interpreter_fail (is);
     211            0 :     return;
     212              :   }
     213            3 :   if (GNUNET_OK !=
     214            3 :       TALER_TESTING_get_trait_h_normalized_payto (ref,
     215              :                                                   &h_payto))
     216              :   {
     217            0 :     GNUNET_break (0);
     218            0 :     TALER_TESTING_interpreter_fail (is);
     219            0 :     return;
     220              :   }
     221            3 :   ref = TALER_TESTING_interpreter_lookup_command (is,
     222              :                                                   ds->officer_ref_cmd);
     223            3 :   if (NULL == ref)
     224              :   {
     225            0 :     GNUNET_break (0);
     226            0 :     TALER_TESTING_interpreter_fail (is);
     227            0 :     return;
     228              :   }
     229            3 :   if (GNUNET_OK !=
     230            3 :       TALER_TESTING_get_trait_officer_priv (ref,
     231              :                                             &officer_priv))
     232              :   {
     233            0 :     GNUNET_break (0);
     234            0 :     TALER_TESTING_interpreter_fail (is);
     235            0 :     return;
     236              :   }
     237            3 :   ds->h_payto = *h_payto;
     238              : 
     239            3 :   num_rules = (unsigned int) json_array_size (jrules);
     240            3 :   num_measures = (unsigned int) json_object_size (jmeasures);
     241            3 :   {
     242            3 :     struct TALER_EXCHANGE_AccountRule rules[
     243            3 :       GNUNET_NZL (num_rules)];
     244            3 :     struct TALER_EXCHANGE_MeasureInformation measures[
     245            3 :       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            3 :     memset (rules,
     253              :             0,
     254              :             sizeof (rules));
     255            3 :     memset (measures,
     256              :             0,
     257              :             sizeof (measures));
     258            6 :     json_array_foreach ((json_t *) jrules, i, jrule)
     259              :     {
     260            3 :       struct TALER_EXCHANGE_AccountRule *rule = &rules[i];
     261            3 :       const json_t *jameasures = NULL;
     262              :       struct GNUNET_JSON_Specification ispec[] = {
     263            3 :         GNUNET_JSON_spec_relative_time ("timeframe",
     264              :                                         &rule->timeframe),
     265            3 :         TALER_JSON_spec_amount_any ("threshold",
     266              :                                     &rule->threshold),
     267            3 :         GNUNET_JSON_spec_mark_optional (
     268              :           GNUNET_JSON_spec_array_const ("measures",
     269              :                                         &jameasures),
     270              :           NULL),
     271            3 :         GNUNET_JSON_spec_mark_optional (
     272              :           GNUNET_JSON_spec_uint32 ("display_priority",
     273              :                                    &rule->display_priority),
     274              :           NULL),
     275            3 :         TALER_JSON_spec_kycte ("operation_type",
     276              :                                &rule->operation_type),
     277            3 :         GNUNET_JSON_spec_mark_optional (
     278              :           GNUNET_JSON_spec_bool ("verboten",
     279              :                                  &rule->verboten),
     280              :           NULL),
     281            3 :         GNUNET_JSON_spec_mark_optional (
     282              :           GNUNET_JSON_spec_bool ("exposed",
     283              :                                  &rule->exposed),
     284              :           NULL),
     285            3 :         GNUNET_JSON_spec_mark_optional (
     286              :           GNUNET_JSON_spec_bool ("is_and_combinator",
     287              :                                  &rule->is_and_combinator),
     288              :           NULL),
     289            3 :         GNUNET_JSON_spec_end ()
     290              :       };
     291              :       const char *err_name;
     292              :       unsigned int err_line;
     293              : 
     294            3 :       if (GNUNET_OK !=
     295            3 :           GNUNET_JSON_parse (jrule,
     296              :                              ispec,
     297              :                              &err_name,
     298              :                              &err_line))
     299              :       {
     300            0 :         GNUNET_break_op (0);
     301            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     302              :                     "Malformed rule #%u in field %s\n",
     303              :                     (unsigned int) i,
     304              :                     err_name);
     305            0 :         TALER_TESTING_interpreter_fail (is);
     306            0 :         return;
     307              :       }
     308            3 :       if (NULL != jameasures)
     309              :       {
     310              :         rule->num_measures
     311            1 :           = (unsigned int) json_array_size (jameasures);
     312              :         rule->measures
     313            1 :           = GNUNET_new_array (rule->num_measures,
     314              :                               const char *);
     315            2 :         for (unsigned int k = 0; k < rule->num_measures; k++)
     316            1 :           rule->measures[k]
     317            1 :             = json_string_value (
     318            1 :                 json_array_get (jameasures,
     319              :                                 k));
     320              :       }
     321              :     }
     322              : 
     323            3 :     off = 0;
     324            4 :     json_object_foreach ((json_t *) jmeasures, mname, jmeasure)
     325              :     {
     326            1 :       struct TALER_EXCHANGE_MeasureInformation *mi = &measures[off++];
     327              :       struct GNUNET_JSON_Specification ispec[] = {
     328            1 :         GNUNET_JSON_spec_mark_optional (
     329              :           GNUNET_JSON_spec_string ("check_name",
     330              :                                    &mi->check_name),
     331              :           NULL),
     332            1 :         GNUNET_JSON_spec_mark_optional (
     333              :           GNUNET_JSON_spec_string ("prog_name",
     334              :                                    &mi->prog_name),
     335              :           NULL),
     336            1 :         GNUNET_JSON_spec_mark_optional (
     337              :           GNUNET_JSON_spec_object_const ("context",
     338              :                                          &mi->context),
     339              :           NULL),
     340            1 :         GNUNET_JSON_spec_end ()
     341              :       };
     342              :       const char *err_name;
     343              :       unsigned int err_line;
     344              : 
     345            1 :       mi->measure_name = mname;
     346            1 :       if (GNUNET_OK !=
     347            1 :           GNUNET_JSON_parse (jmeasure,
     348              :                              ispec,
     349              :                              &err_name,
     350              :                              &err_line))
     351              :       {
     352            0 :         GNUNET_break_op (0);
     353            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     354              :                     "Malformed measure %s in field %s\n",
     355              :                     mname,
     356              :                     err_name);
     357            0 :         TALER_TESTING_interpreter_fail (is);
     358            0 :         for (unsigned int j = 0; j < num_rules; j++)
     359            0 :           GNUNET_free (rules[j].measures);
     360            0 :         return;
     361              :       }
     362              :     }
     363            3 :     GNUNET_assert (off == num_measures);
     364              : 
     365            3 :     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            3 :       ds->keep_investigating,
     377              :       ds->justification,
     378              :       officer_priv);
     379              : 
     380            6 :     for (unsigned int j = 0; j < num_rules; j++)
     381            3 :       GNUNET_free (rules[j].measures);
     382              : 
     383            3 :     if (NULL == ds->dh)
     384              :     {
     385            0 :       GNUNET_break (0);
     386            0 :       TALER_TESTING_interpreter_fail (is);
     387            0 :       return;
     388              :     }
     389              : 
     390              :     /* Set optional: new_measures and properties */
     391            3 :     if (NULL != new_measures)
     392            1 :       TALER_EXCHANGE_post_aml_decision_set_options (
     393              :         ds->dh,
     394              :         TALER_EXCHANGE_post_aml_decision_option_new_measures (new_measures));
     395            3 :     if (NULL != ds->properties)
     396            3 :       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            3 :       ec = TALER_EXCHANGE_post_aml_decision_start (ds->dh,
     404              :                                                    &take_aml_decision_cb,
     405              :                                                    ds);
     406            3 :       if (TALER_EC_NONE != ec)
     407              :       {
     408            0 :         GNUNET_break (0);
     409            0 :         ds->dh = NULL;
     410            0 :         TALER_TESTING_interpreter_fail (is);
     411            0 :         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            3 : take_aml_decision_cleanup (void *cls,
     427              :                            const struct TALER_TESTING_Command *cmd)
     428              : {
     429            3 :   struct AmlDecisionState *ds = cls;
     430              : 
     431            3 :   if (NULL != ds->dh)
     432              :   {
     433            0 :     TALER_TESTING_command_incomplete (ds->is,
     434              :                                       cmd->label);
     435            0 :     TALER_EXCHANGE_post_aml_decision_cancel (ds->dh);
     436            0 :     ds->dh = NULL;
     437              :   }
     438            3 :   json_decref (ds->new_rules);
     439            3 :   json_decref (ds->properties);
     440            3 :   GNUNET_free (ds);
     441            3 : }
     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            4 : take_aml_decision_traits (void *cls,
     456              :                           const void **ret,
     457              :                           const char *trait,
     458              :                           unsigned int index)
     459              : {
     460            4 :   struct AmlDecisionState *ws = cls;
     461              :   struct TALER_TESTING_Trait traits[] = {
     462            4 :     TALER_TESTING_make_trait_h_normalized_payto (&ws->h_payto),
     463            4 :     TALER_TESTING_make_trait_aml_justification (ws->justification),
     464            4 :     TALER_TESTING_trait_end ()
     465              :   };
     466              : 
     467            4 :   return TALER_TESTING_get_trait (traits,
     468              :                                   ret,
     469              :                                   trait,
     470              :                                   index);
     471              : }
     472              : 
     473              : 
     474              : struct TALER_TESTING_Command
     475            3 : 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            3 :   ds = GNUNET_new (struct AmlDecisionState);
     491            3 :   ds->officer_ref_cmd = ref_officer;
     492            3 :   ds->account_ref_cmd = ref_operation;
     493            3 :   ds->keep_investigating = keep_investigating;
     494            3 :   ds->expiration_delay = expiration_delay;
     495            3 :   ds->successor_measure = successor_measure;
     496            3 :   ds->new_rules = json_loads (new_rules,
     497              :                               JSON_DECODE_ANY,
     498              :                               &err);
     499            3 :   if (NULL == ds->new_rules)
     500              :   {
     501            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     502              :                 "Invalid JSON in new rules of %s: %s\n",
     503              :                 label,
     504              :                 err.text);
     505            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     506              :                 "Input was: `%s'\n",
     507              :                 new_rules);
     508            0 :     GNUNET_assert (0);
     509              :   }
     510            3 :   GNUNET_assert (NULL != ds->new_rules);
     511            3 :   ds->properties = json_loads (properties,
     512              :                                0,
     513              :                                &err);
     514            3 :   if (NULL == ds->properties)
     515              :   {
     516            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     517              :                 "Invalid JSON in properties of %s: %s\n",
     518              :                 label,
     519              :                 err.text);
     520            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     521              :                 "Input was: `%s'\n",
     522              :                 properties);
     523            0 :     GNUNET_assert (0);
     524              :   }
     525            3 :   ds->justification = justification;
     526            3 :   ds->expected_response = expected_response;
     527              :   {
     528            3 :     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            3 :     return cmd;
     537              :   }
     538              : }
     539              : 
     540              : 
     541              : /* end of testing_api_cmd_take_aml_decision.c */
        

Generated by: LCOV version 2.0-1