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 */