merchant

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

merchant_api_post-private-otp-devices.c (9166B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022-2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU Lesser General Public License as
      7   published by the Free Software Foundation; either version 2.1,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU Lesser General Public License for more details.
     14 
     15   You should have received a copy of the GNU Lesser General
     16   Public License along with TALER; see the file COPYING.LGPL.
     17   If not, see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file src/lib/merchant_api_post-private-otp-devices.c
     21  * @brief Implementation of the POST /private/otp-devices request
     22  * @author Christian Grothoff
     23  */
     24 #include "platform.h"
     25 #include <curl/curl.h>
     26 #include <jansson.h>
     27 #include <microhttpd.h> /* just for HTTP status codes */
     28 #include <gnunet/gnunet_util_lib.h>
     29 #include <gnunet/gnunet_curl_lib.h>
     30 #include <taler/merchant/post-private-otp-devices.h>
     31 #include "merchant_api_curl_defaults.h"
     32 #include "merchant_api_common.h"
     33 #include <taler/taler_json_lib.h>
     34 #include <taler/taler_curl_lib.h>
     35 
     36 
     37 /**
     38  * Handle for a POST /private/otp-devices operation.
     39  */
     40 struct TALER_MERCHANT_PostPrivateOtpDevicesHandle
     41 {
     42   /**
     43    * Base URL of the merchant backend.
     44    */
     45   char *base_url;
     46 
     47   /**
     48    * The full URL for this request.
     49    */
     50   char *url;
     51 
     52   /**
     53    * Handle for the request.
     54    */
     55   struct GNUNET_CURL_Job *job;
     56 
     57   /**
     58    * Function to call with the result.
     59    */
     60   TALER_MERCHANT_PostPrivateOtpDevicesCallback cb;
     61 
     62   /**
     63    * Closure for @a cb.
     64    */
     65   TALER_MERCHANT_POST_PRIVATE_OTP_DEVICES_RESULT_CLOSURE *cb_cls;
     66 
     67   /**
     68    * Reference to the execution context.
     69    */
     70   struct GNUNET_CURL_Context *ctx;
     71 
     72   /**
     73    * Minor context that holds body and headers.
     74    */
     75   struct TALER_CURL_PostContext post_ctx;
     76 
     77   /**
     78    * OTP device identifier.
     79    */
     80   char *otp_device_id;
     81 
     82   /**
     83    * Human-readable description.
     84    */
     85   char *otp_device_description;
     86 
     87   /**
     88    * Base32-encoded OTP secret key (or NULL).
     89    */
     90   char *otp_key;
     91 
     92   /**
     93    * OTP algorithm to use.
     94    */
     95   enum TALER_MerchantConfirmationAlgorithm otp_algorithm;
     96 
     97   /**
     98    * Initial counter value (for HOTP).
     99    */
    100   uint64_t otp_ctr;
    101 
    102   /**
    103    * True if @e otp_ctr was explicitly set via options.
    104    */
    105   bool have_otp_ctr;
    106 };
    107 
    108 
    109 /**
    110  * Function called when we're done processing the
    111  * HTTP POST /private/otp-devices request.
    112  *
    113  * @param cls the `struct TALER_MERCHANT_PostPrivateOtpDevicesHandle`
    114  * @param response_code HTTP response code, 0 on error
    115  * @param response response body, NULL if not in JSON
    116  */
    117 static void
    118 handle_post_otp_devices_finished (void *cls,
    119                                   long response_code,
    120                                   const void *response)
    121 {
    122   struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *ppoh = cls;
    123   const json_t *json = response;
    124   struct TALER_MERCHANT_PostPrivateOtpDevicesResponse odr = {
    125     .hr.http_status = (unsigned int) response_code,
    126     .hr.reply = json
    127   };
    128 
    129   ppoh->job = NULL;
    130   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    131               "POST /private/otp-devices completed with response code %u\n",
    132               (unsigned int) response_code);
    133   switch (response_code)
    134   {
    135   case 0:
    136     odr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    137     break;
    138   case MHD_HTTP_NO_CONTENT:
    139     break;
    140   case MHD_HTTP_OK:
    141     {
    142       struct GNUNET_JSON_Specification spec[] = {
    143         GNUNET_JSON_spec_string ("otp_device_pub",
    144                                  &odr.details.ok.otp_device_pub),
    145         GNUNET_JSON_spec_end ()
    146       };
    147 
    148       if (GNUNET_OK !=
    149           GNUNET_JSON_parse (json,
    150                              spec,
    151                              NULL, NULL))
    152       {
    153         GNUNET_break_op (0);
    154         odr.hr.http_status = 0;
    155         odr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    156       }
    157       break;
    158     }
    159   case MHD_HTTP_BAD_REQUEST:
    160     odr.hr.ec = TALER_JSON_get_error_code (json);
    161     odr.hr.hint = TALER_JSON_get_error_hint (json);
    162     break;
    163   case MHD_HTTP_UNAUTHORIZED:
    164     odr.hr.ec = TALER_JSON_get_error_code (json);
    165     odr.hr.hint = TALER_JSON_get_error_hint (json);
    166     break;
    167   case MHD_HTTP_FORBIDDEN:
    168     odr.hr.ec = TALER_JSON_get_error_code (json);
    169     odr.hr.hint = TALER_JSON_get_error_hint (json);
    170     break;
    171   case MHD_HTTP_NOT_FOUND:
    172     odr.hr.ec = TALER_JSON_get_error_code (json);
    173     odr.hr.hint = TALER_JSON_get_error_hint (json);
    174     break;
    175   case MHD_HTTP_CONFLICT:
    176     odr.hr.ec = TALER_JSON_get_error_code (json);
    177     odr.hr.hint = TALER_JSON_get_error_hint (json);
    178     break;
    179   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    180     odr.hr.ec = TALER_JSON_get_error_code (json);
    181     odr.hr.hint = TALER_JSON_get_error_hint (json);
    182     break;
    183   default:
    184     TALER_MERCHANT_parse_error_details_ (json,
    185                                          response_code,
    186                                          &odr.hr);
    187     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    188                 "Unexpected response code %u/%d\n",
    189                 (unsigned int) response_code,
    190                 (int) odr.hr.ec);
    191     GNUNET_break_op (0);
    192     break;
    193   }
    194   ppoh->cb (ppoh->cb_cls,
    195             &odr);
    196   TALER_MERCHANT_post_private_otp_devices_cancel (ppoh);
    197 }
    198 
    199 
    200 struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *
    201 TALER_MERCHANT_post_private_otp_devices_create (
    202   struct GNUNET_CURL_Context *ctx,
    203   const char *url,
    204   const char *otp_device_id,
    205   const char *otp_device_description,
    206   const char *otp_key,
    207   enum TALER_MerchantConfirmationAlgorithm otp_algorithm)
    208 {
    209   struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *ppoh;
    210 
    211   ppoh = GNUNET_new (struct TALER_MERCHANT_PostPrivateOtpDevicesHandle);
    212   ppoh->ctx = ctx;
    213   ppoh->base_url = GNUNET_strdup (url);
    214   ppoh->otp_device_id = GNUNET_strdup (otp_device_id);
    215   ppoh->otp_device_description = GNUNET_strdup (otp_device_description);
    216   if (NULL != otp_key)
    217     ppoh->otp_key = GNUNET_strdup (otp_key);
    218   ppoh->otp_algorithm = otp_algorithm;
    219   return ppoh;
    220 }
    221 
    222 
    223 enum GNUNET_GenericReturnValue
    224 TALER_MERCHANT_post_private_otp_devices_set_options_ (
    225   struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *ppoh,
    226   unsigned int num_options,
    227   const struct TALER_MERCHANT_PostPrivateOtpDevicesOptionValue *options)
    228 {
    229   for (unsigned int i = 0; i < num_options; i++)
    230   {
    231     switch (options[i].option)
    232     {
    233     case TALER_MERCHANT_POST_PRIVATE_OTP_DEVICES_OPTION_END:
    234       return GNUNET_OK;
    235     case TALER_MERCHANT_POST_PRIVATE_OTP_DEVICES_OPTION_OTP_CTR:
    236       ppoh->otp_ctr = options[i].details.otp_ctr;
    237       ppoh->have_otp_ctr = true;
    238       break;
    239     default:
    240       GNUNET_break (0);
    241       return GNUNET_SYSERR;
    242     }
    243   }
    244   return GNUNET_OK;
    245 }
    246 
    247 
    248 enum TALER_ErrorCode
    249 TALER_MERCHANT_post_private_otp_devices_start (
    250   struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *ppoh,
    251   TALER_MERCHANT_PostPrivateOtpDevicesCallback cb,
    252   TALER_MERCHANT_POST_PRIVATE_OTP_DEVICES_RESULT_CLOSURE *cb_cls)
    253 {
    254   json_t *req_obj;
    255   CURL *eh;
    256 
    257   ppoh->cb = cb;
    258   ppoh->cb_cls = cb_cls;
    259   ppoh->url = TALER_url_join (ppoh->base_url,
    260                               "private/otp-devices",
    261                               NULL);
    262   if (NULL == ppoh->url)
    263     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    264   req_obj = GNUNET_JSON_PACK (
    265     GNUNET_JSON_pack_string ("otp_device_id",
    266                              ppoh->otp_device_id),
    267     GNUNET_JSON_pack_string ("otp_device_description",
    268                              ppoh->otp_device_description),
    269     GNUNET_JSON_pack_uint64 ("otp_algorithm",
    270                              (uint32_t) ppoh->otp_algorithm),
    271     GNUNET_JSON_pack_allow_null (
    272       GNUNET_JSON_pack_string ("otp_key",
    273                                ppoh->otp_key)),
    274     GNUNET_JSON_pack_uint64 ("otp_ctr",
    275                              ppoh->otp_ctr));
    276   eh = TALER_MERCHANT_curl_easy_get_ (ppoh->url);
    277   if ( (NULL == eh) ||
    278        (GNUNET_OK !=
    279         TALER_curl_easy_post (&ppoh->post_ctx,
    280                               eh,
    281                               req_obj)) )
    282   {
    283     GNUNET_break (0);
    284     json_decref (req_obj);
    285     if (NULL != eh)
    286       curl_easy_cleanup (eh);
    287     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    288   }
    289   json_decref (req_obj);
    290   ppoh->job = GNUNET_CURL_job_add2 (ppoh->ctx,
    291                                     eh,
    292                                     ppoh->post_ctx.headers,
    293                                     &handle_post_otp_devices_finished,
    294                                     ppoh);
    295   if (NULL == ppoh->job)
    296     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    297   return TALER_EC_NONE;
    298 }
    299 
    300 
    301 void
    302 TALER_MERCHANT_post_private_otp_devices_cancel (
    303   struct TALER_MERCHANT_PostPrivateOtpDevicesHandle *ppoh)
    304 {
    305   if (NULL != ppoh->job)
    306   {
    307     GNUNET_CURL_job_cancel (ppoh->job);
    308     ppoh->job = NULL;
    309   }
    310   TALER_curl_easy_post_finished (&ppoh->post_ctx);
    311   GNUNET_free (ppoh->otp_device_id);
    312   GNUNET_free (ppoh->otp_device_description);
    313   GNUNET_free (ppoh->otp_key);
    314   GNUNET_free (ppoh->url);
    315   GNUNET_free (ppoh->base_url);
    316   GNUNET_free (ppoh);
    317 }
    318 
    319 
    320 /* end of merchant_api_post-private-otp-devices-new.c */