Line data Source code
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 0 : handle_account_token_finished (void *cls,
70 : long response_code,
71 : const void *response)
72 : {
73 0 : struct TALER_BANK_AccountTokenHandle *aai = cls;
74 0 : const json_t *j = response;
75 0 : struct TALER_BANK_AccountTokenResponse ir = {
76 : .http_status = response_code,
77 : .response = response
78 : };
79 :
80 0 : aai->job = NULL;
81 0 : switch (response_code)
82 : {
83 0 : case 0:
84 0 : ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
85 0 : break;
86 0 : case MHD_HTTP_OK:
87 : {
88 : struct GNUNET_JSON_Specification spec[] = {
89 0 : GNUNET_JSON_spec_string ("access_token",
90 : &ir.details.ok.access_token),
91 0 : GNUNET_JSON_spec_timestamp ("expiration",
92 : &ir.details.ok.expiration),
93 0 : GNUNET_JSON_spec_end ()
94 : };
95 :
96 0 : if (GNUNET_OK !=
97 0 : GNUNET_JSON_parse (j,
98 : spec,
99 : NULL, NULL))
100 : {
101 0 : GNUNET_break_op (0);
102 0 : ir.http_status = 0;
103 0 : ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
104 0 : break;
105 : }
106 : }
107 0 : break;
108 0 : 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 0 : GNUNET_break_op (0);
112 0 : ir.ec = TALER_JSON_get_error_code (j);
113 0 : break;
114 0 : case MHD_HTTP_FORBIDDEN:
115 : /* Access denied */
116 0 : ir.ec = TALER_JSON_get_error_code (j);
117 0 : break;
118 0 : 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 0 : ir.ec = TALER_JSON_get_error_code (j);
122 0 : break;
123 0 : 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 0 : ir.ec = TALER_JSON_get_error_code (j);
127 0 : break;
128 0 : 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 0 : ir.ec = TALER_JSON_get_error_code (j);
132 0 : break;
133 0 : default:
134 : /* unexpected response code */
135 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
136 : "Unexpected response code %u\n",
137 : (unsigned int) response_code);
138 0 : GNUNET_break (0);
139 0 : ir.ec = TALER_JSON_get_error_code (j);
140 0 : break;
141 : }
142 0 : aai->cb (aai->cb_cls,
143 : &ir);
144 0 : TALER_BANK_account_token_cancel (aai);
145 0 : }
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 0 : scope_to_string (enum TALER_BANK_TokenScope scope)
156 : {
157 0 : switch (scope)
158 : {
159 0 : case TALER_BANK_TOKEN_SCOPE_READONLY:
160 0 : return "readonly";
161 0 : case TALER_BANK_TOKEN_SCOPE_READWRITE:
162 0 : return "readwrite";
163 0 : case TALER_BANK_TOKEN_SCOPE_REVENUE:
164 0 : return "revenue";
165 0 : case TALER_BANK_TOKEN_SCOPE_WIREGATEWAY:
166 0 : return "wiregateway";
167 : }
168 0 : GNUNET_break (0);
169 0 : return NULL;
170 : }
171 :
172 :
173 : struct TALER_BANK_AccountTokenHandle *
174 0 : 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 0 : 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 0 : if (NULL == token_req)
202 : {
203 0 : GNUNET_break (0);
204 0 : return NULL;
205 : }
206 0 : ath = GNUNET_new (struct TALER_BANK_AccountTokenHandle);
207 0 : ath->cb = res_cb;
208 0 : ath->cb_cls = res_cb_cls;
209 : {
210 : char *path;
211 :
212 0 : GNUNET_asprintf (&path,
213 : "accounts/%s/token",
214 : account_name);
215 0 : ath->request_url = TALER_url_join (auth->wire_gateway_url,
216 : path,
217 : NULL);
218 0 : GNUNET_free (path);
219 : }
220 0 : if (NULL == ath->request_url)
221 : {
222 0 : GNUNET_free (ath);
223 0 : json_decref (token_req);
224 0 : return NULL;
225 : }
226 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
227 : "Requesting access token at `%s'\n",
228 : ath->request_url);
229 : ath->post_ctx.headers
230 0 : = curl_slist_append (
231 : ath->post_ctx.headers,
232 : "Content-Type: application/json");
233 0 : eh = curl_easy_init ();
234 0 : if ( (NULL == eh) ||
235 : (GNUNET_OK !=
236 0 : TALER_BANK_setup_auth_ (eh,
237 0 : auth)) ||
238 : (CURLE_OK !=
239 0 : curl_easy_setopt (eh,
240 : CURLOPT_URL,
241 0 : ath->request_url)) ||
242 : (GNUNET_OK !=
243 0 : TALER_curl_easy_post (&ath->post_ctx,
244 : eh,
245 : token_req)) )
246 : {
247 0 : GNUNET_break (0);
248 0 : TALER_BANK_account_token_cancel (ath);
249 0 : if (NULL != eh)
250 0 : curl_easy_cleanup (eh);
251 0 : json_decref (token_req);
252 0 : return NULL;
253 : }
254 0 : json_decref (token_req);
255 0 : ath->job = GNUNET_CURL_job_add2 (ctx,
256 : eh,
257 0 : ath->post_ctx.headers,
258 : &handle_account_token_finished,
259 : ath);
260 0 : GNUNET_assert (NULL != ath->job);
261 0 : return ath;
262 : }
263 :
264 :
265 : void
266 0 : TALER_BANK_account_token_cancel (
267 : struct TALER_BANK_AccountTokenHandle *ath)
268 : {
269 0 : if (NULL != ath->job)
270 : {
271 0 : GNUNET_CURL_job_cancel (ath->job);
272 0 : ath->job = NULL;
273 : }
274 0 : TALER_curl_easy_post_finished (&ath->post_ctx);
275 0 : GNUNET_free (ath->request_url);
276 0 : GNUNET_free (ath);
277 0 : }
278 :
279 :
280 : /* end of bank_api_account_token.c */
|