Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2023, 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 lib/exchange_api_lookup_kyc_attributes.c
19 : * @brief Implementation of the /aml/$OFFICER_PUB/attributes request
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include <microhttpd.h> /* just for HTTP status codes */
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <gnunet/gnunet_curl_lib.h>
26 : #include "taler_exchange_service.h"
27 : #include "taler_json_lib.h"
28 : #include "exchange_api_handle.h"
29 : #include "taler_signatures.h"
30 : #include "exchange_api_curl_defaults.h"
31 :
32 :
33 : /**
34 : * @brief A GET /aml/$OFFICER_PUB/attributes Handle
35 : */
36 : struct TALER_EXCHANGE_LookupKycAttributes
37 : {
38 :
39 : /**
40 : * The url for this request.
41 : */
42 : char *url;
43 :
44 : /**
45 : * Handle for the request.
46 : */
47 : struct GNUNET_CURL_Job *job;
48 :
49 : /**
50 : * Function to call with the result.
51 : */
52 : TALER_EXCHANGE_LookupKycAttributesCallback attributes_cb;
53 :
54 : /**
55 : * Closure for @e cb.
56 : */
57 : void *attributes_cb_cls;
58 :
59 : /**
60 : * HTTP headers for the job.
61 : */
62 : struct curl_slist *job_headers;
63 :
64 : };
65 :
66 :
67 : /**
68 : * Parse AML decision summary array.
69 : *
70 : * @param[in,out] lh handle to use for allocations
71 : * @param jdetails JSON array with AML decision summaries
72 : * @param[out] detail_ar where to write the result
73 : * @return #GNUNET_OK on success
74 : */
75 : static enum GNUNET_GenericReturnValue
76 0 : parse_kyc_attributes (
77 : struct TALER_EXCHANGE_LookupKycAttributes *lh,
78 : const json_t *jdetails,
79 : struct TALER_EXCHANGE_KycAttributeDetail *detail_ar)
80 : {
81 : json_t *obj;
82 : size_t idx;
83 :
84 0 : json_array_foreach (jdetails, idx, obj)
85 : {
86 0 : struct TALER_EXCHANGE_KycAttributeDetail *detail
87 0 : = &detail_ar[idx];
88 : struct GNUNET_JSON_Specification spec[] = {
89 0 : GNUNET_JSON_spec_uint64 ("rowid",
90 : &detail->row_id),
91 0 : GNUNET_JSON_spec_mark_optional (
92 : GNUNET_JSON_spec_string ("provider_name",
93 : &detail->provider_name),
94 : NULL),
95 0 : GNUNET_JSON_spec_object_const ("attributes",
96 : &detail->attributes),
97 0 : GNUNET_JSON_spec_timestamp ("collection_time",
98 : &detail->collection_time),
99 0 : GNUNET_JSON_spec_end ()
100 : };
101 :
102 0 : if (GNUNET_OK !=
103 0 : GNUNET_JSON_parse (obj,
104 : spec,
105 : NULL,
106 : NULL))
107 : {
108 0 : GNUNET_break_op (0);
109 0 : return GNUNET_SYSERR;
110 : }
111 : }
112 0 : return GNUNET_OK;
113 : }
114 :
115 :
116 : /**
117 : * Parse the provided decision data from the "200 OK" response.
118 : *
119 : * @param[in,out] lh handle (callback may be zero'ed out)
120 : * @param json json reply with the data for one coin
121 : * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
122 : */
123 : static enum GNUNET_GenericReturnValue
124 0 : parse_attributes_ok (struct TALER_EXCHANGE_LookupKycAttributes *lh,
125 : const json_t *json)
126 : {
127 0 : struct TALER_EXCHANGE_KycAttributesResponse lr = {
128 : .hr.reply = json,
129 : .hr.http_status = MHD_HTTP_OK
130 : };
131 : const json_t *jdetails;
132 : struct GNUNET_JSON_Specification spec[] = {
133 0 : GNUNET_JSON_spec_array_const ("details",
134 : &jdetails),
135 0 : GNUNET_JSON_spec_end ()
136 : };
137 :
138 0 : if (GNUNET_OK !=
139 0 : GNUNET_JSON_parse (json,
140 : spec,
141 : NULL,
142 : NULL))
143 : {
144 0 : GNUNET_break_op (0);
145 0 : return GNUNET_SYSERR;
146 : }
147 : lr.details.ok.kyc_attributes_length
148 0 : = json_array_size (jdetails);
149 0 : {
150 0 : struct TALER_EXCHANGE_KycAttributeDetail details[
151 0 : GNUNET_NZL (lr.details.ok.kyc_attributes_length)];
152 0 : enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
153 :
154 0 : memset (details,
155 : 0,
156 : sizeof (details));
157 0 : lr.details.ok.kyc_attributes = details;
158 0 : ret = parse_kyc_attributes (lh,
159 : jdetails,
160 : details);
161 0 : if (GNUNET_OK == ret)
162 : {
163 0 : lh->attributes_cb (lh->attributes_cb_cls,
164 : &lr);
165 0 : lh->attributes_cb = NULL;
166 : }
167 0 : return ret;
168 : }
169 : }
170 :
171 :
172 : /**
173 : * Function called when we're done processing the
174 : * HTTP /aml/$OFFICER_PUB/attributes request.
175 : *
176 : * @param cls the `struct TALER_EXCHANGE_LookupKycAttributes`
177 : * @param response_code HTTP response code, 0 on error
178 : * @param response parsed JSON result, NULL on error
179 : */
180 : static void
181 0 : handle_lookup_finished (void *cls,
182 : long response_code,
183 : const void *response)
184 : {
185 0 : struct TALER_EXCHANGE_LookupKycAttributes *lh = cls;
186 0 : const json_t *j = response;
187 0 : struct TALER_EXCHANGE_KycAttributesResponse lr = {
188 : .hr.reply = j,
189 0 : .hr.http_status = (unsigned int) response_code
190 : };
191 :
192 0 : lh->job = NULL;
193 0 : switch (response_code)
194 : {
195 0 : case 0:
196 0 : lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
197 0 : break;
198 0 : case MHD_HTTP_OK:
199 0 : if (GNUNET_OK !=
200 0 : parse_attributes_ok (lh,
201 : j))
202 : {
203 0 : GNUNET_break_op (0);
204 0 : lr.hr.http_status = 0;
205 0 : lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
206 0 : break;
207 : }
208 0 : GNUNET_assert (NULL == lh->attributes_cb);
209 0 : TALER_EXCHANGE_lookup_kyc_attributes_cancel (lh);
210 0 : return;
211 0 : case MHD_HTTP_NO_CONTENT:
212 0 : break;
213 0 : case MHD_HTTP_BAD_REQUEST:
214 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
215 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
216 : /* This should never happen, either us or the exchange is buggy
217 : (or API version conflict); just pass JSON reply to the application */
218 0 : break;
219 0 : case MHD_HTTP_FORBIDDEN:
220 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
221 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
222 : /* Nothing really to verify, exchange says this coin was not melted; we
223 : should pass the JSON reply to the application */
224 0 : break;
225 0 : case MHD_HTTP_NOT_FOUND:
226 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
227 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
228 : /* Nothing really to verify, exchange says this coin was not melted; we
229 : should pass the JSON reply to the application */
230 0 : break;
231 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
232 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
233 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
234 : /* Server had an internal issue; we should retry, but this API
235 : leaves this to the application */
236 0 : break;
237 0 : default:
238 : /* unexpected response code */
239 0 : GNUNET_break_op (0);
240 0 : lr.hr.ec = TALER_JSON_get_error_code (j);
241 0 : lr.hr.hint = TALER_JSON_get_error_hint (j);
242 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
243 : "Unexpected response code %u/%d for lookup AML attributes\n",
244 : (unsigned int) response_code,
245 : (int) lr.hr.ec);
246 0 : break;
247 : }
248 0 : if (NULL != lh->attributes_cb)
249 0 : lh->attributes_cb (lh->attributes_cb_cls,
250 : &lr);
251 0 : TALER_EXCHANGE_lookup_kyc_attributes_cancel (lh);
252 : }
253 :
254 :
255 : struct TALER_EXCHANGE_LookupKycAttributes *
256 0 : TALER_EXCHANGE_lookup_kyc_attributes (
257 : struct GNUNET_CURL_Context *ctx,
258 : const char *exchange_url,
259 : const struct TALER_NormalizedPaytoHashP *h_payto,
260 : uint64_t offset,
261 : int64_t limit,
262 : const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
263 : TALER_EXCHANGE_LookupKycAttributesCallback cb,
264 : void *cb_cls)
265 : {
266 : struct TALER_EXCHANGE_LookupKycAttributes *lh;
267 : CURL *eh;
268 : struct TALER_AmlOfficerPublicKeyP officer_pub;
269 : struct TALER_AmlOfficerSignatureP officer_sig;
270 : char arg_str[sizeof (officer_pub) * 2
271 : + sizeof (*h_payto) * 2
272 : + 32];
273 :
274 0 : GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
275 : &officer_pub.eddsa_pub);
276 0 : TALER_officer_aml_query_sign (officer_priv,
277 : &officer_sig);
278 :
279 : {
280 : char payto_s[sizeof (*h_payto) * 2];
281 : char pub_str[sizeof (officer_pub) * 2];
282 : char *end;
283 :
284 0 : end = GNUNET_STRINGS_data_to_string (
285 : h_payto,
286 : sizeof (*h_payto),
287 : payto_s,
288 : sizeof (payto_s));
289 0 : *end = '\0';
290 0 : end = GNUNET_STRINGS_data_to_string (
291 : &officer_pub,
292 : sizeof (officer_pub),
293 : pub_str,
294 : sizeof (pub_str));
295 0 : *end = '\0';
296 0 : GNUNET_snprintf (arg_str,
297 : sizeof (arg_str),
298 : "aml/%s/attributes/%s",
299 : pub_str,
300 : payto_s);
301 : }
302 0 : lh = GNUNET_new (struct TALER_EXCHANGE_LookupKycAttributes);
303 0 : lh->attributes_cb = cb;
304 0 : lh->attributes_cb_cls = cb_cls;
305 : {
306 : char limit_s[24];
307 : char offset_s[24];
308 :
309 0 : GNUNET_snprintf (limit_s,
310 : sizeof (limit_s),
311 : "%lld",
312 : (long long) limit);
313 0 : GNUNET_snprintf (offset_s,
314 : sizeof (offset_s),
315 : "%llu",
316 : (unsigned long long) offset);
317 0 : lh->url = TALER_url_join (
318 : exchange_url,
319 : arg_str,
320 : "limit",
321 : limit_s,
322 : "offset",
323 : offset_s,
324 : "h_payto",
325 : NULL);
326 : }
327 0 : if (NULL == lh->url)
328 : {
329 0 : GNUNET_free (lh);
330 0 : return NULL;
331 : }
332 0 : eh = TALER_EXCHANGE_curl_easy_get_ (lh->url);
333 0 : if (NULL == eh)
334 : {
335 0 : GNUNET_break (0);
336 0 : GNUNET_free (lh->url);
337 0 : GNUNET_free (lh);
338 0 : return NULL;
339 : }
340 : {
341 : char *hdr;
342 : char sig_str[sizeof (officer_sig) * 2];
343 : char *end;
344 :
345 0 : end = GNUNET_STRINGS_data_to_string (
346 : &officer_sig,
347 : sizeof (officer_sig),
348 : sig_str,
349 : sizeof (sig_str));
350 0 : *end = '\0';
351 :
352 0 : GNUNET_asprintf (&hdr,
353 : "%s: %s",
354 : TALER_AML_OFFICER_SIGNATURE_HEADER,
355 : sig_str);
356 0 : lh->job_headers = curl_slist_append (NULL,
357 : hdr);
358 0 : GNUNET_free (hdr);
359 0 : lh->job_headers = curl_slist_append (lh->job_headers,
360 : "Content-type: application/json");
361 0 : lh->job = GNUNET_CURL_job_add2 (ctx,
362 : eh,
363 0 : lh->job_headers,
364 : &handle_lookup_finished,
365 : lh);
366 : }
367 0 : return lh;
368 : }
369 :
370 :
371 : void
372 0 : TALER_EXCHANGE_lookup_kyc_attributes_cancel (
373 : struct TALER_EXCHANGE_LookupKycAttributes *lh)
374 : {
375 0 : if (NULL != lh->job)
376 : {
377 0 : GNUNET_CURL_job_cancel (lh->job);
378 0 : lh->job = NULL;
379 : }
380 0 : curl_slist_free_all (lh->job_headers);
381 0 : GNUNET_free (lh->url);
382 0 : GNUNET_free (lh);
383 0 : }
384 :
385 :
386 : /* end of exchange_api_lookup_kyc_attributes.c */
|