LCOV - code coverage report
Current view: top level - exchangedb - exchangedb_aml.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 81 262 30.9 %
Date: 2025-06-05 21:03:14 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_exchangedb_plugin.h"
      22             : #include "taler_exchangedb_lib.h"
      23             : #include "taler_kyclogic_lib.h"
      24             : #include "taler_json_lib.h"
      25             : #include "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 :     ru->aml_program_name = GNUNET_strdup (m->prog_name);
     481           0 :     qs = ru->plugin->set_aml_lock (
     482           0 :       ru->plugin->cls,
     483           0 :       &ru->account,
     484           0 :       GNUNET_TIME_relative_multiply (ru->plugin->max_aml_program_runtime,
     485             :                                      2),
     486             :       &xlock);
     487           0 :     if (GNUNET_TIME_absolute_is_future (xlock))
     488             :     {
     489           0 :       struct TALER_KycCompletedEventP eh = {
     490           0 :         .header.size = htons (sizeof (eh)),
     491           0 :         .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
     492             :         .h_payto = ru->account
     493             :       };
     494             :       /* Wait for either timeout or notification */
     495           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     496             :                   "AML program already running, waiting for it to finish\n");
     497           0 :       ru->plugin->rollback (ru->plugin->cls);
     498             :       ru->eh
     499           0 :         = ru->plugin->event_listen (
     500           0 :             ru->plugin->cls,
     501             :             GNUNET_TIME_absolute_get_remaining (xlock),
     502             :             &eh.header,
     503             :             &trigger_fetch_latest_rules,
     504             :             ru);
     505           0 :       return;
     506             :     }
     507           0 :     qs = ru->plugin->commit (ru->plugin->cls);
     508           0 :     if (qs < 0)
     509             :     {
     510           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     511           0 :       fail_update (ru,
     512             :                    GNUNET_DB_STATUS_SOFT_ERROR == qs
     513             :                    ? TALER_EC_GENERIC_DB_SOFT_FAILURE
     514             :                    : TALER_EC_GENERIC_DB_COMMIT_FAILED,
     515             :                    "current-aml-rule-fetch");
     516           0 :       return;
     517             :     }
     518           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     519             :                 "Check is of type 'skip', running AML program %s.\n",
     520             :                 m->prog_name);
     521           0 :     GNUNET_assert (NULL == ru->t);
     522           0 :     ru->amlh = TALER_KYCLOGIC_run_aml_program3 (
     523           0 :       ru->is_wallet,
     524             :       m,
     525             :       &TALER_EXCHANGEDB_current_attributes_builder,
     526             :       &hbc,
     527             :       &TALER_EXCHANGEDB_current_rule_builder,
     528             :       &hbc,
     529             :       &TALER_EXCHANGEDB_aml_history_builder,
     530             :       &hbc,
     531             :       &TALER_EXCHANGEDB_kyc_history_builder,
     532             :       &hbc,
     533           0 :       ru->plugin->max_aml_program_runtime,
     534             :       &aml_result_callback,
     535             :       ru);
     536           0 :     return;
     537             :   }
     538             : 
     539             :   /* User MUST pass interactive check */
     540           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     541             :               "Measure %s involves check %s\n",
     542             :               m->measure_name,
     543             :               m->check_name);
     544             :   {
     545             :     /* activate the measure/check */
     546             :     json_t *succ_jmeasures
     547           0 :       = TALER_KYCLOGIC_get_jmeasures (
     548           0 :           ru->lrs,
     549           0 :           m->measure_name);
     550             :     bool unknown_account;
     551             :     struct GNUNET_TIME_Timestamp last_date;
     552             :     enum GNUNET_DB_QueryStatus qs;
     553             : 
     554           0 :     qs = ru->plugin->insert_successor_measure (
     555           0 :       ru->plugin->cls,
     556           0 :       &ru->account,
     557             :       GNUNET_TIME_timestamp_get (),
     558           0 :       m->measure_name,
     559             :       succ_jmeasures,
     560             :       &unknown_account,
     561             :       &last_date);
     562           0 :     json_decref (succ_jmeasures);
     563           0 :     switch (qs)
     564             :     {
     565           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     566           0 :       GNUNET_log (
     567             :         GNUNET_ERROR_TYPE_INFO,
     568             :         "Serialization issue!\n");
     569           0 :       fail_update (ru,
     570             :                    TALER_EC_GENERIC_DB_SOFT_FAILURE,
     571             :                    "insert_successor_measure");
     572           0 :       return;
     573           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     574           0 :       GNUNET_break (0);
     575           0 :       fail_update (ru,
     576             :                    TALER_EC_GENERIC_DB_STORE_FAILED,
     577             :                    "insert_successor_measure");
     578           0 :       return;
     579           0 :     default:
     580           0 :       break;
     581             :     }
     582           0 :     if (unknown_account)
     583             :     {
     584           0 :       fail_update (ru,
     585             :                    TALER_EC_EXCHANGE_GENERIC_BANK_ACCOUNT_UNKNOWN,
     586             :                    NULL);
     587           0 :       return;
     588             :     }
     589             :   }
     590             :   /* The rules remain these rules until the user passes the check */
     591           0 :   return_result (ru);
     592             : }
     593             : 
     594             : 
     595             : /**
     596             :  * Update the expired legitimization rules in @a ru, checking for expiration
     597             :  * first.  Called with an open database transaction.
     598             :  *
     599             :  * @param[in,out] ru account we are processing
     600             :  */
     601             : static void
     602           0 : update_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
     603             : {
     604             :   const struct TALER_KYCLOGIC_Measure *m;
     605             : 
     606           0 :   GNUNET_assert (NULL != ru->lrs);
     607           0 :   GNUNET_assert (GNUNET_TIME_absolute_is_past (
     608             :                    TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time));
     609           0 :   m = TALER_KYCLOGIC_rules_get_successor (ru->lrs);
     610           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     611             :               "Successor measure is %s.\n",
     612             :               (NULL != m) ? m->measure_name : "(null)");
     613           0 :   run_measure (ru,
     614             :                m);
     615           0 : }
     616             : 
     617             : 
     618             : static void
     619          28 : check_rules (struct TALER_EXCHANGEDB_RuleUpdater *ru)
     620             : {
     621          28 :   ru->depth++;
     622          28 :   if (ru->depth > MAX_DEPTH)
     623             :   {
     624           0 :     fail_update (ru,
     625             :                  TALER_EC_EXCHANGE_GENERIC_AML_PROGRAM_RECURSION_DETECTED,
     626             :                  NULL);
     627           0 :     return;
     628             :   }
     629          28 :   if (NULL == ru->lrs)
     630             :   {
     631             :     /* return NULL, aka default rules */
     632          11 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     633             :                 "Default rules apply\n");
     634          11 :     return_result (ru);
     635          11 :     return;
     636             :   }
     637          17 :   if (! GNUNET_TIME_absolute_is_past
     638          17 :         (TALER_KYCLOGIC_rules_get_expiration (ru->lrs).abs_time) )
     639             :   {
     640             :     /* Rules did not expire, return them! */
     641          17 :     return_result (ru);
     642          17 :     return;
     643             :   }
     644           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     645             :               "Custom rules expired, updating...\n");
     646           0 :   update_rules (ru);
     647             : }
     648             : 
     649             : 
     650             : static void
     651          28 : fetch_latest_rules (void *cls)
     652             : {
     653          28 :   struct TALER_EXCHANGEDB_RuleUpdater *ru = cls;
     654             :   enum GNUNET_DB_QueryStatus qs;
     655             :   json_t *jnew_rules;
     656             :   enum GNUNET_GenericReturnValue res;
     657             : 
     658          28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     659             :               "Fetching latest rules.");
     660             : 
     661          28 :   ru->t = NULL;
     662          28 :   if (NULL != ru->eh)
     663             :   {
     664             :     /* cancel event listener, if we have one */
     665           0 :     ru->plugin->event_listen_cancel (ru->plugin->cls,
     666             :                                      ru->eh);
     667           0 :     ru->eh = NULL;
     668             :   }
     669          28 :   GNUNET_break (NULL == ru->lrs);
     670          28 :   res = ru->plugin->start (ru->plugin->cls,
     671             :                            "aml-begin-lookup-rules-by-access-token");
     672          28 :   if (GNUNET_OK != res)
     673             :   {
     674           0 :     GNUNET_break (0);
     675           0 :     fail_update (ru,
     676             :                  TALER_EC_GENERIC_DB_START_FAILED,
     677             :                  "aml-begin-lookup-rules-by-access-token");
     678           0 :     return;
     679             :   }
     680          28 :   qs = ru->plugin->lookup_rules_by_access_token (
     681          28 :     ru->plugin->cls,
     682          28 :     &ru->account,
     683             :     &jnew_rules,
     684             :     &ru->legitimization_outcome_last_row);
     685          28 :   if (qs < 0)
     686             :   {
     687           0 :     GNUNET_break (0);
     688           0 :     fail_update (ru,
     689             :                  TALER_EC_GENERIC_DB_FETCH_FAILED,
     690             :                  "lookup_rules_by_access_token");
     691           0 :     return;
     692             :   }
     693          28 :   if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
     694          17 :        (NULL != jnew_rules) )
     695             :   {
     696          17 :     ru->lrs = TALER_KYCLOGIC_rules_parse (jnew_rules);
     697          17 :     GNUNET_break (NULL != ru->lrs);
     698          17 :     json_decref (jnew_rules);
     699             :   }
     700          28 :   check_rules (ru);
     701             : }
     702             : 
     703             : 
     704             : struct TALER_EXCHANGEDB_RuleUpdater *
     705          28 : TALER_EXCHANGEDB_update_rules (
     706             :   struct TALER_EXCHANGEDB_Plugin *plugin,
     707             :   const struct TALER_AttributeEncryptionKeyP *attribute_key,
     708             :   const struct TALER_NormalizedPaytoHashP *account,
     709             :   bool is_wallet,
     710             :   TALER_EXCHANGEDB_CurrentRulesCallback cb,
     711             :   void *cb_cls)
     712             : {
     713             :   struct TALER_EXCHANGEDB_RuleUpdater *ru;
     714             : 
     715          28 :   ru = GNUNET_new (struct TALER_EXCHANGEDB_RuleUpdater);
     716          28 :   ru->plugin = plugin;
     717          28 :   ru->attribute_key = *attribute_key;
     718          28 :   ru->account = *account;
     719          28 :   ru->is_wallet = is_wallet;
     720          28 :   ru->cb = cb;
     721          28 :   ru->cb_cls = cb_cls;
     722          28 :   ru->t = GNUNET_SCHEDULER_add_now (&fetch_latest_rules,
     723             :                                     ru);
     724          28 :   return ru;
     725             : }
     726             : 
     727             : 
     728             : void
     729          28 : TALER_EXCHANGEDB_update_rules_cancel (
     730             :   struct TALER_EXCHANGEDB_RuleUpdater *ru)
     731             : {
     732          28 :   if (NULL != ru->t)
     733             :   {
     734           0 :     GNUNET_SCHEDULER_cancel (ru->t);
     735           0 :     ru->t = NULL;
     736             :   }
     737          28 :   if (NULL != ru->amlh)
     738             :   {
     739           0 :     TALER_KYCLOGIC_run_aml_program_cancel (ru->amlh);
     740           0 :     ru->amlh = NULL;
     741             :   }
     742          28 :   if (NULL != ru->lrs)
     743             :   {
     744           0 :     TALER_KYCLOGIC_rules_free (ru->lrs);
     745           0 :     ru->lrs = NULL;
     746             :   }
     747          28 :   if (NULL != ru->eh)
     748             :   {
     749           0 :     ru->plugin->event_listen_cancel (ru->plugin->cls,
     750             :                                      ru->eh);
     751           0 :     ru->eh = NULL;
     752             :   }
     753          28 :   GNUNET_free (ru->aml_program_name);
     754          28 :   GNUNET_free (ru);
     755          28 : }

Generated by: LCOV version 1.16