bank_api_account_token.c (7676B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2015--2024 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, 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 General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see 15 <http://www.gnu.org/licenses/> 16 */ 17 /** 18 * @file bank-lib/bank_api_account_token.c 19 * @brief Implementation of the /account/$ACC/token requests of the bank's HTTP API 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include "bank_api_common.h" 24 #include <microhttpd.h> /* just for HTTP status codes */ 25 #include "taler/taler_signatures.h" 26 #include "taler/taler_curl_lib.h" 27 28 29 struct TALER_BANK_AccountTokenHandle 30 { 31 32 /** 33 * The url for this request. 34 */ 35 char *request_url; 36 37 /** 38 * POST context. 39 */ 40 struct TALER_CURL_PostContext post_ctx; 41 42 /** 43 * Handle for the request. 44 */ 45 struct GNUNET_CURL_Job *job; 46 47 /** 48 * Function to call with the result. 49 */ 50 TALER_BANK_AccountTokenCallback cb; 51 52 /** 53 * Closure for @a cb. 54 */ 55 void *cb_cls; 56 57 }; 58 59 60 /** 61 * Function called when we're done processing the 62 * HTTP /account/$ACC/token request. 63 * 64 * @param cls the `struct TALER_BANK_AccountTokenHandle` 65 * @param response_code HTTP response code, 0 on error 66 * @param response parsed JSON result, NULL on error 67 */ 68 static void 69 handle_account_token_finished (void *cls, 70 long response_code, 71 const void *response) 72 { 73 struct TALER_BANK_AccountTokenHandle *aai = cls; 74 const json_t *j = response; 75 struct TALER_BANK_AccountTokenResponse ir = { 76 .http_status = response_code, 77 .response = response 78 }; 79 80 aai->job = NULL; 81 switch (response_code) 82 { 83 case 0: 84 ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 85 break; 86 case MHD_HTTP_OK: 87 { 88 struct GNUNET_JSON_Specification spec[] = { 89 GNUNET_JSON_spec_string ("access_token", 90 &ir.details.ok.access_token), 91 GNUNET_JSON_spec_timestamp ("expiration", 92 &ir.details.ok.expiration), 93 GNUNET_JSON_spec_end () 94 }; 95 96 if (GNUNET_OK != 97 GNUNET_JSON_parse (j, 98 spec, 99 NULL, NULL)) 100 { 101 GNUNET_break_op (0); 102 ir.http_status = 0; 103 ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 104 break; 105 } 106 } 107 break; 108 case MHD_HTTP_BAD_REQUEST: 109 /* This should never happen, either us or the bank is buggy 110 (or API version conflict); just pass JSON reply to the application */ 111 GNUNET_break_op (0); 112 ir.ec = TALER_JSON_get_error_code (j); 113 break; 114 case MHD_HTTP_FORBIDDEN: 115 /* Access denied */ 116 ir.ec = TALER_JSON_get_error_code (j); 117 break; 118 case MHD_HTTP_UNAUTHORIZED: 119 /* Nothing really to verify, bank says the password is invalid; we should 120 pass the JSON reply to the application */ 121 ir.ec = TALER_JSON_get_error_code (j); 122 break; 123 case MHD_HTTP_NOT_FOUND: 124 /* Nothing really to verify, maybe account really does not exist. 125 We should pass the JSON reply to the application */ 126 ir.ec = TALER_JSON_get_error_code (j); 127 break; 128 case MHD_HTTP_INTERNAL_SERVER_ERROR: 129 /* Server had an internal issue; we should retry, but this API 130 leaves this to the application */ 131 ir.ec = TALER_JSON_get_error_code (j); 132 break; 133 default: 134 /* unexpected response code */ 135 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 136 "Unexpected response code %u\n", 137 (unsigned int) response_code); 138 GNUNET_break (0); 139 ir.ec = TALER_JSON_get_error_code (j); 140 break; 141 } 142 aai->cb (aai->cb_cls, 143 &ir); 144 TALER_BANK_account_token_cancel (aai); 145 } 146 147 148 /** 149 * Convert @a scope to string. 150 * 151 * @param scope a scope 152 * @return string encoding of the scope 153 */ 154 static const char * 155 scope_to_string (enum TALER_BANK_TokenScope scope) 156 { 157 switch (scope) 158 { 159 case TALER_BANK_TOKEN_SCOPE_READONLY: 160 return "readonly"; 161 case TALER_BANK_TOKEN_SCOPE_READWRITE: 162 return "readwrite"; 163 case TALER_BANK_TOKEN_SCOPE_REVENUE: 164 return "revenue"; 165 case TALER_BANK_TOKEN_SCOPE_WIREGATEWAY: 166 return "wiregateway"; 167 } 168 GNUNET_break (0); 169 return NULL; 170 } 171 172 173 struct TALER_BANK_AccountTokenHandle * 174 TALER_BANK_account_token ( 175 struct GNUNET_CURL_Context *ctx, 176 const struct TALER_BANK_AuthenticationData *auth, 177 const char *account_name, 178 enum TALER_BANK_TokenScope scope, 179 bool refreshable, 180 const char *description, 181 struct GNUNET_TIME_Relative duration, 182 TALER_BANK_AccountTokenCallback res_cb, 183 void *res_cb_cls) 184 { 185 struct TALER_BANK_AccountTokenHandle *ath; 186 json_t *token_req; 187 CURL *eh; 188 189 token_req = GNUNET_JSON_PACK ( 190 GNUNET_JSON_pack_string ("scope", 191 scope_to_string (scope)), 192 GNUNET_JSON_pack_allow_null ( 193 GNUNET_JSON_pack_string ("description", 194 description)), 195 GNUNET_JSON_pack_allow_null ( 196 GNUNET_JSON_pack_time_rel ("duration", 197 duration)), 198 GNUNET_JSON_pack_allow_null ( 199 GNUNET_JSON_pack_bool ("refreshable", 200 refreshable))); 201 if (NULL == token_req) 202 { 203 GNUNET_break (0); 204 return NULL; 205 } 206 ath = GNUNET_new (struct TALER_BANK_AccountTokenHandle); 207 ath->cb = res_cb; 208 ath->cb_cls = res_cb_cls; 209 { 210 char *path; 211 212 GNUNET_asprintf (&path, 213 "accounts/%s/token", 214 account_name); 215 ath->request_url = TALER_url_join (auth->wire_gateway_url, 216 path, 217 NULL); 218 GNUNET_free (path); 219 } 220 if (NULL == ath->request_url) 221 { 222 GNUNET_free (ath); 223 json_decref (token_req); 224 return NULL; 225 } 226 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 227 "Requesting access token at `%s'\n", 228 ath->request_url); 229 ath->post_ctx.headers 230 = curl_slist_append ( 231 ath->post_ctx.headers, 232 "Content-Type: application/json"); 233 eh = curl_easy_init (); 234 if ( (NULL == eh) || 235 (GNUNET_OK != 236 TALER_BANK_setup_auth_ (eh, 237 auth)) || 238 (CURLE_OK != 239 curl_easy_setopt (eh, 240 CURLOPT_URL, 241 ath->request_url)) || 242 (GNUNET_OK != 243 TALER_curl_easy_post (&ath->post_ctx, 244 eh, 245 token_req)) ) 246 { 247 GNUNET_break (0); 248 TALER_BANK_account_token_cancel (ath); 249 if (NULL != eh) 250 curl_easy_cleanup (eh); 251 json_decref (token_req); 252 return NULL; 253 } 254 json_decref (token_req); 255 ath->job = GNUNET_CURL_job_add2 (ctx, 256 eh, 257 ath->post_ctx.headers, 258 &handle_account_token_finished, 259 ath); 260 GNUNET_assert (NULL != ath->job); 261 return ath; 262 } 263 264 265 void 266 TALER_BANK_account_token_cancel ( 267 struct TALER_BANK_AccountTokenHandle *ath) 268 { 269 if (NULL != ath->job) 270 { 271 GNUNET_CURL_job_cancel (ath->job); 272 ath->job = NULL; 273 } 274 TALER_curl_easy_post_finished (&ath->post_ctx); 275 GNUNET_free (ath->request_url); 276 GNUNET_free (ath); 277 } 278 279 280 /* end of bank_api_account_token.c */