merchant

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

taler-merchant-httpd_post-management-instances.c (26263B)


      1 /*
      2   This file is part of TALER
      3   (C) 2020-2025 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 src/backend/taler-merchant-httpd_post-management-instances.c
     22  * @brief implementing POST /instances request handling
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "taler-merchant-httpd_post-management-instances.h"
     27 #include "taler-merchant-httpd_helper.h"
     28 #include "taler-merchant-httpd.h"
     29 #include "taler-merchant-httpd_auth.h"
     30 #include "taler-merchant-httpd_mfa.h"
     31 #include "taler/taler_merchant_bank_lib.h"
     32 #include <taler/taler_dbevents.h>
     33 #include <taler/taler_json_lib.h>
     34 #include <regex.h>
     35 #include "merchant-database/insert_instance.h"
     36 #include "merchant-database/set_instance.h"
     37 #include "merchant-database/insert_login_token.h"
     38 #include "merchant-database/start.h"
     39 
     40 /**
     41  * How often do we retry the simple INSERT database transaction?
     42  */
     43 #define MAX_RETRIES 3
     44 
     45 
     46 /**
     47  * Generate an instance, given its configuration.
     48  *
     49  * @param rh context of the handler
     50  * @param connection the MHD connection to handle
     51  * @param[in,out] hc context with further information about the request
     52  * @param login_token_expiration set to how long a login token validity
     53  *   should be, use zero if no login token should be created
     54  * @param validation_needed true if self-provisioned and
     55  *   email/phone registration is required before the
     56  *   instance can become fully active
     57  * @return MHD result code
     58  */
     59 static enum MHD_Result
     60 post_instances (const struct TMH_RequestHandler *rh,
     61                 struct MHD_Connection *connection,
     62                 struct TMH_HandlerContext *hc,
     63                 struct GNUNET_TIME_Relative login_token_expiration,
     64                 bool validation_needed)
     65 {
     66   struct TALER_MERCHANTDB_InstanceSettings is = { 0 };
     67   struct TALER_MERCHANTDB_InstanceAuthSettings ias;
     68   const char *auth_password = NULL;
     69   struct TMH_WireMethod *wm_head = NULL;
     70   struct TMH_WireMethod *wm_tail = NULL;
     71   const json_t *jauth;
     72   const char *iphone = NULL;
     73   char *id_lower;
     74   bool no_pay_delay;
     75   bool no_refund_delay;
     76   bool no_transfer_delay;
     77   bool no_rounding_interval;
     78   struct GNUNET_JSON_Specification spec[] = {
     79     TALER_JSON_spec_slug ("id",
     80                           (const char **) &is.id),
     81     GNUNET_JSON_spec_string ("name",
     82                              (const char **) &is.name),
     83     GNUNET_JSON_spec_mark_optional (
     84       GNUNET_JSON_spec_string ("email",
     85                                (const char **) &is.email),
     86       NULL),
     87     GNUNET_JSON_spec_mark_optional (
     88       GNUNET_JSON_spec_string ("phone_number",
     89                                &iphone),
     90       NULL),
     91     GNUNET_JSON_spec_mark_optional (
     92       GNUNET_JSON_spec_string ("website",
     93                                (const char **) &is.website),
     94       NULL),
     95     GNUNET_JSON_spec_mark_optional (
     96       GNUNET_JSON_spec_string ("logo",
     97                                (const char **) &is.logo),
     98       NULL),
     99     GNUNET_JSON_spec_object_const ("auth",
    100                                    &jauth),
    101     GNUNET_JSON_spec_json ("address",
    102                            &is.address),
    103     GNUNET_JSON_spec_json ("jurisdiction",
    104                            &is.jurisdiction),
    105     GNUNET_JSON_spec_bool ("use_stefan",
    106                            &is.use_stefan),
    107     GNUNET_JSON_spec_mark_optional (
    108       GNUNET_JSON_spec_relative_time ("default_pay_delay",
    109                                       &is.default_pay_delay),
    110       &no_pay_delay),
    111     GNUNET_JSON_spec_mark_optional (
    112       GNUNET_JSON_spec_relative_time ("default_refund_delay",
    113                                       &is.default_refund_delay),
    114       &no_refund_delay),
    115     GNUNET_JSON_spec_mark_optional (
    116       GNUNET_JSON_spec_relative_time ("default_wire_transfer_delay",
    117                                       &is.default_wire_transfer_delay),
    118       &no_transfer_delay),
    119     GNUNET_JSON_spec_mark_optional (
    120       GNUNET_JSON_spec_time_rounder_interval (
    121         "default_wire_transfer_rounding_interval",
    122         &is.default_wire_transfer_rounding_interval),
    123       &no_rounding_interval),
    124     GNUNET_JSON_spec_end ()
    125   };
    126 
    127   {
    128     enum GNUNET_GenericReturnValue res;
    129 
    130     res = TALER_MHD_parse_json_data (connection,
    131                                      hc->request_body,
    132                                      spec);
    133     if (GNUNET_OK != res)
    134       return (GNUNET_NO == res)
    135              ? MHD_YES
    136              : MHD_NO;
    137   }
    138   if (no_pay_delay)
    139     is.default_pay_delay = TMH_default_pay_delay;
    140   if (no_refund_delay)
    141     is.default_refund_delay = TMH_default_refund_delay;
    142   if (no_transfer_delay)
    143     is.default_wire_transfer_delay = TMH_default_wire_transfer_delay;
    144   if (no_rounding_interval)
    145     is.default_wire_transfer_rounding_interval
    146       = TMH_default_wire_transfer_rounding_interval;
    147   if (GNUNET_TIME_relative_is_forever (is.default_pay_delay))
    148   {
    149     GNUNET_break_op (0);
    150     GNUNET_JSON_parse_free (spec);
    151     return TALER_MHD_reply_with_error (connection,
    152                                        MHD_HTTP_BAD_REQUEST,
    153                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    154                                        "default_pay_delay");
    155   }
    156   if (GNUNET_TIME_relative_is_forever (is.default_refund_delay))
    157   {
    158     GNUNET_break_op (0);
    159     GNUNET_JSON_parse_free (spec);
    160     return TALER_MHD_reply_with_error (connection,
    161                                        MHD_HTTP_BAD_REQUEST,
    162                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    163                                        "default_refund_delay");
    164   }
    165   if (GNUNET_TIME_relative_is_forever (is.default_wire_transfer_delay))
    166   {
    167     GNUNET_break_op (0);
    168     GNUNET_JSON_parse_free (spec);
    169     return TALER_MHD_reply_with_error (connection,
    170                                        MHD_HTTP_BAD_REQUEST,
    171                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    172                                        "default_wire_transfer_delay");
    173   }
    174   if (NULL != iphone)
    175   {
    176     is.phone = TALER_MERCHANT_phone_validate_normalize (iphone,
    177                                                         false);
    178     if (NULL == is.phone)
    179     {
    180       GNUNET_break_op (0);
    181       GNUNET_JSON_parse_free (spec);
    182       return TALER_MHD_reply_with_error (connection,
    183                                          MHD_HTTP_BAD_REQUEST,
    184                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    185                                          "phone_number");
    186     }
    187     if ( (NULL != TMH_phone_regex) &&
    188          (0 !=
    189           regexec (&TMH_phone_rx,
    190                    is.phone,
    191                    0,
    192                    NULL,
    193                    0)) )
    194     {
    195       GNUNET_break_op (0);
    196       GNUNET_JSON_parse_free (spec);
    197       return TALER_MHD_reply_with_error (connection,
    198                                          MHD_HTTP_BAD_REQUEST,
    199                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    200                                          "phone_number");
    201     }
    202   }
    203   if ( (NULL != is.email) &&
    204        (! TALER_MERCHANT_email_valid (is.email)) )
    205   {
    206     GNUNET_break_op (0);
    207     GNUNET_JSON_parse_free (spec);
    208     GNUNET_free (is.phone);
    209     return TALER_MHD_reply_with_error (connection,
    210                                        MHD_HTTP_BAD_REQUEST,
    211                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    212                                        "email");
    213   }
    214 
    215   {
    216     enum GNUNET_GenericReturnValue ret;
    217 
    218     ret = TMH_check_auth_config (connection,
    219                                  jauth,
    220                                  &auth_password);
    221     if (GNUNET_OK != ret)
    222     {
    223       GNUNET_free (is.phone);
    224       GNUNET_JSON_parse_free (spec);
    225       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    226     }
    227   }
    228 
    229   /* check 'id' well-formed */
    230   {
    231     static bool once;
    232     static regex_t reg;
    233     bool id_wellformed = true;
    234 
    235     if (! once)
    236     {
    237       once = true;
    238       GNUNET_assert (0 ==
    239                      regcomp (&reg,
    240                               "^[A-Za-z0-9][A-Za-z0-9_.@-]+$",
    241                               REG_EXTENDED));
    242     }
    243 
    244     if (0 != regexec (&reg,
    245                       is.id,
    246                       0, NULL, 0))
    247       id_wellformed = false;
    248     if (! id_wellformed)
    249     {
    250       GNUNET_JSON_parse_free (spec);
    251       GNUNET_free (is.phone);
    252       return TALER_MHD_reply_with_error (connection,
    253                                          MHD_HTTP_BAD_REQUEST,
    254                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
    255                                          "id");
    256     }
    257   }
    258 
    259   if (! TMH_location_object_valid (is.address))
    260   {
    261     GNUNET_break_op (0);
    262     GNUNET_JSON_parse_free (spec);
    263     GNUNET_free (is.phone);
    264     return TALER_MHD_reply_with_error (connection,
    265                                        MHD_HTTP_BAD_REQUEST,
    266                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    267                                        "address");
    268   }
    269 
    270   if (! TMH_location_object_valid (is.jurisdiction))
    271   {
    272     GNUNET_break_op (0);
    273     GNUNET_JSON_parse_free (spec);
    274     GNUNET_free (is.phone);
    275     return TALER_MHD_reply_with_error (connection,
    276                                        MHD_HTTP_BAD_REQUEST,
    277                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    278                                        "jurisdiction");
    279   }
    280 
    281   if ( (NULL != is.logo) &&
    282        (! TALER_MERCHANT_image_data_url_valid (is.logo)) )
    283   {
    284     GNUNET_break_op (0);
    285     GNUNET_JSON_parse_free (spec);
    286     GNUNET_free (is.phone);
    287     return TALER_MHD_reply_with_error (connection,
    288                                        MHD_HTTP_BAD_REQUEST,
    289                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    290                                        "logo");
    291   }
    292 
    293   id_lower = GNUNET_STRINGS_utf8_tolower (is.id);
    294   {
    295     /* Test if an instance of this id is known */
    296     struct TMH_MerchantInstance *mi;
    297 
    298     mi = TMH_lookup_instance (id_lower);
    299     if (NULL != mi)
    300     {
    301       enum MHD_Result ret;
    302 
    303       if (mi->deleted)
    304       {
    305         GNUNET_JSON_parse_free (spec);
    306         GNUNET_free (is.phone);
    307         GNUNET_free (id_lower);
    308         return TALER_MHD_reply_with_error (
    309           connection,
    310           MHD_HTTP_CONFLICT,
    311           TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_PURGE_REQUIRED,
    312           is.id);
    313       }
    314       /* Check for idempotency */
    315       if ( (0 == strcmp (mi->settings.id,
    316                          id_lower)) &&
    317            (0 == strcmp (mi->settings.name,
    318                          is.name)) &&
    319            ((mi->settings.email == is.email) ||
    320             (NULL != is.email && NULL != mi->settings.email &&
    321              0 == strcmp (mi->settings.email,
    322                           is.email))) &&
    323            ((mi->settings.website == is.website) ||
    324             (NULL != is.website && NULL != mi->settings.website &&
    325              0 == strcmp (mi->settings.website,
    326                           is.website))) &&
    327            ((mi->settings.logo == is.logo) ||
    328             (NULL != is.logo && NULL != mi->settings.logo &&
    329              0 == strcmp (mi->settings.logo,
    330                           is.logo))) &&
    331            ( ( (NULL != auth_password) &&
    332                (GNUNET_OK ==
    333                 TMH_check_auth (auth_password,
    334                                 &mi->auth.auth_salt,
    335                                 &mi->auth.auth_hash)) ) ||
    336              ( (NULL == auth_password) &&
    337                (GNUNET_YES ==
    338                 GNUNET_is_zero (&mi->auth.auth_hash))) ) &&
    339            (1 == json_equal (mi->settings.address,
    340                              is.address)) &&
    341            (1 == json_equal (mi->settings.jurisdiction,
    342                              is.jurisdiction)) &&
    343            (mi->settings.use_stefan == is.use_stefan) &&
    344            (GNUNET_TIME_relative_cmp (mi->settings.default_wire_transfer_delay,
    345                                       ==,
    346                                       is.default_wire_transfer_delay)) &&
    347            (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay,
    348                                       ==,
    349                                       is.default_pay_delay)) &&
    350            (GNUNET_TIME_relative_cmp (mi->settings.default_refund_delay,
    351                                       ==,
    352                                       is.default_refund_delay)) )
    353       {
    354         GNUNET_JSON_parse_free (spec);
    355         GNUNET_free (is.phone);
    356         GNUNET_free (id_lower);
    357         return TALER_MHD_reply_static (connection,
    358                                        MHD_HTTP_NO_CONTENT,
    359                                        NULL,
    360                                        NULL,
    361                                        0);
    362       }
    363       GNUNET_JSON_parse_free (spec);
    364       GNUNET_free (is.phone);
    365       ret = TALER_MHD_reply_with_error (connection,
    366                                         MHD_HTTP_CONFLICT,
    367                                         TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS,
    368                                         id_lower);
    369       GNUNET_free (id_lower);
    370       return ret;
    371     }
    372   }
    373 
    374   /* Check MFA is satisfied */
    375   if (validation_needed)
    376   {
    377     enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
    378 
    379     if ( (0 != (TMH_TCS_SMS & TEH_mandatory_tan_channels)) &&
    380          (NULL == is.phone) )
    381     {
    382       GNUNET_break_op (0);
    383       GNUNET_JSON_parse_free (spec);
    384       GNUNET_free (is.phone); /* does nothing... */
    385       GNUNET_free (id_lower);
    386       return TALER_MHD_reply_with_error (connection,
    387                                          MHD_HTTP_BAD_REQUEST,
    388                                          TALER_EC_GENERIC_PARAMETER_MISSING,
    389                                          "phone_number");
    390 
    391     }
    392     if ( (0 != (TMH_TCS_EMAIL & TEH_mandatory_tan_channels)) &&
    393          (NULL == is.email) )
    394     {
    395       GNUNET_break_op (0);
    396       GNUNET_JSON_parse_free (spec);
    397       GNUNET_free (is.phone);
    398       GNUNET_free (id_lower);
    399       return TALER_MHD_reply_with_error (connection,
    400                                          MHD_HTTP_BAD_REQUEST,
    401                                          TALER_EC_GENERIC_PARAMETER_MISSING,
    402                                          "email");
    403     }
    404     switch (TEH_mandatory_tan_channels)
    405     {
    406     case TMH_TCS_NONE:
    407       GNUNET_assert (0);
    408       ret = GNUNET_OK;
    409       break;
    410     case TMH_TCS_SMS:
    411       is.phone_validated = true;
    412       ret = TMH_mfa_challenges_do (hc,
    413                                    id_lower,
    414                                    TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
    415                                    true,
    416                                    TALER_MERCHANT_MFA_CHANNEL_SMS,
    417                                    is.phone,
    418                                    TALER_MERCHANT_MFA_CHANNEL_NONE);
    419       break;
    420     case TMH_TCS_EMAIL:
    421       is.email_validated = true;
    422       ret = TMH_mfa_challenges_do (hc,
    423                                    id_lower,
    424                                    TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
    425                                    true,
    426                                    TALER_MERCHANT_MFA_CHANNEL_EMAIL,
    427                                    is.email,
    428                                    TALER_MERCHANT_MFA_CHANNEL_NONE);
    429       break;
    430     case TMH_TCS_EMAIL_AND_SMS:
    431       is.phone_validated = true;
    432       is.email_validated = true;
    433       ret = TMH_mfa_challenges_do (hc,
    434                                    id_lower,
    435                                    TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION,
    436                                    true,
    437                                    TALER_MERCHANT_MFA_CHANNEL_EMAIL,
    438                                    is.email,
    439                                    TALER_MERCHANT_MFA_CHANNEL_SMS,
    440                                    is.phone,
    441                                    TALER_MERCHANT_MFA_CHANNEL_NONE);
    442       break;
    443     }
    444     if (GNUNET_OK != ret)
    445     {
    446       GNUNET_JSON_parse_free (spec);
    447       GNUNET_free (is.phone);
    448       GNUNET_free (id_lower);
    449       return (GNUNET_NO == ret)
    450         ? MHD_YES
    451         : MHD_NO;
    452     }
    453   }
    454 
    455   /* handle authentication token setup */
    456   if (NULL == auth_password)
    457   {
    458     memset (&ias.auth_salt,
    459             0,
    460             sizeof (ias.auth_salt));
    461     memset (&ias.auth_hash,
    462             0,
    463             sizeof (ias.auth_hash));
    464   }
    465   else
    466   {
    467     /* Sets 'auth_salt' and 'auth_hash' */
    468     TMH_compute_auth (auth_password,
    469                       &ias.auth_salt,
    470                       &ias.auth_hash);
    471   }
    472 
    473   /* create in-memory data structure */
    474   {
    475     struct TMH_MerchantInstance *mi;
    476     enum GNUNET_DB_QueryStatus qs;
    477 
    478     mi = GNUNET_new (struct TMH_MerchantInstance);
    479     mi->wm_head = wm_head;
    480     mi->wm_tail = wm_tail;
    481     mi->settings = is;
    482     mi->settings.address = json_incref (mi->settings.address);
    483     mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
    484     mi->settings.id = id_lower;
    485     mi->settings.name = GNUNET_strdup (is.name);
    486     if (NULL != is.email)
    487       mi->settings.email = GNUNET_strdup (is.email);
    488     mi->settings.phone = is.phone;
    489     is.phone = NULL;
    490     if (NULL != is.website)
    491       mi->settings.website = GNUNET_strdup (is.website);
    492     if (NULL != is.logo)
    493       mi->settings.logo = GNUNET_strdup (is.logo);
    494     mi->auth = ias;
    495     GNUNET_CRYPTO_eddsa_key_create (&mi->merchant_priv.eddsa_priv);
    496     GNUNET_CRYPTO_eddsa_key_get_public (&mi->merchant_priv.eddsa_priv,
    497                                         &mi->merchant_pub.eddsa_pub);
    498 
    499     for (unsigned int i = 0; i<MAX_RETRIES; i++)
    500     {
    501       if (GNUNET_OK !=
    502           TALER_MERCHANTDB_start (TMH_db,
    503                                   "post /instances"))
    504       {
    505         GNUNET_break (0);
    506         mi->rc = 1;
    507         TMH_instance_decref (mi);
    508         GNUNET_JSON_parse_free (spec);
    509         return TALER_MHD_reply_with_error (
    510           connection,
    511           MHD_HTTP_INTERNAL_SERVER_ERROR,
    512           TALER_EC_GENERIC_DB_START_FAILED,
    513           NULL);
    514       }
    515       qs = TALER_MERCHANTDB_insert_instance (TMH_db,
    516                                              &mi->merchant_pub,
    517                                              &mi->merchant_priv,
    518                                              &mi->settings,
    519                                              &mi->auth);
    520       switch (qs)
    521       {
    522       case GNUNET_DB_STATUS_HARD_ERROR:
    523         {
    524           enum MHD_Result ret;
    525 
    526           TALER_MERCHANTDB_rollback (TMH_db);
    527           GNUNET_break (0);
    528           ret = TALER_MHD_reply_with_error (
    529             connection,
    530             MHD_HTTP_INTERNAL_SERVER_ERROR,
    531             TALER_EC_GENERIC_DB_STORE_FAILED,
    532             "insert_instance");
    533           mi->rc = 1;
    534           TMH_instance_decref (mi);
    535           GNUNET_JSON_parse_free (spec);
    536           return ret;
    537         }
    538       case GNUNET_DB_STATUS_SOFT_ERROR:
    539         goto retry;
    540       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    541         {
    542           enum MHD_Result ret;
    543 
    544           TALER_MERCHANTDB_rollback (TMH_db);
    545           GNUNET_break (0);
    546           ret = TALER_MHD_reply_with_error (
    547             connection,
    548             MHD_HTTP_CONFLICT,
    549             TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS,
    550             id_lower);
    551           mi->rc = 1;
    552           TMH_instance_decref (mi);
    553           GNUNET_JSON_parse_free (spec);
    554           return ret;
    555         }
    556       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    557         /* handled below */
    558         break;
    559       }
    560       qs = TALER_MERCHANTDB_commit (TMH_db);
    561       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    562         qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    563 retry:
    564       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    565         break; /* success! -- or hard failure */
    566     } /* for .. MAX_RETRIES */
    567     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    568     {
    569       GNUNET_break (0);
    570       mi->rc = 1;
    571       TMH_instance_decref (mi);
    572       GNUNET_JSON_parse_free (spec);
    573       return TALER_MHD_reply_with_error (connection,
    574                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    575                                          TALER_EC_GENERIC_DB_COMMIT_FAILED,
    576                                          NULL);
    577     }
    578     /* Finally, also update our running process */
    579     GNUNET_assert (GNUNET_OK ==
    580                    TMH_add_instance (mi));
    581     TMH_reload_instances (mi->settings.id);
    582   }
    583   GNUNET_JSON_parse_free (spec);
    584   if (GNUNET_TIME_relative_is_zero (login_token_expiration))
    585   {
    586     return TALER_MHD_reply_static (connection,
    587                                    MHD_HTTP_NO_CONTENT,
    588                                    NULL,
    589                                    NULL,
    590                                    0);
    591   }
    592 
    593   {
    594     /* Narrow DB interaction to new instance */
    595     enum GNUNET_DB_QueryStatus qs;
    596 
    597     qs = TALER_MERCHANTDB_set_instance (TMH_db,
    598                                         id_lower);
    599     switch (qs)
    600     {
    601     case GNUNET_DB_STATUS_HARD_ERROR:
    602       GNUNET_break (0);
    603       return TALER_MHD_reply_with_error (
    604         connection,
    605         MHD_HTTP_INTERNAL_SERVER_ERROR,
    606         TALER_EC_GENERIC_DB_SETUP_FAILED,
    607         "set_instance");
    608     case GNUNET_DB_STATUS_SOFT_ERROR:
    609       GNUNET_break (0);
    610       return TALER_MHD_reply_with_error (
    611         connection,
    612         MHD_HTTP_INTERNAL_SERVER_ERROR,
    613         TALER_EC_GENERIC_DB_SETUP_FAILED,
    614         "set_instance");
    615     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    616       return TALER_MHD_reply_with_error (
    617         connection,
    618         MHD_HTTP_NOT_FOUND,
    619         TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    620         hc->url);
    621     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    622       break;
    623     }
    624   }
    625 
    626   {
    627     struct TALER_MERCHANTDB_LoginTokenP btoken;
    628     enum TMH_AuthScope iscope = TMH_AS_REFRESHABLE | TMH_AS_SPA;
    629     enum GNUNET_DB_QueryStatus qs;
    630     struct GNUNET_TIME_Timestamp expiration_time;
    631     bool refreshable = true;
    632 
    633     GNUNET_CRYPTO_random_block (&btoken,
    634                                 sizeof (btoken));
    635     expiration_time
    636       = GNUNET_TIME_relative_to_timestamp (login_token_expiration);
    637     qs = TALER_MERCHANTDB_insert_login_token (TMH_db,
    638                                               id_lower,
    639                                               &btoken,
    640                                               GNUNET_TIME_timestamp_get (),
    641                                               expiration_time,
    642                                               iscope,
    643                                               "login token from instance creation");
    644     switch (qs)
    645     {
    646     case GNUNET_DB_STATUS_HARD_ERROR:
    647     case GNUNET_DB_STATUS_SOFT_ERROR:
    648     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    649       GNUNET_break (0);
    650       return TALER_MHD_reply_with_error (
    651         connection,
    652         MHD_HTTP_INTERNAL_SERVER_ERROR,
    653         TALER_EC_GENERIC_DB_STORE_FAILED,
    654         "insert_login_token");
    655     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    656       break;
    657     }
    658 
    659     {
    660       char *tok;
    661       enum MHD_Result ret;
    662       char *val;
    663 
    664       val = GNUNET_STRINGS_data_to_string_alloc (&btoken,
    665                                                  sizeof (btoken));
    666       GNUNET_asprintf (&tok,
    667                        RFC_8959_PREFIX "%s",
    668                        val);
    669       GNUNET_free (val);
    670       ret = TALER_MHD_REPLY_JSON_PACK (
    671         connection,
    672         MHD_HTTP_OK,
    673         GNUNET_JSON_pack_string ("access_token",
    674                                  tok),
    675         GNUNET_JSON_pack_string ("token",
    676                                  tok),
    677         GNUNET_JSON_pack_string ("scope",
    678                                  TMH_get_name_by_scope (iscope,
    679                                                         &refreshable)),
    680         GNUNET_JSON_pack_bool ("refreshable",
    681                                refreshable),
    682         GNUNET_JSON_pack_timestamp ("expiration",
    683                                     expiration_time));
    684       GNUNET_free (tok);
    685       return ret;
    686     }
    687   }
    688 }
    689 
    690 
    691 /**
    692  * Generate an instance, given its configuration.
    693  *
    694  * @param rh context of the handler
    695  * @param connection the MHD connection to handle
    696  * @param[in,out] hc context with further information about the request
    697  * @return MHD result code
    698  */
    699 enum MHD_Result
    700 TMH_private_post_instances (const struct TMH_RequestHandler *rh,
    701                             struct MHD_Connection *connection,
    702                             struct TMH_HandlerContext *hc)
    703 {
    704   return post_instances (rh,
    705                          connection,
    706                          hc,
    707                          GNUNET_TIME_UNIT_ZERO,
    708                          false);
    709 }
    710 
    711 
    712 /**
    713  * Generate an instance, given its configuration.
    714  * Public handler to be used when self-provisioning.
    715  *
    716  * @param rh context of the handler
    717  * @param connection the MHD connection to handle
    718  * @param[in,out] hc context with further information about the request
    719  * @return MHD result code
    720  */
    721 enum MHD_Result
    722 TMH_public_post_instances (const struct TMH_RequestHandler *rh,
    723                            struct MHD_Connection *connection,
    724                            struct TMH_HandlerContext *hc)
    725 {
    726   struct GNUNET_TIME_Relative expiration;
    727 
    728   TALER_MHD_parse_request_rel_time (connection,
    729                                     "token_validity_ms",
    730                                     &expiration);
    731   if (GNUNET_YES !=
    732       TMH_have_self_provisioning)
    733   {
    734     GNUNET_break_op (0);
    735     return TALER_MHD_reply_with_error (connection,
    736                                        MHD_HTTP_FORBIDDEN,
    737                                        TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
    738                                        "Self-provisioning is not enabled");
    739   }
    740 
    741   return post_instances (rh,
    742                          connection,
    743                          hc,
    744                          expiration,
    745                          TMH_TCS_NONE !=
    746                          TEH_mandatory_tan_channels);
    747 }
    748 
    749 
    750 /* end of taler-merchant-httpd_post-management-instances.c */