merchant_api_patch-private-otp-devices-DEVICE_ID.c (8908B)
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_patch-private-otp-devices-DEVICE_ID.c 21 * @brief Implementation of the PATCH /private/otp-devices/$DEVICE_ID 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/patch-private-otp-devices-DEVICE_ID.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 PATCH /private/otp-devices/$DEVICE_ID operation. 39 */ 40 struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle 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_PatchPrivateOtpDeviceCallback cb; 61 62 /** 63 * Closure for @a cb. 64 */ 65 TALER_MERCHANT_PATCH_PRIVATE_OTP_DEVICE_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 to keep unchanged. 89 */ 90 char *otp_key; 91 92 /** 93 * OTP algorithm. 94 */ 95 enum TALER_MerchantConfirmationAlgorithm mca; 96 97 /** 98 * 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 PATCH /private/otp-devices/$DEVICE_ID request. 112 * 113 * @param cls the `struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle` 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_patch_otp_device_finished (void *cls, 119 long response_code, 120 const void *response) 121 { 122 struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *odh = cls; 123 const json_t *json = response; 124 struct TALER_MERCHANT_PatchPrivateOtpDeviceResponse odr = { 125 .hr.http_status = (unsigned int) response_code, 126 .hr.reply = json 127 }; 128 129 odh->job = NULL; 130 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 131 "PATCH /private/otp-devices/$DEVICE_ID 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_BAD_REQUEST: 141 odr.hr.ec = TALER_JSON_get_error_code (json); 142 odr.hr.hint = TALER_JSON_get_error_hint (json); 143 GNUNET_break_op (0); 144 break; 145 case MHD_HTTP_UNAUTHORIZED: 146 odr.hr.ec = TALER_JSON_get_error_code (json); 147 odr.hr.hint = TALER_JSON_get_error_hint (json); 148 break; 149 case MHD_HTTP_FORBIDDEN: 150 odr.hr.ec = TALER_JSON_get_error_code (json); 151 odr.hr.hint = TALER_JSON_get_error_hint (json); 152 break; 153 case MHD_HTTP_CONFLICT: 154 case MHD_HTTP_NOT_FOUND: 155 odr.hr.ec = TALER_JSON_get_error_code (json); 156 odr.hr.hint = TALER_JSON_get_error_hint (json); 157 break; 158 case MHD_HTTP_INTERNAL_SERVER_ERROR: 159 odr.hr.ec = TALER_JSON_get_error_code (json); 160 odr.hr.hint = TALER_JSON_get_error_hint (json); 161 break; 162 default: 163 TALER_MERCHANT_parse_error_details_ (json, 164 response_code, 165 &odr.hr); 166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 167 "Unexpected response code %u/%d\n", 168 (unsigned int) response_code, 169 (int) odr.hr.ec); 170 GNUNET_break_op (0); 171 break; 172 } 173 odh->cb (odh->cb_cls, 174 &odr); 175 TALER_MERCHANT_patch_private_otp_device_cancel (odh); 176 } 177 178 179 struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle * 180 TALER_MERCHANT_patch_private_otp_device_create ( 181 struct GNUNET_CURL_Context *ctx, 182 const char *url, 183 const char *otp_device_id, 184 const char *otp_device_description, 185 const char *otp_key, 186 enum TALER_MerchantConfirmationAlgorithm mca) 187 { 188 struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *odh; 189 190 odh = GNUNET_new (struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle); 191 odh->ctx = ctx; 192 odh->base_url = GNUNET_strdup (url); 193 odh->otp_device_id = GNUNET_strdup (otp_device_id); 194 odh->otp_device_description = GNUNET_strdup (otp_device_description); 195 if (NULL != otp_key) 196 odh->otp_key = GNUNET_strdup (otp_key); 197 odh->mca = mca; 198 return odh; 199 } 200 201 202 enum GNUNET_GenericReturnValue 203 TALER_MERCHANT_patch_private_otp_device_set_options_ ( 204 struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *odh, 205 unsigned int num_options, 206 const struct TALER_MERCHANT_PatchPrivateOtpDeviceOptionValue *options) 207 { 208 for (unsigned int i = 0; i < num_options; i++) 209 { 210 switch (options[i].option) 211 { 212 case TALER_MERCHANT_PATCH_PRIVATE_OTP_DEVICE_OPTION_END: 213 return GNUNET_OK; 214 case TALER_MERCHANT_PATCH_PRIVATE_OTP_DEVICE_OPTION_OTP_CTR: 215 odh->otp_ctr = options[i].details.otp_ctr; 216 odh->have_otp_ctr = true; 217 break; 218 default: 219 GNUNET_break (0); 220 return GNUNET_SYSERR; 221 } 222 } 223 return GNUNET_OK; 224 } 225 226 227 enum TALER_ErrorCode 228 TALER_MERCHANT_patch_private_otp_device_start ( 229 struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *odh, 230 TALER_MERCHANT_PatchPrivateOtpDeviceCallback cb, 231 TALER_MERCHANT_PATCH_PRIVATE_OTP_DEVICE_RESULT_CLOSURE *cb_cls) 232 { 233 json_t *req_obj; 234 CURL *eh; 235 236 odh->cb = cb; 237 odh->cb_cls = cb_cls; 238 { 239 char *path; 240 241 GNUNET_asprintf (&path, 242 "private/otp-devices/%s", 243 odh->otp_device_id); 244 odh->url = TALER_url_join (odh->base_url, 245 path, 246 NULL); 247 GNUNET_free (path); 248 } 249 if (NULL == odh->url) 250 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 251 req_obj = GNUNET_JSON_PACK ( 252 GNUNET_JSON_pack_string ("otp_device_description", 253 odh->otp_device_description), 254 GNUNET_JSON_pack_uint64 ("otp_algorithm", 255 (uint32_t) odh->mca), 256 GNUNET_JSON_pack_allow_null ( 257 GNUNET_JSON_pack_string ("otp_key", 258 odh->otp_key))); 259 if (odh->have_otp_ctr) 260 { 261 GNUNET_assert ( 262 0 == 263 json_object_set_new (req_obj, 264 "otp_ctr", 265 json_integer ((json_int_t) odh->otp_ctr))); 266 } 267 eh = TALER_MERCHANT_curl_easy_get_ (odh->url); 268 if ( (NULL == eh) || 269 (GNUNET_OK != 270 TALER_curl_easy_post (&odh->post_ctx, 271 eh, 272 req_obj)) ) 273 { 274 GNUNET_break (0); 275 json_decref (req_obj); 276 if (NULL != eh) 277 curl_easy_cleanup (eh); 278 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 279 } 280 json_decref (req_obj); 281 GNUNET_assert (CURLE_OK == 282 curl_easy_setopt (eh, 283 CURLOPT_CUSTOMREQUEST, 284 MHD_HTTP_METHOD_PATCH)); 285 odh->job = GNUNET_CURL_job_add2 (odh->ctx, 286 eh, 287 odh->post_ctx.headers, 288 &handle_patch_otp_device_finished, 289 odh); 290 if (NULL == odh->job) 291 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 292 return TALER_EC_NONE; 293 } 294 295 296 void 297 TALER_MERCHANT_patch_private_otp_device_cancel ( 298 struct TALER_MERCHANT_PatchPrivateOtpDeviceHandle *odh) 299 { 300 if (NULL != odh->job) 301 { 302 GNUNET_CURL_job_cancel (odh->job); 303 odh->job = NULL; 304 } 305 TALER_curl_easy_post_finished (&odh->post_ctx); 306 GNUNET_free (odh->otp_device_id); 307 GNUNET_free (odh->otp_device_description); 308 GNUNET_free (odh->otp_key); 309 GNUNET_free (odh->url); 310 GNUNET_free (odh->base_url); 311 GNUNET_free (odh); 312 } 313 314 315 /* end of merchant_api_patch-private-otp-devices-DEVICE_ID-new.c */