LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_oauth.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 83.3 % 90 75
Test Date: 2025-12-28 14:06:02 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2021-2023 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              : /**
      21              :  * @file testing/testing_api_cmd_oauth.c
      22              :  * @brief Implement a CMD to run an OAuth service for faking the legitimation service
      23              :  * @author Christian Grothoff
      24              :  */
      25              : #include "taler/platform.h"
      26              : #include "taler/taler_json_lib.h"
      27              : #include <gnunet/gnunet_curl_lib.h>
      28              : #include "taler/taler_testing_lib.h"
      29              : #include "taler/taler_mhd_lib.h"
      30              : 
      31              : /**
      32              :  * State for the oauth CMD.
      33              :  */
      34              : struct OAuthState
      35              : {
      36              : 
      37              :   /**
      38              :    * Handle to the "oauth" service.
      39              :    */
      40              :   struct MHD_Daemon *mhd;
      41              : 
      42              :   /**
      43              :    * Birthdate that the oauth server should return in a response, may be NULL
      44              :    */
      45              :   const char *birthdate;
      46              : 
      47              :   /**
      48              :    * Port to listen on.
      49              :    */
      50              :   uint16_t port;
      51              : };
      52              : 
      53              : 
      54              : struct RequestCtx
      55              : {
      56              :   struct MHD_PostProcessor *pp;
      57              :   char *code;
      58              :   char *client_id;
      59              :   char *redirect_uri;
      60              :   char *client_secret;
      61              : };
      62              : 
      63              : 
      64              : static void
      65           40 : append (char **target,
      66              :         const char *data,
      67              :         size_t size)
      68              : {
      69              :   char *tmp;
      70              : 
      71           40 :   if (NULL == *target)
      72              :   {
      73           40 :     *target = GNUNET_strndup (data,
      74              :                               size);
      75           40 :     return;
      76              :   }
      77            0 :   GNUNET_asprintf (&tmp,
      78              :                    "%s%.*s",
      79              :                    *target,
      80              :                    (int) size,
      81              :                    data);
      82            0 :   GNUNET_free (*target);
      83            0 :   *target = tmp;
      84              : }
      85              : 
      86              : 
      87              : static MHD_RESULT
      88           60 : handle_post (void *cls,
      89              :              enum MHD_ValueKind kind,
      90              :              const char *key,
      91              :              const char *filename,
      92              :              const char *content_type,
      93              :              const char *transfer_encoding,
      94              :              const char *data,
      95              :              uint64_t off,
      96              :              size_t size)
      97              : {
      98           60 :   struct RequestCtx *rc = cls;
      99              : 
     100              :   (void) kind;
     101              :   (void) filename;
     102              :   (void) content_type;
     103              :   (void) transfer_encoding;
     104              :   (void) off;
     105           60 :   if (0 == strcmp (key,
     106              :                    "code"))
     107           10 :     append (&rc->code,
     108              :             data,
     109              :             size);
     110           60 :   if (0 == strcmp (key,
     111              :                    "client_id"))
     112           10 :     append (&rc->client_id,
     113              :             data,
     114              :             size);
     115           60 :   if (0 == strcmp (key,
     116              :                    "redirect_uri"))
     117           10 :     append (&rc->redirect_uri,
     118              :             data,
     119              :             size);
     120           60 :   if (0 == strcmp (key,
     121              :                    "client_secret"))
     122           10 :     append (&rc->client_secret,
     123              :             data,
     124              :             size);
     125           60 :   return MHD_YES;
     126              : }
     127              : 
     128              : 
     129              : /**
     130              :  * A client has requested the given url using the given method
     131              :  * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
     132              :  * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc).  The callback
     133              :  * must call MHD callbacks to provide content to give back to the
     134              :  * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
     135              :  * #MHD_HTTP_NOT_FOUND, etc.).
     136              :  *
     137              :  * @param cls argument given together with the function
     138              :  *        pointer when the handler was registered with MHD
     139              :  * @param connection the connection being handled
     140              :  * @param url the requested url
     141              :  * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
     142              :  *        #MHD_HTTP_METHOD_PUT, etc.)
     143              :  * @param version the HTTP version string (i.e.
     144              :  *        MHD_HTTP_VERSION_1_1)
     145              :  * @param upload_data the data being uploaded (excluding HEADERS,
     146              :  *        for a POST that fits into memory and that is encoded
     147              :  *        with a supported encoding, the POST data will NOT be
     148              :  *        given in upload_data and is instead available as
     149              :  *        part of MHD_get_connection_values(); very large POST
     150              :  *        data *will* be made available incrementally in
     151              :  *        @a upload_data)
     152              :  * @param[in,out] upload_data_size set initially to the size of the
     153              :  *        @a upload_data provided; the method must update this
     154              :  *        value to the number of bytes NOT processed;
     155              :  * @param[in,out] con_cls pointer that the callback can set to some
     156              :  *        address and that will be preserved by MHD for future
     157              :  *        calls for this request; since the access handler may
     158              :  *        be called many times (i.e., for a PUT/POST operation
     159              :  *        with plenty of upload data) this allows the application
     160              :  *        to easily associate some request-specific state.
     161              :  *        If necessary, this state can be cleaned up in the
     162              :  *        global MHD_RequestCompletedCallback (which
     163              :  *        can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
     164              :  *        Initially, `*con_cls` will be NULL.
     165              :  * @return #MHD_YES if the connection was handled successfully,
     166              :  *         #MHD_NO if the socket must be closed due to a serious
     167              :  *         error while handling the request
     168              :  */
     169              : static MHD_RESULT
     170           39 : handler_cb (void *cls,
     171              :             struct MHD_Connection *connection,
     172              :             const char *url,
     173              :             const char *method,
     174              :             const char *version,
     175              :             const char *upload_data,
     176              :             size_t *upload_data_size,
     177              :             void **con_cls)
     178              : {
     179           39 :   struct RequestCtx *rc = *con_cls;
     180           39 :   struct OAuthState *oas = cls;
     181              :   unsigned int hc;
     182              :   json_t *body;
     183              : 
     184              :   (void) version;
     185           39 :   if (0 == strcasecmp (method,
     186              :                        MHD_HTTP_METHOD_GET))
     187              :   {
     188              :     json_t *data =
     189            9 :       GNUNET_JSON_PACK (
     190              :         GNUNET_JSON_pack_string ("id",
     191              :                                  "XXXID12345678"),
     192              :         GNUNET_JSON_pack_string ("first_name",
     193              :                                  "Bob"),
     194              :         GNUNET_JSON_pack_string ("last_name",
     195              :                                  "Builder"));
     196              : 
     197            9 :     if (NULL != oas->birthdate)
     198            9 :       GNUNET_assert (0 ==
     199              :                      json_object_set_new (data,
     200              :                                           "birthdate",
     201              :                                           json_string_nocheck (
     202              :                                             oas->birthdate)));
     203              : 
     204            9 :     body = GNUNET_JSON_PACK (
     205              :       GNUNET_JSON_pack_string (
     206              :         "status",
     207              :         "success"),
     208              :       GNUNET_JSON_pack_object_steal (
     209              :         "data", data));
     210            9 :     return TALER_MHD_reply_json_steal (connection,
     211              :                                        body,
     212              :                                        MHD_HTTP_OK);
     213              :   }
     214           30 :   if (0 != strcasecmp (method,
     215              :                        MHD_HTTP_METHOD_POST))
     216              :   {
     217            0 :     GNUNET_break (0);
     218            0 :     return MHD_NO;
     219              :   }
     220           30 :   if (NULL == rc)
     221              :   {
     222           10 :     rc = GNUNET_new (struct RequestCtx);
     223           10 :     *con_cls = rc;
     224           10 :     rc->pp = MHD_create_post_processor (connection,
     225              :                                         4092,
     226              :                                         &handle_post,
     227              :                                         rc);
     228           10 :     return MHD_YES;
     229              :   }
     230           20 :   if (0 != *upload_data_size)
     231              :   {
     232              :     MHD_RESULT ret;
     233              : 
     234           10 :     ret = MHD_post_process (rc->pp,
     235              :                             upload_data,
     236              :                             *upload_data_size);
     237           10 :     *upload_data_size = 0;
     238           10 :     return ret;
     239              :   }
     240              : 
     241              : 
     242              :   /* NOTE: In the future, we MAY want to distinguish between
     243              :      the different URLs and possibly return more information.
     244              :      For now, just do the minimum: implement the main handler
     245              :      that checks the code. */
     246           10 :   if ( (NULL == rc->code) ||
     247           10 :        (NULL == rc->client_id) ||
     248           10 :        (NULL == rc->redirect_uri) ||
     249           10 :        (NULL == rc->client_secret) )
     250              :   {
     251            0 :     GNUNET_break (0);
     252            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     253              :                 "Bad request to Oauth faker: `%s' with %s/%s/%s/%s\n",
     254              :                 url,
     255              :                 rc->code,
     256              :                 rc->client_id,
     257              :                 rc->redirect_uri,
     258              :                 rc->client_secret);
     259            0 :     return MHD_NO;
     260              :   }
     261           10 :   if (0 != strcmp (rc->client_id,
     262              :                    "taler-exchange"))
     263              :   {
     264            0 :     body = GNUNET_JSON_PACK (
     265              :       GNUNET_JSON_pack_string ("error",
     266              :                                "unknown_client"),
     267              :       GNUNET_JSON_pack_string ("error_description",
     268              :                                "only 'taler-exchange' is allowed"));
     269            0 :     hc = MHD_HTTP_NOT_FOUND;
     270              :   }
     271           10 :   else if (0 != strcmp (rc->client_secret,
     272              :                         "exchange-secret"))
     273              :   {
     274            0 :     body = GNUNET_JSON_PACK (
     275              :       GNUNET_JSON_pack_string ("error",
     276              :                                "invalid_client_secret"),
     277              :       GNUNET_JSON_pack_string ("error_description",
     278              :                                "only 'exchange-secret' is valid"));
     279            0 :     hc = MHD_HTTP_FORBIDDEN;
     280              :   }
     281              :   else
     282              :   {
     283           10 :     if (0 != strcmp (rc->code,
     284              :                      "pass"))
     285              :     {
     286            1 :       body = GNUNET_JSON_PACK (
     287              :         GNUNET_JSON_pack_string ("error",
     288              :                                  "invalid_grant"),
     289              :         GNUNET_JSON_pack_string ("error_description",
     290              :                                  "only 'pass' shall pass"));
     291            1 :       hc = MHD_HTTP_FORBIDDEN;
     292              :     }
     293              :     else
     294              :     {
     295            9 :       body = GNUNET_JSON_PACK (
     296              :         GNUNET_JSON_pack_string ("access_token",
     297              :                                  "good"),
     298              :         GNUNET_JSON_pack_string ("token_type",
     299              :                                  "bearer"),
     300              :         GNUNET_JSON_pack_uint64 ("expires_in",
     301              :                                  3600),
     302              :         GNUNET_JSON_pack_string ("refresh_token",
     303              :                                  "better"));
     304            9 :       hc = MHD_HTTP_OK;
     305              :     }
     306              :   }
     307           10 :   return TALER_MHD_reply_json_steal (connection,
     308              :                                      body,
     309              :                                      hc);
     310              : }
     311              : 
     312              : 
     313              : static void
     314           19 : cleanup (void *cls,
     315              :          struct MHD_Connection *connection,
     316              :          void **con_cls,
     317              :          enum MHD_RequestTerminationCode toe)
     318              : {
     319           19 :   struct RequestCtx *rc = *con_cls;
     320              : 
     321              :   (void) cls;
     322              :   (void) connection;
     323              :   (void) toe;
     324           19 :   if (NULL == rc)
     325            9 :     return;
     326           10 :   MHD_destroy_post_processor (rc->pp);
     327           10 :   GNUNET_free (rc->code);
     328           10 :   GNUNET_free (rc->client_id);
     329           10 :   GNUNET_free (rc->redirect_uri);
     330           10 :   GNUNET_free (rc->client_secret);
     331           10 :   GNUNET_free (rc);
     332              : }
     333              : 
     334              : 
     335              : /**
     336              :  * Run the command.
     337              :  *
     338              :  * @param cls closure.
     339              :  * @param cmd the command to execute.
     340              :  * @param is the interpreter state.
     341              :  */
     342              : static void
     343            5 : oauth_run (void *cls,
     344              :            const struct TALER_TESTING_Command *cmd,
     345              :            struct TALER_TESTING_Interpreter *is)
     346              : {
     347            5 :   struct OAuthState *oas = cls;
     348              : 
     349              :   (void) cmd;
     350           10 :   oas->mhd = MHD_start_daemon (MHD_USE_AUTO_INTERNAL_THREAD | MHD_USE_DEBUG,
     351            5 :                                oas->port,
     352              :                                NULL, NULL,
     353              :                                &handler_cb, oas,
     354              :                                MHD_OPTION_NOTIFY_COMPLETED, &cleanup, NULL,
     355              :                                NULL);
     356            5 :   if (NULL == oas->mhd)
     357              :   {
     358            0 :     GNUNET_break (0);
     359            0 :     TALER_TESTING_interpreter_fail (is);
     360            0 :     return;
     361              :   }
     362            5 :   TALER_TESTING_interpreter_next (is);
     363              : }
     364              : 
     365              : 
     366              : /**
     367              :  * Cleanup the state from a "oauth" CMD, and possibly cancel a operation
     368              :  * thereof.
     369              :  *
     370              :  * @param cls closure.
     371              :  * @param cmd the command which is being cleaned up.
     372              :  */
     373              : static void
     374            5 : oauth_cleanup (void *cls,
     375              :                const struct TALER_TESTING_Command *cmd)
     376              : {
     377            5 :   struct OAuthState *oas = cls;
     378              : 
     379              :   (void) cmd;
     380            5 :   if (NULL != oas->mhd)
     381              :   {
     382            5 :     MHD_stop_daemon (oas->mhd);
     383            5 :     oas->mhd = NULL;
     384              :   }
     385            5 :   GNUNET_free (oas);
     386            5 : }
     387              : 
     388              : 
     389              : struct TALER_TESTING_Command
     390            5 : TALER_TESTING_cmd_oauth_with_birthdate (const char *label,
     391              :                                         const char *birthdate,
     392              :                                         uint16_t port)
     393              : {
     394              :   struct OAuthState *oas;
     395              : 
     396            5 :   oas = GNUNET_new (struct OAuthState);
     397            5 :   oas->port = port;
     398            5 :   oas->birthdate = birthdate;
     399              :   {
     400            5 :     struct TALER_TESTING_Command cmd = {
     401              :       .cls = oas,
     402              :       .label = label,
     403              :       .run = &oauth_run,
     404              :       .cleanup = &oauth_cleanup,
     405              :     };
     406              : 
     407            5 :     return cmd;
     408              :   }
     409              : }
     410              : 
     411              : 
     412              : /* end of testing_api_cmd_oauth.c */
        

Generated by: LCOV version 2.0-1