LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-account.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 50.0 % 120 60
Test Date: 2025-11-06 19:31:41 Functions: 100.0 % 1 1

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2020-2024 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify
       6              :   it under the terms of the GNU Affero General Public License as
       7              :   published by the Free Software Foundation; either version 3,
       8              :   or (at your 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
      13              :   GNU 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,
      17              :   see <http://www.gnu.org/licenses/>
      18              : */
      19              : 
      20              : /**
      21              :  * @file taler-merchant-httpd_private-post-account.c
      22              :  * @brief implementing POST /private/accounts request handling
      23              :  * @author Christian Grothoff
      24              :  */
      25              : #include "platform.h"
      26              : #include "taler-merchant-httpd_private-post-account.h"
      27              : #include "taler-merchant-httpd_helper.h"
      28              : #include "taler_merchant_bank_lib.h"
      29              : #include <taler/taler_dbevents.h>
      30              : #include <taler/taler_json_lib.h>
      31              : #include "taler-merchant-httpd_mfa.h"
      32              : #include <regex.h>
      33              : 
      34              : 
      35              : MHD_RESULT
      36           21 : TMH_private_post_account (const struct TMH_RequestHandler *rh,
      37              :                           struct MHD_Connection *connection,
      38              :                           struct TMH_HandlerContext *hc)
      39              : {
      40           21 :   struct TMH_MerchantInstance *mi = hc->instance;
      41           21 :   const char *credit_facade_url = NULL;
      42           21 :   const json_t *credit_facade_credentials = NULL;
      43              :   struct TALER_FullPayto uri;
      44              :   struct GNUNET_JSON_Specification ispec[] = {
      45           21 :     TALER_JSON_spec_full_payto_uri ("payto_uri",
      46              :                                     &uri),
      47           21 :     GNUNET_JSON_spec_mark_optional (
      48              :       TALER_JSON_spec_web_url ("credit_facade_url",
      49              :                                &credit_facade_url),
      50              :       NULL),
      51           21 :     GNUNET_JSON_spec_mark_optional (
      52              :       GNUNET_JSON_spec_object_const ("credit_facade_credentials",
      53              :                                      &credit_facade_credentials),
      54              :       NULL),
      55           21 :     GNUNET_JSON_spec_end ()
      56              :   };
      57              :   struct TMH_WireMethod *wm;
      58              : 
      59              :   {
      60              :     enum GNUNET_GenericReturnValue res;
      61              : 
      62           21 :     res = TALER_MHD_parse_json_data (connection,
      63           21 :                                      hc->request_body,
      64              :                                      ispec);
      65           21 :     if (GNUNET_OK != res)
      66              :       return (GNUNET_NO == res)
      67              :              ? MHD_YES
      68            0 :              : MHD_NO;
      69              :   }
      70              : 
      71              :   {
      72              :     char *err;
      73              : 
      74           21 :     if (NULL !=
      75           21 :         (err = TALER_payto_validate (uri)))
      76              :     {
      77              :       MHD_RESULT mret;
      78              : 
      79            0 :       GNUNET_break_op (0);
      80            0 :       mret = TALER_MHD_reply_with_error (connection,
      81              :                                          MHD_HTTP_BAD_REQUEST,
      82              :                                          TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
      83              :                                          err);
      84            0 :       GNUNET_free (err);
      85            0 :       return mret;
      86              :     }
      87              :   }
      88              :   {
      89           21 :     char *apt = GNUNET_strdup (TMH_allowed_payment_targets);
      90           21 :     char *method = TALER_payto_get_method (uri.full_payto);
      91              :     bool ok;
      92              : 
      93           21 :     ok = false;
      94           21 :     for (const char *tok = strtok (apt,
      95              :                                    " ");
      96           21 :          NULL != tok;
      97            0 :          tok = strtok (NULL,
      98              :                        " "))
      99              :     {
     100           21 :       if (0 == strcmp ("*",
     101              :                        tok))
     102           21 :         ok = true;
     103           21 :       if (0 == strcmp (method,
     104              :                        tok))
     105            0 :         ok = true;
     106           21 :       if (ok)
     107           21 :         break;
     108              :     }
     109           21 :     GNUNET_free (method);
     110           21 :     GNUNET_free (apt);
     111           21 :     if (! ok)
     112              :     {
     113            0 :       GNUNET_break_op (0);
     114            0 :       return TALER_MHD_reply_with_error (connection,
     115              :                                          MHD_HTTP_BAD_REQUEST,
     116              :                                          TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
     117              :                                          "The payment target type is forbidden by policy");
     118              :     }
     119              :   }
     120              : 
     121           21 :   if ( (NULL != TMH_payment_target_regex) &&
     122              :        (0 !=
     123            0 :         regexec (&TMH_payment_target_re,
     124            0 :                  uri.full_payto,
     125              :                  0,
     126              :                  NULL,
     127              :                  0)) )
     128              :   {
     129            0 :     GNUNET_break_op (0);
     130            0 :     return TALER_MHD_reply_with_error (connection,
     131              :                                        MHD_HTTP_BAD_REQUEST,
     132              :                                        TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
     133              :                                        "The specific account is forbidden by policy");
     134              :   }
     135              : 
     136           21 :   if ( (NULL == credit_facade_url) !=
     137              :        (NULL == credit_facade_credentials) )
     138              :   {
     139            0 :     GNUNET_break_op (0);
     140            0 :     return TALER_MHD_reply_with_error (connection,
     141              :                                        MHD_HTTP_BAD_REQUEST,
     142              :                                        TALER_EC_GENERIC_PARAMETER_MISSING,
     143            0 :                                        (NULL == credit_facade_url)
     144              :                                        ? "credit_facade_url"
     145              :                                        : "credit_facade_credentials");
     146              :   }
     147           21 :   if ( (NULL != credit_facade_url) ||
     148           19 :        (NULL != credit_facade_credentials) )
     149              :   {
     150              :     struct TALER_MERCHANT_BANK_AuthenticationData auth;
     151              : 
     152            2 :     if (GNUNET_OK !=
     153            2 :         TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
     154              :                                              credit_facade_url,
     155              :                                              &auth))
     156              :     {
     157            0 :       GNUNET_break_op (0);
     158            0 :       return TALER_MHD_reply_with_error (connection,
     159              :                                          MHD_HTTP_BAD_REQUEST,
     160              :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     161              :                                          "credit_facade_url or credit_facade_credentials");
     162              :     }
     163            2 :     TALER_MERCHANT_BANK_auth_free (&auth);
     164              :   }
     165              : 
     166              :   /* FIXME: might want to do all of the DB interactions below in one transaction... */
     167              :   {
     168              :     enum GNUNET_DB_QueryStatus qs;
     169              :     enum GNUNET_GenericReturnValue ret;
     170              : 
     171           21 :     qs = TMH_db->select_accounts (TMH_db->cls,
     172           21 :                                   mi->settings.id,
     173              :                                   NULL,
     174              :                                   NULL);
     175           21 :     switch (qs)
     176              :     {
     177            0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     178              :     case GNUNET_DB_STATUS_HARD_ERROR:
     179            0 :       GNUNET_break (0);
     180            0 :       return TALER_MHD_reply_with_error (connection,
     181              :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     182              :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
     183              :                                          "select_accounts");
     184           18 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     185              :       /* skip MFA */
     186           18 :       break;
     187            3 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     188            3 :       ret = TMH_mfa_check_simple (hc,
     189              :                                   TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION,
     190              :                                   mi);
     191              : 
     192            3 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     193              :                   "Account creation MFA check returned %d\n",
     194              :                   (int) ret);
     195            3 :       if (GNUNET_OK != ret)
     196              :       {
     197              :         return (GNUNET_NO == ret)
     198              :         ? MHD_YES
     199            0 :         : MHD_NO;
     200              :       }
     201              :     }
     202              :   }
     203              : 
     204              :   /* convert provided payto URI into internal data structure with salts */
     205           21 :   wm = TMH_setup_wire_account (uri,
     206              :                                credit_facade_url,
     207              :                                credit_facade_credentials);
     208           21 :   GNUNET_assert (NULL != wm);
     209              :   {
     210           21 :     struct TALER_MERCHANTDB_AccountDetails ad = {
     211              :       .payto_uri = wm->payto_uri,
     212              :       .salt = wm->wire_salt,
     213           21 :       .instance_id = mi->settings.id,
     214              :       .h_wire = wm->h_wire,
     215           21 :       .credit_facade_url = wm->credit_facade_url,
     216           21 :       .credit_facade_credentials = wm->credit_facade_credentials,
     217           21 :       .active = wm->active
     218              :     };
     219              :     enum GNUNET_DB_QueryStatus qs;
     220              : 
     221           21 :     qs = TMH_db->insert_account (TMH_db->cls,
     222              :                                  &ad);
     223           21 :     switch (qs)
     224              :     {
     225           21 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     226           21 :       break;
     227            0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     228              :       /* conflict: account exists */
     229              :       {
     230              :         struct TALER_MERCHANTDB_AccountDetails adx;
     231              : 
     232            0 :         qs = TMH_db->select_account_by_uri (TMH_db->cls,
     233            0 :                                             mi->settings.id,
     234              :                                             ad.payto_uri,
     235              :                                             &adx);
     236            0 :         switch (qs)
     237              :         {
     238            0 :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     239            0 :           if ( (0 == TALER_full_payto_cmp (adx.payto_uri,
     240            0 :                                            ad.payto_uri) ) &&
     241            0 :                ( (adx.credit_facade_credentials ==
     242            0 :                   ad.credit_facade_credentials) ||
     243            0 :                  ( (NULL != adx.credit_facade_credentials) &&
     244            0 :                    (NULL != ad.credit_facade_credentials) &&
     245            0 :                    (1 == json_equal (adx.credit_facade_credentials,
     246            0 :                                      ad.credit_facade_credentials)) ) ) &&
     247            0 :                ( (adx.credit_facade_url == ad.credit_facade_url) ||
     248            0 :                  ( (NULL != adx.credit_facade_url) &&
     249            0 :                    (NULL != ad.credit_facade_url) &&
     250            0 :                    (0 == strcmp (adx.credit_facade_url,
     251            0 :                                  ad.credit_facade_url)) ) ) )
     252              :           {
     253            0 :             TMH_wire_method_free (wm);
     254            0 :             GNUNET_free (adx.payto_uri.full_payto);
     255            0 :             GNUNET_free (adx.credit_facade_url);
     256            0 :             json_decref (adx.credit_facade_credentials);
     257            0 :             return TALER_MHD_REPLY_JSON_PACK (
     258              :               connection,
     259              :               MHD_HTTP_OK,
     260              :               GNUNET_JSON_pack_data_auto (
     261              :                 "salt",
     262              :                 &adx.salt),
     263              :               GNUNET_JSON_pack_data_auto (
     264              :                 "h_wire",
     265              :                 &adx.h_wire));
     266              :           }
     267            0 :           GNUNET_free (adx.payto_uri.full_payto);
     268            0 :           GNUNET_free (adx.credit_facade_url);
     269            0 :           json_decref (adx.credit_facade_credentials);
     270            0 :           break;
     271            0 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     272              :         case GNUNET_DB_STATUS_SOFT_ERROR:
     273              :         case GNUNET_DB_STATUS_HARD_ERROR:
     274            0 :           GNUNET_break (0);
     275            0 :           TMH_wire_method_free (wm);
     276            0 :           return TALER_MHD_reply_with_error (connection,
     277              :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     278              :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
     279              :                                              "select_account");
     280              :         }
     281              :       }
     282            0 :       TMH_wire_method_free (wm);
     283            0 :       return TALER_MHD_reply_with_error (connection,
     284              :                                          MHD_HTTP_CONFLICT,
     285              :                                          TALER_EC_MERCHANT_PRIVATE_ACCOUNT_EXISTS,
     286            0 :                                          uri.full_payto);
     287            0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
     288              :     case GNUNET_DB_STATUS_HARD_ERROR:
     289            0 :       GNUNET_break (0);
     290            0 :       TMH_wire_method_free (wm);
     291            0 :       return TALER_MHD_reply_with_error (connection,
     292              :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     293              :                                          TALER_EC_GENERIC_DB_STORE_FAILED,
     294              :                                          "insert_account");
     295              :     }
     296              :   }
     297              : 
     298              :   {
     299           21 :     struct GNUNET_DB_EventHeaderP es = {
     300           21 :       .size = htons (sizeof (es)),
     301           21 :       .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
     302              :     };
     303              : 
     304           21 :     TMH_db->event_notify (TMH_db->cls,
     305              :                           &es,
     306              :                           NULL,
     307              :                           0);
     308              :   }
     309              :   /* Finally, also update our running process */
     310           21 :   GNUNET_CONTAINER_DLL_insert (mi->wm_head,
     311              :                                mi->wm_tail,
     312              :                                wm);
     313              :   /* Note: we may not need to do this, as we notified
     314              :      about the account change above. But also hardly hurts. */
     315           21 :   TMH_reload_instances (mi->settings.id);
     316           21 :   return TALER_MHD_REPLY_JSON_PACK (connection,
     317              :                                     MHD_HTTP_OK,
     318              :                                     GNUNET_JSON_pack_data_auto ("salt",
     319              :                                                                 &wm->wire_salt),
     320              :                                     GNUNET_JSON_pack_data_auto ("h_wire",
     321              :                                                                 &wm->h_wire));
     322              : }
     323              : 
     324              : 
     325              : /* end of taler-merchant-httpd_private-post-account.c */
        

Generated by: LCOV version 2.0-1