LCOV - code coverage report
Current view: top level - exchangedb - exchangedb_aml.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 81 265 30.6 %
Date: 2025-06-22 12:09:43 Functions: 6 11 54.5 %

          Line data    Source code
       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 under the
       6             :   terms of the GNU Affero General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU Affero General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file exchangedb_aml.c
      18             :  * @brief helper function to handle AML programs
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "taler/taler_exchangedb_plugin.h"
      22             : #include "taler/taler_exchangedb_lib.h"
      23             : #include "taler/taler_kyclogic_lib.h"
      24             : #include "taler/taler_json_lib.h"
      25             : #include "taler/taler_dbevents.h"
      26             : #include <gnunet/gnunet_common.h>
      27             : 
      28             : /**
      29             :  * Maximum recursion depth we allow for AML programs.
      30             :  * Basically, after this number of "skip" processes
      31             :  * we forcefully terminate the recursion and fail hard.
      32             :  */
      33             : #define MAX_DEPTH 16
      34             : 
      35             : enum GNUNET_DB_QueryStatus
      36          10 : TALER_EXCHANGEDB_persist_aml_program_result (
      37             :   struct TALER_EXCHANGEDB_Plugin *plugin,
      38             :   uint64_t process_row,
      39             :   const struct TALER_NormalizedPaytoHashP *account_id,
      40             :   const struct TALER_KYCLOGIC_AmlProgramResult *apr,
      41             :   enum TALER_EXCHANGEDB_PersistProgramResultStatus *ret_pprs)
      42             : {
      43             :   enum GNUNET_DB_QueryStatus qs;
      44          10 :   json_t *jmeasures = NULL;
      45          10 :   struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs = NULL;
      46             : 
      47          10 :   GNUNET_assert (NULL != ret_pprs);
      48             : 
      49          10 :   *ret_pprs = TALER_EXCHANGEDB_PPRS_OK;
      50             : 
      51          10 :   if ( (TALER_KYCLOGIC_AMLR_SUCCESS == apr->status) &&
      52          10 :        (NULL != apr->details.success.new_measures) )
      53             :   {
      54           0 :     lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules);
      55           0 :     if (NULL == lrs)
      56             :     {
      57           0 :       qs = plugin->insert_aml_program_failure (
      58             :         plugin->cls,
      59             :         process_row,
      60             :         account_id,
      61             :         "Failed to parse AML program output",
      62             :         TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT);
      63           0 :       GNUNET_break (qs > 0);
      64           0 :       return qs;
      65             :     }
      66           0 :     jmeasures = TALER_KYCLOGIC_get_jmeasures (
      67             :       lrs,
      68           0 :       apr->details.success.new_measures);
      69           0 :     if (NULL == jmeasures)
      70             :     {
      71             :       char *err;
      72             : 
      73           0 :       GNUNET_break (0);
      74           0 :       GNUNET_asprintf (&err,
      75             :                        "Failed to find measures `%s' specified in AML program output",
      76           0 :                        apr->details.success.new_measures);
      77           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      78             :                   "AML program specified invalid measures `%s'\n",
      79             :                   apr->details.success.new_measures);
      80           0 :       qs = plugin->insert_aml_program_failure (
      81             :         plugin->cls,
      82             :         process_row,
      83             :         account_id,
      84             :         err,
      85             :         TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT);
      86           0 :       *ret_pprs = TALER_EXCHANGEDB_PPRS_BAD_OUTCOME;
      87           0 :       GNUNET_free (err);
      88           0 :       GNUNET_break (qs > 0);
      89           0 :       return qs;
      90             :     }
      91             :   }
      92             : 
      93          10 :   qs = plugin->clear_aml_lock (
      94             :     plugin->cls,
      95             :     account_id);
      96          10 :   switch (apr->status)
      97             :   {
      98           0 :   case TALER_KYCLOGIC_AMLR_FAILURE:
      99           0 :     qs = plugin->insert_aml_program_failure (
     100             :       plugin->cls,
     101             :       process_row,
     102             :       account_id,
     103           0 :       apr->details.failure.error_message,
     104           0 :       apr->details.failure.ec);
     105           0 :     GNUNET_break (qs > 0);
     106           0 :     goto cleanup;
     107          10 :   case TALER_KYCLOGIC_AMLR_SUCCESS:
     108             :     {
     109          10 :       struct TALER_FullPayto null_payto_uri = { 0 };
     110             :       bool invalid_officer;
     111             :       bool unknown_account;
     112             :       struct GNUNET_TIME_Timestamp last_date;
     113             :       uint64_t legitimization_measure_serial_id;
     114             :       bool is_wallet;
     115             : 
     116          10 :       qs = plugin->insert_aml_decision (
     117             :         plugin->cls,
     118             :         null_payto_uri,
     119             :         account_id,
     120             :         GNUNET_TIME_timestamp_get (),
     121             :         apr->details.success.expiration_time,
     122          10 :         apr->details.success.account_properties,
     123          10 :         apr->details.success.new_rules,
     124          10 :         apr->details.success.to_investigate,
     125          10 :         apr->details.success.new_measures,
     126             :         jmeasures,
     127             :         NULL, /* justification */
     128             :         NULL, /* decider_pub */
     129             :         NULL, /* decider_sig */
     130          10 :         apr->details.success.num_events,
     131          10 :         apr->details.success.events,
     132             :         NULL, /* form ID */
     133             :         0, /* enc_attributes_size*/
     134             :         NULL, /* enc_attributes*/
     135             :         NULL, /* attributes_hash */
     136          10 :         GNUNET_TIME_UNIT_ZERO_TS, /* attributes_expiration_time */
     137             :         &invalid_officer,
     138             :         &unknown_account,
     139             :         &last_date,
     140             :         &legitimization_measure_serial_id,
     141             :         &is_wallet);
     142          10 :       GNUNET_break (qs > 0);
     143          10 :       goto cleanup;
     144             :     }
     145             :   }
     146           0 :   GNUNET_break (0);
     147           0 :   qs = GNUNET_DB_STATUS_HARD_ERROR;
     148          10 : cleanup:
     149          10 :   TALER_KYCLOGIC_rules_free (lrs);
     150          10 :   json_decref (jmeasures);
     151          10 :   return qs;
     152             : }
     153             : 
     154             : 
     155             : struct TALER_EXCHANGEDB_RuleUpdater
     156             : {
     157             :   /**
     158             :    * database plugin to use
     159             :    */
     160             :   struct TALER_EXCHANGEDB_Plugin *plugin;
     161             : 
     162             :   /**
     163             :    * key to use to decrypt attributes
     164             :    */
     165             :   struct TALER_AttributeEncryptionKeyP attribute_key;
     166             : 
     167             :   /**
     168             :    * account to get the rule set for
     169             :    */
     170             :   struct TALER_NormalizedPaytoHashP account;
     171             : 
     172             :   /**
     173             :    * function to call with the result
     174             :    */
     175             :   TALER_EXCHANGEDB_CurrentRulesCallback cb;
     176             : 
     177             :   /**
     178             :    * Closure for @e cb.
     179             :    */
     180             :   void *cb_cls;
     181             : 
     182             :   /**
     183             :    * Current rule set we are working on.
     184             :    */
     185             :   struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs;
     186             : 
     187             :   /**
     188             :    * Task for asynchronous continuations.
     189             :    */
     190             :   struct GNUNET_SCHEDULER_Task *t;
     191             : 
     192             :   /**
     193             :    * Handler waiting notification that (previous) AML program
     194             :    * finished.
     195             :    */
     196             :   struct GNUNET_DB_EventHandler *eh;
     197             : 
     198             :   /**
     199             :    * Handle to running AML program.
     200             :    */
     201             :   struct TALER_KYCLOGIC_AmlProgramRunnerHandle *amlh;
     202             : 
     203             :   /**
     204             :    * Name of the AML program we were running asynchronously,
     205             :    * for diagnostics.
     206             :    */
     207             :   char *aml_program_name;
     208             : 
     209             :   /**
     210             :    * Error hint to return with @e ec.
     211             :    */
     212             :   const char *hint;
     213             : 
     214             :   /**
     215             :    * Row the rule set in @a lrs is based on.
     216             :    */
     217             :   uint64_t legitimization_outcome_last_row;
     218             : 
     219             :   /**
     220             :    * Taler error code to return.
     221             :    */
     222             :   enum TALER_ErrorCode ec;
     223             : 
     224             :   /**
     225             :    * Counter used to limit recursion depth.
     226             :    */
     227             :   unsigned int depth;
     228             : 
     229             :   /**
     230             :    * True if @e account is for a wallet.
     231             :    */
     232             :   bool is_wallet;
     233             : };
     234             : 
     235             : 
     236             : /**
     237             :  * Function that finally returns the result to the application and cleans
     238             :  * up. Called with an open database transaction on success; on failure, the
     239             :  * transaction will have already been rolled back.
     240             :  *
     241             :  * @param[in,out] ru rule updater to return result for
     242             :  */
     243             : static void
     244          28 : return_result (struct TALER_EXCHANGEDB_RuleUpdater *ru)
     245             : {
     246          28 :   struct TALER_EXCHANGEDB_RuleUpdaterResult rur = {
     247          28 :     .legitimization_outcome_last_row = ru->legitimization_outcome_last_row,
     248          28 :     .lrs = ru->lrs,
     249          28 :     .ec = ru->ec,
     250             :   };
     251             : 
     252          28 :   ru->cb (ru->cb_cls,
     253             :           &rur);
     254          28 :   ru->lrs = NULL;
     255          28 :   TALER_EXCHANGEDB_update_rules_cancel (ru);
     256          28 : }
     257             : 
     258             : 
     259             : /**
     260             :  * Fail the update with the given @a ec and @a hint.
     261             :  * Called with an open database transaction, which will
     262             :  * be rolled back (!).
     263             :  *
     264             :  * @param[in,out] ru account we are processing
     265             :  * @param ec error code to fail with
     266             :  * @param hint hint to return, can be NULL
     267             :  */
     268             : static void
     269           0 : fail_update (struct TALER_EXCHANGEDB_RuleUpdater *ru,
     270             :              enum TALER_ErrorCode ec,
     271             :              const char *hint)
     272             : {
     273           0 :   GNUNET_assert (NULL == ru->t);
     274           0 :   ru->plugin->rollback (ru->plugin->cls);
     275           0 :   ru->ec = ec;
     276           0 :   ru->hint = hint;
     277           0 :   return_result (ru);
     278           0 : }
     279             : 
     280             : 
     281             : /**
     282             :  * Check the rules in @a ru to see if they are current, and
     283             :  * if not begin the updating process.  Called with an open
     284             :  * database transaction.
     285             :  *
     286             :  * @param[in] ru rule updater context
     287             :  */
     288             : static void
     289             : check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru);
     290             : 
     291             : 
     292             : /**
     293             :  * Run the measure @a m in the context of the legitimisation rules
     294             :  * of @a ru.  Called with an open database transaction.
     295             :  *
     296             :  * @param ru updating context we are using
     297             :  * @param m measure we need to run next
     298             :  */
     299             : static void
     300             : run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru,
     301             :              const struct TALER_KYCLOGIC_Measure *m);
     302             : 
     303             : 
     304             : /**
     305             :  * Function called after AML program was run.  Called
     306             :  * without an open database transaction, will start one!
     307             :  *
     308             :  * @param cls the `struct TALER_EXCHANGEDB_RuleUpdater *`
     309             :  * @param apr result of the AML program.
     310             :  */
     311             : static void
     312           0 : aml_result_callback (
     313             :   void *cls,
     314             :   const struct TALER_KYCLOGIC_AmlProgramResult *apr)
     315             : {
     316           0 :   struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
     317             :   enum GNUNET_DB_QueryStatus qs;
     318             :   enum GNUNET_GenericReturnValue res;
     319             :   enum TALER_EXCHANGEDB_PersistProgramResultStatus pprs;
     320             : 
     321           0 :   ru->amlh = NULL;
     322           0 :   res = ru->plugin->start (ru->plugin->cls,
     323             :                            "aml-persist-aml-program-result");
     324           0 :   if (GNUNET_OK != res)
     325             :   {
     326           0 :     GNUNET_break (0);
     327           0 :     fail_update (ru,
     328             :                  TALER_EC_GENERIC_DB_START_FAILED,
     329             :                  "aml-persist-aml-program-result");
     330           0 :     return;
     331             :   }
     332             :   /* Update database update based on result */
     333           0 :   qs = TALER_EXCHANGEDB_persist_aml_program_result (
     334             :     ru->plugin,
     335             :     0LLU, /* 0: no existing legitimization process, creates new row */
     336           0 :     &ru->account,
     337             :     apr,
     338             :     &pprs);
     339           0 :   switch (qs)
     340             :   {
     341           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     342           0 :     GNUNET_break (0);
     343           0 :     fail_update (ru,
     344             :                  TALER_EC_GENERIC_DB_STORE_FAILED,
     345             :                  "persist_aml_program_result");
     346           0 :     return;
     347           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     348             :     /* Bad, couldn't persist AML result. Try again... */
     349           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     350             :                 "Serialization issue persisting result of AML program. Restarting.\n");
     351           0 :     fail_update (ru,
     352             :                  TALER_EC_GENERIC_DB_SOFT_FAILURE,
     353             :                  "persist_aml_program_result");
     354           0 :     return;
     355           0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     356             :     /* Strange, but let's just continue */
     357           0 :     break;
     358           0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     359             :     /* normal case */
     360           0 :     break;
     361             :   }
     362           0 :   switch (pprs)
     363             :   {
     364           0 :   case TALER_EXCHANGEDB_PPRS_OK:
     365           0 :     break;
     366           0 :   case TALER_EXCHANGEDB_PPRS_BAD_OUTCOME:
     367           0 :     fail_update (ru,
     368             :                  TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT,
     369             :                  "persist_aml_program_result");
     370           0 :     return;
     371             :   }
     372           0 :   switch (apr->status)
     373             :   {
     374           0 :   case TALER_KYCLOGIC_AMLR_SUCCESS:
     375           0 :     TALER_KYCLOGIC_rules_free (ru->lrs);
     376           0 :     ru->lrs = NULL;
     377           0 :     ru->lrs = TALER_KYCLOGIC_rules_parse (apr->details.success.new_rules);
     378             :     /* Fall back to default rules on parse error! */
     379           0 :     GNUNET_break (NULL != ru->lrs);
     380           0 :     check_rules (ru);
     381           0 :     return;
     382           0 :   case TALER_KYCLOGIC_AMLR_FAILURE:
     383             :     {
     384           0 :       const char *fmn = apr->details.failure.fallback_measure;
     385             :       const struct TALER_KYCLOGIC_Measure *m;
     386             : 
     387           0 :       m = TALER_KYCLOGIC_get_measure (ru->lrs,
     388             :                                       fmn);
     389           0 :       if (NULL == m)
     390             :       {
     391           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     392             :                     "Fallback measure `%s' does not exist (anymore?).\n",
     393             :                     fmn);
     394           0 :         TALER_KYCLOGIC_rules_free (ru->lrs);
     395           0 :         ru->lrs = NULL;
     396           0 :         return_result (ru);
     397           0 :         return;
     398             :       }
     399           0 :       run_measure (ru,
     400             :                    m);
     401           0 :       return;
     402             :     }
     403             :   }
     404             :   /* This should be impossible */
     405           0 :   GNUNET_assert (0);
     406             : }
     407             : 
     408             : 
     409             : /**
     410             :  * Entrypoint that fetches the latest rules from the database
     411             :  * and starts processing them. Called without an open database
     412             :  * transaction, will start one.
     413             :  *
     414             :  * @param[in] cls the `struct TALER_EXCHANGEDB_RuleUpdater *` to run
     415             :  */
     416             : static void
     417             : fetch_latest_rules (void *cls);
     418             : 
     419             : 
     420             : /**
     421             :  * Notification called when we either timeout on the AML program lock
     422             :  * or when the (previous) AML program finished and we can thus try again.
     423             :  *
     424             :  * @param cls  the `struct TALER_EXCHANGEDB_RuleUpdater *` to continue
     425             :  * @param extra additional event data provided (unused)
     426             :  * @param extra_size number of bytes in @a extra (unused)
     427             :  */
     428             : static void
     429           0 : trigger_fetch_latest_rules (void *cls,
     430             :                             const void *extra,
     431             :                             size_t extra_size)
     432             : {
     433           0 :   struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
     434             : 
     435             :   (void) extra;
     436             :   (void) extra_size;
     437           0 :   if (NULL != ru->t)
     438           0 :     return; /* multiple events triggered us, ignore */
     439           0 :   ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules,
     440             :                                     ru);
     441             : }
     442             : 
     443             : 
     444             : static void
     445           0 : run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru,
     446             :              const struct TALER_KYCLOGIC_Measure *m)
     447             : {
     448           0 :   if (NULL == m)
     449             :   {
     450             :     /* fall back to default rules */
     451           0 :     TALER_KYCLOGIC_rules_free (ru->lrs);
     452           0 :     ru->lrs = NULL;
     453           0 :     return_result (ru);
     454           0 :     return;
     455             :   }
     456           0 :   ru->depth++;
     457           0 :   if (ru->depth > MAX_DEPTH)
     458             :   {
     459           0 :     fail_update (ru,
     460             :                  TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED,
     461             :                  NULL);
     462           0 :     return;
     463             :   }
     464           0 :   if ( (NULL == m->check_name) ||
     465             :        (0 ==
     466           0 :         strcasecmp ("SKIP",
     467           0 :                     m->check_name)) )
     468             :   {
     469           0 :     struct TALER_EXCHANGEDB_HistoryBuilderContext hbc = {
     470           0 :       .account = &ru->account,
     471           0 :       .is_wallet = ru->is_wallet,
     472           0 :       .db_plugin = ru->plugin,
     473           0 :       .attribute_key = &ru->attribute_key
     474             :     };
     475             :     enum GNUNET_DB_QueryStatus qs;
     476             :     struct GNUNET_TIME_Absolute xlock;
     477             : 
     478             :     /* Free previous one, in case we are iterating... */
     479           0 :     GNUNET_free (ru->aml_program_name);
     480           0 :     if (NULL != m->prog_name)
     481             :     {
     482           0 :       ru->aml_program_name = GNUNET_strdup (m->prog_name);
     483             :     }
     484             :     else
     485             :     {
     486             :       /* How do we get to run a measure if the check type
     487             :          is INFO (which is the only case where prog_name
     488             :          is allowed to be NULL?) */
     489           0 :       GNUNET_break (0);
     490           0 :       ru->aml_program_name = NULL;
     491             :     }
     492           0 :     qs = ru->plugin->set_aml_lock (
     493           0 :       ru->plugin->cls,
     494           0 :       &ru->account,
     495           0 :       GNUNET_TIME_relative_multiply (ru->plugin->max_aml_program_runtime,
     496             :                                      2),
     497             :       &xlock);
     498           0 :     if (GNUNET_TIME_absolute_is_future (xlock))
     499             :     {
     500           0 :       struct TALER_KycCompletedEventP eh = {
     501           0 :         .header.size = htons (sizeof (eh)),
     502           0 :         .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
     503             :         .h_payto = ru->account
     504             :       };
     505             :       /* Wait for either timeout or notification */
     506           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     507             :                   "AML program already running, waiting for it to finish\n");
     508           0 :       ru->plugin->rollback (ru->plugin->cls);
     509             :       ru->eh
     510           0 :         = ru->plugin->event_listen (
     511           0 :             ru->plugin->cls,
     512             :             GNUNET_TIME_absolute_get_remaining (xlock),
     513             :             &eh.header,
     514             :             &trigger_fetch_latest_rules,
     515             :             ru);
     516           0 :       return;
     517             :     }
     518           0 :     qs = ru->plugin->commit (ru->plugin->cls);
     519           0 :     if (qs < 0)
     520             :     {
     521           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     522           0 :       fail_update (ru,
     523             :                    GNUNET_DB_STATUS_SOFT_ERROR == qs
     524             :                    ? TALER_EC_GENERIC_DB_SOFT_FAILURE
     525             :                    : TALER_EC_GENERIC_DB_COMMIT_FAILED,
     526             :                    "current-aml-rule-fetch");
     527           0 :       return;
     528             :     }
     529           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     530             :                 "Check is of type 'SKIP', running AML program %s.\n",
     531             :                 m->prog_name);
     532           0 :     GNUNET_assert (NULL == ru->t);
     533           0 :     ru->amlh = TALER_KYCLOGIC_run_aml_program3 (
     534           0 :       ru->is_wallet,
     535             :       m,
     536             :       &TALER_EXCHANGEDB_current_attributes_builder,
     537             :       &hbc,
     538             :       &TALER_EXCHANGEDB_current_rule_builder,
     539             :       &hbc,
     540             :       &TALER_EXCHANGEDB_aml_history_builder,
     541             :       &hbc,
     542             :       &TALER_EXCHANGEDB_kyc_history_builder,
     543             :       &hbc,
     544           0 :       ru->plugin->max_aml_program_runtime,
     545             :       &aml_result_callback,
     546             :       ru);
     547           0 :     return;
     548             :   }
     549             : 
     550             :   /* User MUST pass interactive check */
     551           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     552             :               "Measure %s involves check %s\n",
     553             :               m->measure_name,
     554             :               m->check_name);
     555             :   {
     556             :     /* activate the measure/check */
     557             :     json_t *succ_jmeasures
     558           0 :       = TALER_KYCLOGIC_get_jmeasures (
     559           0 :           ru->lrs,
     560           0 :           m->measure_name);
     561             :     bool unknown_account;
     562             :     struct GNUNET_TIME_Timestamp last_date;
     563             :     enum GNUNET_DB_QueryStatus qs;
     564             : 
     565           0 :     qs = ru->plugin->insert_successor_measure (
     566           0 :       ru->plugin->cls,
     567           0 :       &ru->account,
     568             :       GNUNET_TIME_timestamp_get (),
     569           0 :       m->measure_name,
     570             :       succ_jmeasures,
     571             :       &unknown_account,
     572             :       &last_date);
     573           0 :     json_decref (succ_jmeasures);
     574           0 :     switch (qs)
     575             :     {
     576           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     577           0 :       GNUNET_log (
     578             :         GNUNET_ERROR_TYPE_INFO,
     579             :         "Serialization issue!\n");
     580           0 :       fail_update (ru,
     581             :                    TALER_EC_GENERIC_DB_SOFT_FAILURE,
     582             :                    "insert_successor_measure");
     583           0 :       return;
     584           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     585           0 :       GNUNET_break (0);
     586           0 :       fail_update (ru,
     587             :                    TALER_EC_GENERIC_DB_STORE_FAILED,
     588             :                    "insert_successor_measure");
     589           0 :       return;
     590           0 :     default:
     591           0 :       break;
     592             :     }
     593           0 :     if (unknown_account)
     594             :     {
     595           0 :       fail_update (ru,
     596             :                    TALER_EC_EXCHANGE_GENERIC_BANK_ACCOUNT_UNKNOWN,
     597             :                    NULL);
     598           0 :       return;
     599             :     }
     600             :   }
     601             :   /* The rules remain these rules until the user passes the check */
     602           0 :   return_result (ru);
     603             : }
     604             : 
     605             : 
     606             : /**
     607             :  * Update the expired legitimization rules in @a ru, checking for expiration
     608             :  * first.  Called with an open database transaction.
     609             :  *
     610             :  * @param[in,out] ru account we are processing
     611             :  */
     612             : static void
     613           0 : update_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
     614             : {
     615             :   const struct TALER_KYCLOGIC_Measure *m;
     616             : 
     617           0 :   GNUNET_assert (NULL != ru->lrs);
     618           0 :   GNUNET_assert (GNUNET_TIME_absolute_is_past (
     619             :                    TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time));
     620           0 :   m = TALER_KYCLOGIC_rules_get_successor (ru->lrs);
     621           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     622             :               "Successor measure is %s.\n",
     623             :               (NULL != m) ? m->measure_name : "(null)");
     624           0 :   run_measure (ru,
     625             :                m);
     626           0 : }
     627             : 
     628             : 
     629             : static void
     630          28 : check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
     631             : {
     632          28 :   ru->depth++;
     633          28 :   if (ru->depth > MAX_DEPTH)
     634             :   {
     635           0 :     fail_update (ru,
     636             :                  TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED,
     637             :                  NULL);
     638           0 :     return;
     639             :   }
     640          28 :   if (NULL == ru->lrs)
     641             :   {
     642             :     /* return NULL, aka default rules */
     643          11 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     644             :                 "Default rules apply\n");
     645          11 :     return_result (ru);
     646          11 :     return;
     647             :   }
     648          17 :   if (! GNUNET_TIME_absolute_is_past
     649          17 :         (TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time) )
     650             :   {
     651             :     /* Rules did not expire, return them! */
     652          17 :     return_result (ru);
     653          17 :     return;
     654             :   }
     655           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     656             :               "Custom rules expired, updating...\n");
     657           0 :   update_rules (ru);
     658             : }
     659             : 
     660             : 
     661             : static void
     662          28 : fetch_latest_rules (void *cls)
     663             : {
     664          28 :   struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
     665             :   enum GNUNET_DB_QueryStatus qs;
     666             :   json_t *jnew_rules;
     667             :   enum GNUNET_GenericReturnValue res;
     668             : 
     669          28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     670             :               "Fetching latest rules.");
     671             : 
     672          28 :   ru->t = NULL;
     673          28 :   if (NULL != ru->eh)
     674             :   {
     675             :     /* cancel event listener, if we have one */
     676           0 :     ru->plugin->event_listen_cancel (ru->plugin->cls,
     677             :                                      ru->eh);
     678           0 :     ru->eh = NULL;
     679             :   }
     680          28 :   GNUNET_break (NULL == ru->lrs);
     681          28 :   res = ru->plugin->start (ru->plugin->cls,
     682             :                            "aml-begin-lookup-rules-by-access-token");
     683          28 :   if (GNUNET_OK != res)
     684             :   {
     685           0 :     GNUNET_break (0);
     686           0 :     fail_update (ru,
     687             :                  TALER_EC_GENERIC_DB_START_FAILED,
     688             :                  "aml-begin-lookup-rules-by-access-token");
     689           0 :     return;
     690             :   }
     691          28 :   qs = ru->plugin->lookup_rules_by_access_token (
     692          28 :     ru->plugin->cls,
     693          28 :     &ru->account,
     694             :     &jnew_rules,
     695             :     &ru->legitimization_outcome_last_row);
     696          28 :   if (qs < 0)
     697             :   {
     698           0 :     GNUNET_break (0);
     699           0 :     fail_update (ru,
     700             :                  TALER_EC_GENERIC_DB_FETCH_FAILED,
     701             :                  "lookup_rules_by_access_token");
     702           0 :     return;
     703             :   }
     704          28 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
     705          17 :        (NULL != jnew_rules) )
     706             :   {
     707          17 :     ru->lrs = TALER_KYCLOGIC_rules_parse (jnew_rules);
     708          17 :     GNUNET_break (NULL != ru->lrs);
     709          17 :     json_decref (jnew_rules);
     710             :   }
     711          28 :   check_rules (ru);
     712             : }
     713             : 
     714             : 
     715             : struct TALER_EXCHANGEDB_RuleUpdater *
     716          28 : TALER_EXCHANGEDB_update_rules (
     717             :   struct TALER_EXCHANGEDB_Plugin *plugin,
     718             :   const struct TALER_AttributeEncryptionKeyP *attribute_key,
     719             :   const struct TALER_NormalizedPaytoHashP *account,
     720             :   bool is_wallet,
     721             :   TALER_EXCHANGEDB_CurrentRulesCallback cb,
     722             :   void *cb_cls)
     723             : {
     724             :   struct TALER_EXCHANGEDB_RuleUpdater *ru;
     725             : 
     726          28 :   ru = GNUNET_new (struct TALER_EXCHANGEDB_RuleUpdater);
     727          28 :   ru->plugin = plugin;
     728          28 :   ru->attribute_key = *attribute_key;
     729          28 :   ru->account = *account;
     730          28 :   ru->is_wallet = is_wallet;
     731          28 :   ru->cb = cb;
     732          28 :   ru->cb_cls = cb_cls;
     733          28 :   ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules,
     734             :                                     ru);
     735          28 :   return ru;
     736             : }
     737             : 
     738             : 
     739             : void
     740          28 : TALER_EXCHANGEDB_update_rules_cancel (
     741             :   struct TALER_EXCHANGEDB_RuleUpdater *ru)
     742             : {
     743          28 :   if (NULL != ru->t)
     744             :   {
     745           0 :     GNUNET_SCHEDULER_cancel (ru->t);
     746           0 :     ru->t = NULL;
     747             :   }
     748          28 :   if (NULL != ru->amlh)
     749             :   {
     750           0 :     TALER_KYCLOGIC_run_aml_program_cancel (ru->amlh);
     751           0 :     ru->amlh = NULL;
     752             :   }
     753          28 :   if (NULL != ru->lrs)
     754             :   {
     755           0 :     TALER_KYCLOGIC_rules_free (ru->lrs);
     756           0 :     ru->lrs = NULL;
     757             :   }
     758          28 :   if (NULL != ru->eh)
     759             :   {
     760           0 :     ru->plugin->event_listen_cancel (ru->plugin->cls,
     761             :                                      ru->eh);
     762           0 :     ru->eh = NULL;
     763             :   }
     764          28 :   GNUNET_free (ru->aml_program_name);
     765          28 :   GNUNET_free (ru);
     766          28 : }

Generated by: LCOV version 1.16