merchant

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

testing_api_cmd_instance_token.c (10350B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 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 General Public License as
      7   published by the Free Software Foundation; either version 3, or
      8   (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, see
     17   <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file testing_api_cmd_instance_token.c
     21  * @brief command to test /private/token POSTing
     22  * @author Martin Schanzenbach
     23  */
     24 #include "platform.h"
     25 #include <taler/taler_exchange_service.h>
     26 #include <taler/taler_testing_lib.h>
     27 #include "taler_merchant_service.h"
     28 #include "taler_merchant_testing_lib.h"
     29 
     30 
     31 /**
     32  * State of a "POST /instances/$ID/private/token" CMD.
     33  */
     34 struct TokenInstanceState
     35 {
     36 
     37   /**
     38    * Handle for a "POST token" request.
     39    */
     40   struct TALER_MERCHANT_InstanceTokenPostHandle *itph;
     41 
     42   /**
     43    * Handle for a "DELETE token" request.
     44    */
     45   struct TALER_MERCHANT_InstanceTokenDeleteHandle *itdh;
     46 
     47   /**
     48    * The interpreter state.
     49    */
     50   struct TALER_TESTING_Interpreter *is;
     51 
     52   /**
     53    * Base URL of the merchant serving the request.
     54    */
     55   const char *merchant_url;
     56 
     57   /**
     58    * ID of the instance to run GET for.
     59    */
     60   const char *instance_id;
     61 
     62   /**
     63    * The received token (if any).
     64    */
     65   char *token;
     66 
     67   /**
     68    * Desired scope. Can be NULL
     69    */
     70   const char *scope;
     71 
     72   /**
     73    * Desired duration.
     74    */
     75   struct GNUNET_TIME_Relative duration;
     76 
     77   /**
     78    * Refreshable?
     79    */
     80   bool refreshable;
     81 
     82   /**
     83    * Expected HTTP response code.
     84    */
     85   unsigned int http_status;
     86 
     87   /**
     88    * DELETE or POST.
     89    */
     90   unsigned int is_delete;
     91 
     92 };
     93 
     94 /**
     95  * Callback for a POST /instances/$ID/private/token operation.
     96  *
     97  * @param cls closure for this function
     98  * @param hr response being processed
     99  */
    100 static void
    101 token_instance_cb (void *cls,
    102                    const struct TALER_MERCHANT_HttpResponse *hr)
    103 {
    104   struct TokenInstanceState *tis = cls;
    105   const char *scope;
    106   struct GNUNET_TIME_Timestamp duration;
    107   bool refreshable;
    108   const char *error_name;
    109   unsigned int error_line;
    110 
    111 
    112   tis->itph = NULL;
    113   if (tis->http_status != hr->http_status)
    114   {
    115     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    116                 "Unexpected response code %u (%d) to command %s\n",
    117                 hr->http_status,
    118                 (int) hr->ec,
    119                 TALER_TESTING_interpreter_get_current_label (tis->is));
    120     TALER_TESTING_interpreter_fail (tis->is);
    121     return;
    122   }
    123   switch (hr->http_status)
    124   {
    125   case MHD_HTTP_NO_CONTENT:
    126     GNUNET_assert (GNUNET_YES == tis->is_delete);
    127     break;
    128   case MHD_HTTP_OK:
    129     {
    130       /* Get token */
    131       struct GNUNET_JSON_Specification spec[] = {
    132         GNUNET_JSON_spec_string_copy ("access_token",
    133                                       &tis->token),
    134         GNUNET_JSON_spec_string ("scope",
    135                                  &scope),
    136         GNUNET_JSON_spec_bool ("refreshable",
    137                                &refreshable),
    138         GNUNET_JSON_spec_timestamp ("expiration",
    139                                     &duration),
    140         GNUNET_JSON_spec_end ()
    141       };
    142 
    143       GNUNET_assert (GNUNET_NO == tis->is_delete);
    144       if (GNUNET_OK !=
    145           GNUNET_JSON_parse (hr->reply,
    146                              spec,
    147                              &error_name,
    148                              &error_line))
    149       {
    150         char *js;
    151 
    152         js = json_dumps (hr->reply,
    153                          JSON_INDENT (1));
    154         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    155                     "Parser failed on %s:%u for input `%s'\n",
    156                     error_name,
    157                     error_line,
    158                     js);
    159         free (js);
    160         TALER_TESTING_FAIL (tis->is);
    161       }
    162       break;
    163     }
    164   case MHD_HTTP_BAD_REQUEST:
    165     /* likely invalid auth value, we do not check client-side */
    166     break;
    167   case MHD_HTTP_FORBIDDEN:
    168     break;
    169   default:
    170     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    171                 "Unhandled HTTP status %u (%d) returned from /private/token operation.\n",
    172                 hr->http_status,
    173                 hr->ec);
    174   }
    175 
    176 
    177   TALER_TESTING_interpreter_next (tis->is);
    178 }
    179 
    180 
    181 /**
    182  * set a token
    183  *
    184  *
    185  * @param cls closure.
    186  * @param cmd command being run now.
    187  * @param is interpreter state.
    188  */
    189 static void
    190 set_token_instance_run (void *cls,
    191                         const struct TALER_TESTING_Command *cmd,
    192                         struct TALER_TESTING_Interpreter *is)
    193 {
    194   const char *token_job_label = cls;
    195   const char *token;
    196   const struct TALER_TESTING_Command *tok_cmd;
    197   struct GNUNET_CURL_Context *cctx;
    198   char *authorization;
    199 
    200   cctx = TALER_TESTING_interpreter_get_context (is);
    201   GNUNET_assert (NULL != cctx);
    202   tok_cmd = TALER_TESTING_interpreter_lookup_command (
    203     is,
    204     token_job_label);
    205   TALER_TESTING_get_trait_bearer_token (tok_cmd,
    206                                         &token);
    207   GNUNET_assert (NULL != token);
    208 
    209   GNUNET_asprintf (&authorization,
    210                    "%s: Bearer %s",
    211                    MHD_HTTP_HEADER_AUTHORIZATION,
    212                    token);
    213   GNUNET_assert (GNUNET_OK ==
    214                  GNUNET_CURL_append_header (cctx,
    215                                             authorization));
    216   GNUNET_free (authorization);
    217   TALER_TESTING_interpreter_next (is);
    218 }
    219 
    220 
    221 /**
    222  * Run the "token /instances/$ID" CMD.
    223  *
    224  *
    225  * @param cls closure.
    226  * @param cmd command being run now.
    227  * @param is interpreter state.
    228  */
    229 static void
    230 token_instance_run (void *cls,
    231                     const struct TALER_TESTING_Command *cmd,
    232                     struct TALER_TESTING_Interpreter *is)
    233 {
    234   struct TokenInstanceState *tis = cls;
    235 
    236   tis->is = is;
    237   if (GNUNET_NO == tis->is_delete)
    238     tis->itph = TALER_MERCHANT_instance_token_post (
    239       TALER_TESTING_interpreter_get_context (is),
    240       tis->merchant_url,
    241       tis->instance_id,
    242       tis->scope,
    243       tis->duration,
    244       tis->refreshable,
    245       &token_instance_cb,
    246       tis);
    247   else
    248     tis->itdh = TALER_MERCHANT_instance_token_delete (
    249       TALER_TESTING_interpreter_get_context (is),
    250       tis->merchant_url,
    251       tis->instance_id,
    252       &token_instance_cb,
    253       tis);
    254   GNUNET_assert ((NULL != tis->itph) || (NULL != tis->itdh));
    255 }
    256 
    257 
    258 /**
    259  * Free the state of a "POST instance token" CMD, and possibly
    260  * cancel a pending operation thereof.
    261  *
    262  * @param cls closure.
    263  * @param cmd command being run.
    264  */
    265 static void
    266 token_instance_cleanup (void *cls,
    267                         const struct TALER_TESTING_Command *cmd)
    268 {
    269   struct TokenInstanceState *tis = cls;
    270 
    271   if (NULL != tis->itph)
    272   {
    273     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    274                 "%s /instance/$ID/token operation did not complete\n",
    275                 (GNUNET_NO == tis->is_delete) ? "DELETE" : "POST");
    276     if (GNUNET_NO == tis->is_delete)
    277       TALER_MERCHANT_instance_token_post_cancel (tis->itph);
    278     else
    279       TALER_MERCHANT_instance_token_delete_cancel (tis->itdh);
    280   }
    281   GNUNET_free (tis);
    282 }
    283 
    284 
    285 /**
    286  * Offer internal data to other commands.
    287  *
    288  * @param cls closure
    289  * @param[out] ret result (could be anything)
    290  * @param trait name of the trait
    291  * @param index index number of the object to extract.
    292  * @return #GNUNET_OK on success
    293  */
    294 static enum GNUNET_GenericReturnValue
    295 token_instance_traits (void *cls,
    296                        const void **ret,
    297                        const char *trait,
    298                        unsigned int index)
    299 {
    300   struct TokenInstanceState *ais = cls;
    301   struct TALER_TESTING_Trait traits[] = {
    302     TALER_TESTING_make_trait_bearer_token (ais->token),
    303     TALER_TESTING_trait_end ()
    304   };
    305 
    306   return TALER_TESTING_get_trait (traits,
    307                                   ret,
    308                                   trait,
    309                                   index);
    310 }
    311 
    312 
    313 struct TALER_TESTING_Command
    314 TALER_TESTING_cmd_merchant_delete_instance_token (const char *label,
    315                                                   const char *merchant_url,
    316                                                   const char *instance_id,
    317                                                   unsigned int http_status)
    318 {
    319   struct TokenInstanceState *tis;
    320 
    321   tis = GNUNET_new (struct TokenInstanceState);
    322   tis->merchant_url = merchant_url;
    323   tis->instance_id = instance_id;
    324   tis->is_delete = GNUNET_YES;
    325   tis->http_status = http_status;
    326 
    327   {
    328     struct TALER_TESTING_Command cmd = {
    329       .cls = tis,
    330       .label = label,
    331       .run = &token_instance_run,
    332       .cleanup = &token_instance_cleanup,
    333       .traits = &token_instance_traits
    334     };
    335 
    336     return cmd;
    337   }
    338 }
    339 
    340 
    341 struct TALER_TESTING_Command
    342 TALER_TESTING_cmd_merchant_set_instance_token (const char *label,
    343                                                const char *token_job_label)
    344 {
    345   {
    346     struct TALER_TESTING_Command cmd = {
    347       .cls = (void*) token_job_label, // FIXME scope
    348       .label = label,
    349       .run = &set_token_instance_run,
    350       .cleanup = NULL,
    351       .traits = NULL
    352     };
    353 
    354     return cmd;
    355   }
    356 }
    357 
    358 
    359 struct TALER_TESTING_Command
    360 TALER_TESTING_cmd_merchant_post_instance_token (const char *label,
    361                                                 const char *merchant_url,
    362                                                 const char *instance_id,
    363                                                 const char *scope,
    364                                                 struct GNUNET_TIME_Relative
    365                                                 duration,
    366                                                 bool refreshable,
    367                                                 unsigned int http_status)
    368 {
    369   struct TokenInstanceState *tis;
    370 
    371   tis = GNUNET_new (struct TokenInstanceState);
    372   tis->merchant_url = merchant_url;
    373   tis->instance_id = instance_id;
    374   tis->scope = scope;
    375   tis->duration = duration;
    376   tis->refreshable = refreshable;
    377   tis->is_delete = GNUNET_NO;
    378   tis->http_status = http_status;
    379 
    380   {
    381     struct TALER_TESTING_Command cmd = {
    382       .cls = tis,
    383       .label = label,
    384       .run = &token_instance_run,
    385       .cleanup = &token_instance_cleanup,
    386       .traits = &token_instance_traits
    387     };
    388 
    389     return cmd;
    390   }
    391 }
    392 
    393 
    394 /* end of testing_api_cmd_token_instance.c */