merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_private-post-account.c (11656B)


      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 TMH_private_post_account (const struct TMH_RequestHandler *rh,
     37                           struct MHD_Connection *connection,
     38                           struct TMH_HandlerContext *hc)
     39 {
     40   struct TMH_MerchantInstance *mi = hc->instance;
     41   const char *credit_facade_url = NULL;
     42   const json_t *credit_facade_credentials = NULL;
     43   struct TALER_FullPayto uri;
     44   struct GNUNET_JSON_Specification ispec[] = {
     45     TALER_JSON_spec_full_payto_uri ("payto_uri",
     46                                     &uri),
     47     GNUNET_JSON_spec_mark_optional (
     48       TALER_JSON_spec_web_url ("credit_facade_url",
     49                                &credit_facade_url),
     50       NULL),
     51     GNUNET_JSON_spec_mark_optional (
     52       GNUNET_JSON_spec_object_const ("credit_facade_credentials",
     53                                      &credit_facade_credentials),
     54       NULL),
     55     GNUNET_JSON_spec_end ()
     56   };
     57   struct TMH_WireMethod *wm;
     58 
     59   {
     60     enum GNUNET_GenericReturnValue res;
     61 
     62     res = TALER_MHD_parse_json_data (connection,
     63                                      hc->request_body,
     64                                      ispec);
     65     if (GNUNET_OK != res)
     66       return (GNUNET_NO == res)
     67              ? MHD_YES
     68              : MHD_NO;
     69   }
     70 
     71   {
     72     char *err;
     73 
     74     if (NULL !=
     75         (err = TALER_payto_validate (uri)))
     76     {
     77       MHD_RESULT mret;
     78 
     79       GNUNET_break_op (0);
     80       mret = TALER_MHD_reply_with_error (connection,
     81                                          MHD_HTTP_BAD_REQUEST,
     82                                          TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
     83                                          err);
     84       GNUNET_free (err);
     85       return mret;
     86     }
     87   }
     88   {
     89     char *apt = GNUNET_strdup (TMH_allowed_payment_targets);
     90     char *method = TALER_payto_get_method (uri.full_payto);
     91     bool ok;
     92 
     93     ok = false;
     94     for (const char *tok = strtok (apt,
     95                                    " ");
     96          NULL != tok;
     97          tok = strtok (NULL,
     98                        " "))
     99     {
    100       if (0 == strcmp ("*",
    101                        tok))
    102         ok = true;
    103       if (0 == strcmp (method,
    104                        tok))
    105         ok = true;
    106       if (ok)
    107         break;
    108     }
    109     GNUNET_free (method);
    110     GNUNET_free (apt);
    111     if (! ok)
    112     {
    113       GNUNET_break_op (0);
    114       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   if ( (NULL != TMH_payment_target_regex) &&
    122        (0 !=
    123         regexec (&TMH_payment_target_re,
    124                  uri.full_payto,
    125                  0,
    126                  NULL,
    127                  0)) )
    128   {
    129     GNUNET_break_op (0);
    130     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   if ( (NULL == credit_facade_url) !=
    137        (NULL == credit_facade_credentials) )
    138   {
    139     GNUNET_break_op (0);
    140     return TALER_MHD_reply_with_error (connection,
    141                                        MHD_HTTP_BAD_REQUEST,
    142                                        TALER_EC_GENERIC_PARAMETER_MISSING,
    143                                        (NULL == credit_facade_url)
    144                                        ? "credit_facade_url"
    145                                        : "credit_facade_credentials");
    146   }
    147   if ( (NULL != credit_facade_url) ||
    148        (NULL != credit_facade_credentials) )
    149   {
    150     struct TALER_MERCHANT_BANK_AuthenticationData auth;
    151 
    152     if (GNUNET_OK !=
    153         TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials,
    154                                              credit_facade_url,
    155                                              &auth))
    156     {
    157       GNUNET_break_op (0);
    158       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     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     qs = TMH_db->select_accounts (TMH_db->cls,
    172                                   mi->settings.id,
    173                                   NULL,
    174                                   NULL);
    175     switch (qs)
    176     {
    177     case GNUNET_DB_STATUS_SOFT_ERROR:
    178     case GNUNET_DB_STATUS_HARD_ERROR:
    179       GNUNET_break (0);
    180       return TALER_MHD_reply_with_error (connection,
    181                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    182                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    183                                          "select_accounts");
    184     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    185       /* skip MFA */
    186       break;
    187     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    188       ret = TMH_mfa_check_simple (hc,
    189                                   TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION,
    190                                   mi);
    191 
    192       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    193                   "Account creation MFA check returned %d\n",
    194                   (int) ret);
    195       if (GNUNET_OK != ret)
    196       {
    197         return (GNUNET_NO == ret)
    198         ? MHD_YES
    199         : MHD_NO;
    200       }
    201     }
    202   }
    203 
    204   /* convert provided payto URI into internal data structure with salts */
    205   wm = TMH_setup_wire_account (uri,
    206                                credit_facade_url,
    207                                credit_facade_credentials);
    208   GNUNET_assert (NULL != wm);
    209   {
    210     struct TALER_MERCHANTDB_AccountDetails ad = {
    211       .payto_uri = wm->payto_uri,
    212       .salt = wm->wire_salt,
    213       .instance_id = mi->settings.id,
    214       .h_wire = wm->h_wire,
    215       .credit_facade_url = wm->credit_facade_url,
    216       .credit_facade_credentials = wm->credit_facade_credentials,
    217       .active = wm->active
    218     };
    219     enum GNUNET_DB_QueryStatus qs;
    220 
    221     qs = TMH_db->insert_account (TMH_db->cls,
    222                                  &ad);
    223     switch (qs)
    224     {
    225     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    226       break;
    227     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    228       /* conflict: account exists */
    229       {
    230         struct TALER_MERCHANTDB_AccountDetails adx;
    231 
    232         qs = TMH_db->select_account_by_uri (TMH_db->cls,
    233                                             mi->settings.id,
    234                                             ad.payto_uri,
    235                                             &adx);
    236         switch (qs)
    237         {
    238         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    239           if ( (0 == TALER_full_payto_cmp (adx.payto_uri,
    240                                            ad.payto_uri) ) &&
    241                ( (adx.credit_facade_credentials ==
    242                   ad.credit_facade_credentials) ||
    243                  ( (NULL != adx.credit_facade_credentials) &&
    244                    (NULL != ad.credit_facade_credentials) &&
    245                    (1 == json_equal (adx.credit_facade_credentials,
    246                                      ad.credit_facade_credentials)) ) ) &&
    247                ( (adx.credit_facade_url == ad.credit_facade_url) ||
    248                  ( (NULL != adx.credit_facade_url) &&
    249                    (NULL != ad.credit_facade_url) &&
    250                    (0 == strcmp (adx.credit_facade_url,
    251                                  ad.credit_facade_url)) ) ) )
    252           {
    253             TMH_wire_method_free (wm);
    254             GNUNET_free (adx.payto_uri.full_payto);
    255             GNUNET_free (adx.credit_facade_url);
    256             json_decref (adx.credit_facade_credentials);
    257             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           GNUNET_free (adx.payto_uri.full_payto);
    268           GNUNET_free (adx.credit_facade_url);
    269           json_decref (adx.credit_facade_credentials);
    270           break;
    271         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    272         case GNUNET_DB_STATUS_SOFT_ERROR:
    273         case GNUNET_DB_STATUS_HARD_ERROR:
    274           GNUNET_break (0);
    275           TMH_wire_method_free (wm);
    276           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       TMH_wire_method_free (wm);
    283       return TALER_MHD_reply_with_error (connection,
    284                                          MHD_HTTP_CONFLICT,
    285                                          TALER_EC_MERCHANT_PRIVATE_ACCOUNT_EXISTS,
    286                                          uri.full_payto);
    287     case GNUNET_DB_STATUS_SOFT_ERROR:
    288     case GNUNET_DB_STATUS_HARD_ERROR:
    289       GNUNET_break (0);
    290       TMH_wire_method_free (wm);
    291       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     struct GNUNET_DB_EventHeaderP es = {
    300       .size = htons (sizeof (es)),
    301       .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
    302     };
    303 
    304     TMH_db->event_notify (TMH_db->cls,
    305                           &es,
    306                           NULL,
    307                           0);
    308   }
    309   /* Finally, also update our running process */
    310   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   TMH_reload_instances (mi->settings.id);
    316   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 */