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

Generated by: LCOV version 2.0-1