merchant_api_post_instance_token.c (6756B)
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 it under the 6 terms of the GNU Lesser General Public License as published by the Free Software 7 Foundation; either version 2.1, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 13 You should have received a copy of the GNU Lesser General Public License along with 14 TALER; see the file COPYING.LGPL. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file merchant_api_post_instance_token.c 19 * @brief Implementation of the POST /instance/$ID/private/token request 20 * @author Martin Schanzenbach 21 */ 22 #include "platform.h" 23 #include <curl/curl.h> 24 #include <jansson.h> 25 #include <microhttpd.h> /* just for HTTP status codes */ 26 #include <gnunet/gnunet_util_lib.h> 27 #include <gnunet/gnunet_curl_lib.h> 28 #include "taler_merchant_service.h" 29 #include "merchant_api_curl_defaults.h" 30 #include <taler/taler_json_lib.h> 31 #include <taler/taler_signatures.h> 32 #include <taler/taler_curl_lib.h> 33 34 35 /** 36 * Handle for a POST /instances/$ID/private/token operation. 37 */ 38 struct TALER_MERCHANT_InstanceTokenPostHandle 39 { 40 /** 41 * The url for this request. 42 */ 43 char *url; 44 45 /** 46 * Handle for the request. 47 */ 48 struct GNUNET_CURL_Job *job; 49 50 /** 51 * Function to call with the result. 52 */ 53 TALER_MERCHANT_InstanceTokenPostCallback cb; 54 55 /** 56 * Closure for @a cb. 57 */ 58 void *cb_cls; 59 60 /** 61 * Reference to the execution context. 62 */ 63 struct GNUNET_CURL_Context *ctx; 64 65 /** 66 * Minor context that holds body and headers. 67 */ 68 struct TALER_CURL_PostContext post_ctx; 69 70 }; 71 72 73 /** 74 * Function called when we're done processing the 75 * HTTP GET /instances/$ID/private/token request. 76 * 77 * @param cls the `struct TALER_MERCHANT_InstanceTokenPostHandle` 78 * @param response_code HTTP response code, 0 on error 79 * @param response response body, NULL if not in JSON 80 */ 81 static void 82 handle_post_instance_token_finished (void *cls, 83 long response_code, 84 const void *response) 85 { 86 struct TALER_MERCHANT_InstanceTokenPostHandle *itph = cls; 87 const json_t *json = response; 88 struct TALER_MERCHANT_HttpResponse hr = { 89 .http_status = (unsigned int) response_code, 90 .reply = json 91 }; 92 93 itph->job = NULL; 94 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 95 "Got /instances/$ID response with status code %u\n", 96 (unsigned int) response_code); 97 switch (response_code) 98 { 99 case MHD_HTTP_OK: 100 break; 101 case MHD_HTTP_BAD_REQUEST: 102 /* happens if the auth token is malformed */ 103 hr.ec = TALER_JSON_get_error_code (json); 104 hr.hint = TALER_JSON_get_error_hint (json); 105 /* Nothing really to verify, merchant says we need to authenticate. */ 106 break; 107 case MHD_HTTP_UNAUTHORIZED: 108 hr.ec = TALER_JSON_get_error_code (json); 109 hr.hint = TALER_JSON_get_error_hint (json); 110 /* Nothing really to verify, merchant says we need to authenticate. */ 111 break; 112 default: 113 /* unexpected response code */ 114 hr.ec = TALER_JSON_get_error_code (json); 115 hr.hint = TALER_JSON_get_error_hint (json); 116 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 117 "Unexpected response code %u/%d\n", 118 (unsigned int) response_code, 119 (int) hr.ec); 120 break; 121 } 122 itph->cb (itph->cb_cls, 123 &hr); 124 TALER_MERCHANT_instance_token_post_cancel (itph); 125 } 126 127 128 struct TALER_MERCHANT_InstanceTokenPostHandle * 129 TALER_MERCHANT_instance_token_post ( 130 struct GNUNET_CURL_Context *ctx, 131 const char *backend_url, 132 const char *instance_id, 133 const char *scope, 134 struct GNUNET_TIME_Relative duration, 135 bool refreshable, 136 TALER_MERCHANT_InstanceTokenPostCallback cb, 137 void *cb_cls) 138 { 139 struct TALER_MERCHANT_InstanceTokenPostHandle *itph; 140 json_t *req_obj; 141 142 itph = GNUNET_new (struct TALER_MERCHANT_InstanceTokenPostHandle); 143 itph->ctx = ctx; 144 itph->cb = cb; 145 itph->cb_cls = cb_cls; 146 if (NULL != instance_id) 147 { 148 char *path; 149 150 GNUNET_asprintf (&path, 151 "instances/%s/private/token", 152 instance_id); 153 itph->url = TALER_url_join (backend_url, 154 path, 155 NULL); 156 GNUNET_free (path); 157 } 158 else 159 { 160 /* backend_url is already identifying the instance */ 161 itph->url = TALER_url_join (backend_url, 162 "private/token", 163 NULL); 164 } 165 if (NULL == itph->url) 166 { 167 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 168 "Could not construct request URL.\n"); 169 GNUNET_free (itph); 170 return NULL; 171 } 172 if (NULL == scope) 173 { 174 req_obj = GNUNET_JSON_PACK ( 175 GNUNET_JSON_pack_time_rel ("duration", 176 duration), 177 GNUNET_JSON_pack_bool ("refreshable", 178 refreshable), 179 GNUNET_JSON_pack_string ("scope", 180 "readonly")); 181 } 182 else 183 { 184 req_obj = GNUNET_JSON_PACK ( 185 GNUNET_JSON_pack_time_rel ("duration", 186 duration), 187 GNUNET_JSON_pack_bool ("refreshable", 188 refreshable), 189 GNUNET_JSON_pack_string ("scope", 190 scope)); 191 } 192 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 193 "Requesting URL '%s'\n", 194 itph->url); 195 { 196 CURL *eh; 197 198 eh = TALER_MERCHANT_curl_easy_get_ (itph->url); 199 if (GNUNET_OK != 200 TALER_curl_easy_post (&itph->post_ctx, 201 eh, 202 req_obj)) 203 { 204 GNUNET_break (0); 205 curl_easy_cleanup (eh); 206 json_decref (req_obj); 207 GNUNET_free (itph->url); 208 GNUNET_free (itph); 209 return NULL; 210 } 211 json_decref (req_obj); 212 GNUNET_assert (CURLE_OK == 213 curl_easy_setopt (eh, 214 CURLOPT_CUSTOMREQUEST, 215 MHD_HTTP_METHOD_POST)); 216 itph->job = GNUNET_CURL_job_add2 (ctx, 217 eh, 218 itph->post_ctx.headers, 219 &handle_post_instance_token_finished, 220 itph); 221 } 222 return itph; 223 } 224 225 226 void 227 TALER_MERCHANT_instance_token_post_cancel ( 228 struct TALER_MERCHANT_InstanceTokenPostHandle *itph) 229 { 230 if (NULL != itph->job) 231 GNUNET_CURL_job_cancel (itph->job); 232 TALER_curl_easy_post_finished (&itph->post_ctx); 233 GNUNET_free (itph->url); 234 GNUNET_free (itph); 235 }