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_add_aml_decision.c
19 : * @brief functions to add an AML decision by an AML officer
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include "taler_json_lib.h"
24 : #include <microhttpd.h>
25 : #include <gnunet/gnunet_curl_lib.h>
26 : #include "taler_exchange_service.h"
27 : #include "exchange_api_curl_defaults.h"
28 : #include "taler_signatures.h"
29 : #include "taler_curl_lib.h"
30 : #include "taler_json_lib.h"
31 :
32 :
33 : struct TALER_EXCHANGE_AddAmlDecision
34 : {
35 :
36 : /**
37 : * The url for this request.
38 : */
39 : char *url;
40 :
41 : /**
42 : * Minor context that holds body and headers.
43 : */
44 : struct TALER_CURL_PostContext post_ctx;
45 :
46 : /**
47 : * Handle for the request.
48 : */
49 : struct GNUNET_CURL_Job *job;
50 :
51 : /**
52 : * Function to call with the result.
53 : */
54 : TALER_EXCHANGE_AddAmlDecisionCallback cb;
55 :
56 : /**
57 : * Closure for @a cb.
58 : */
59 : void *cb_cls;
60 :
61 : /**
62 : * Reference to the execution context.
63 : */
64 : struct GNUNET_CURL_Context *ctx;
65 : };
66 :
67 :
68 : /**
69 : * Function called when we're done processing the
70 : * HTTP POST /aml/$OFFICER_PUB/decision request.
71 : *
72 : * @param cls the `struct TALER_EXCHANGE_AddAmlDecision *`
73 : * @param response_code HTTP response code, 0 on error
74 : * @param response response body, NULL if not in JSON
75 : */
76 : static void
77 3 : handle_add_aml_decision_finished (void *cls,
78 : long response_code,
79 : const void *response)
80 : {
81 3 : struct TALER_EXCHANGE_AddAmlDecision *wh = cls;
82 3 : const json_t *json = response;
83 3 : struct TALER_EXCHANGE_AddAmlDecisionResponse adr = {
84 3 : .hr.http_status = (unsigned int) response_code,
85 : .hr.reply = json
86 : };
87 :
88 3 : wh->job = NULL;
89 3 : switch (response_code)
90 : {
91 0 : case 0:
92 : /* no reply */
93 0 : adr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
94 0 : adr.hr.hint = "server offline?";
95 0 : break;
96 2 : case MHD_HTTP_NO_CONTENT:
97 2 : break;
98 1 : case MHD_HTTP_FORBIDDEN:
99 1 : adr.hr.ec = TALER_JSON_get_error_code (json);
100 1 : adr.hr.hint = TALER_JSON_get_error_hint (json);
101 1 : break;
102 0 : case MHD_HTTP_CONFLICT:
103 0 : adr.hr.ec = TALER_JSON_get_error_code (json);
104 0 : adr.hr.hint = TALER_JSON_get_error_hint (json);
105 0 : break;
106 0 : default:
107 : /* unexpected response code */
108 0 : GNUNET_break_op (0);
109 0 : adr.hr.ec = TALER_JSON_get_error_code (json);
110 0 : adr.hr.hint = TALER_JSON_get_error_hint (json);
111 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
112 : "Unexpected response code %u/%d for exchange AML decision\n",
113 : (unsigned int) response_code,
114 : (int) adr.hr.ec);
115 0 : break;
116 : }
117 3 : if (NULL != wh->cb)
118 : {
119 3 : wh->cb (wh->cb_cls,
120 : &adr);
121 3 : wh->cb = NULL;
122 : }
123 3 : TALER_EXCHANGE_post_aml_decision_cancel (wh);
124 3 : }
125 :
126 :
127 : struct TALER_EXCHANGE_AddAmlDecision *
128 3 : TALER_EXCHANGE_post_aml_decision (
129 : struct GNUNET_CURL_Context *ctx,
130 : const char *url,
131 : const struct TALER_NormalizedPaytoHashP *h_payto,
132 : const struct TALER_FullPayto payto_uri,
133 : struct GNUNET_TIME_Timestamp decision_time,
134 : const char *successor_measure,
135 : const char *new_measures,
136 : struct GNUNET_TIME_Timestamp expiration_time,
137 : unsigned int num_rules,
138 : const struct TALER_EXCHANGE_AccountRule *rules,
139 : unsigned int num_measures,
140 : const struct TALER_EXCHANGE_MeasureInformation *measures,
141 : const json_t *properties,
142 : bool keep_investigating,
143 : const char *justification,
144 : const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
145 : unsigned int num_events,
146 : const char **events,
147 : TALER_EXCHANGE_AddAmlDecisionCallback cb,
148 : void *cb_cls)
149 : {
150 : struct TALER_AmlOfficerPublicKeyP officer_pub;
151 : struct TALER_AmlOfficerSignatureP officer_sig;
152 : struct TALER_EXCHANGE_AddAmlDecision *wh;
153 : CURL *eh;
154 : json_t *body;
155 : json_t *new_rules;
156 : json_t *jrules;
157 : json_t *jmeasures;
158 3 : json_t *jevents = NULL;
159 :
160 3 : if (0 != num_events)
161 : {
162 0 : jevents = json_array ();
163 0 : GNUNET_assert (NULL != jevents);
164 0 : for (unsigned int i = 0; i<num_events; i++)
165 0 : GNUNET_assert (0 ==
166 : json_array_append_new (jevents,
167 : json_string (events[i])));
168 : }
169 3 : jrules = json_array ();
170 3 : GNUNET_assert (NULL != jrules);
171 6 : for (unsigned int i = 0; i<num_rules; i++)
172 : {
173 3 : const struct TALER_EXCHANGE_AccountRule *al = &rules[i];
174 : json_t *rule;
175 : json_t *ameasures;
176 :
177 3 : ameasures = json_array ();
178 3 : GNUNET_assert (NULL != ameasures);
179 4 : for (unsigned int j = 0; j<al->num_measures; j++)
180 1 : GNUNET_assert (0 ==
181 : json_array_append_new (ameasures,
182 : json_string (al->measures[j])));
183 3 : rule = GNUNET_JSON_PACK (
184 : TALER_JSON_pack_kycte ("operation_type",
185 : al->operation_type),
186 : TALER_JSON_pack_amount ("threshold",
187 : &al->threshold),
188 : GNUNET_JSON_pack_time_rel ("timeframe",
189 : al->timeframe),
190 : GNUNET_JSON_pack_array_steal ("measures",
191 : ameasures),
192 : GNUNET_JSON_pack_allow_null (
193 : GNUNET_JSON_pack_array_steal ("events",
194 : jevents)),
195 : GNUNET_JSON_pack_bool ("exposed",
196 : al->exposed),
197 : GNUNET_JSON_pack_bool ("is_and_combinator",
198 : al->is_and_combinator),
199 : GNUNET_JSON_pack_uint64 ("display_priority",
200 : al->display_priority)
201 : );
202 3 : GNUNET_break (0 ==
203 : json_array_append_new (jrules,
204 : rule));
205 : }
206 :
207 3 : jmeasures = json_object ();
208 3 : GNUNET_assert (NULL != jmeasures);
209 4 : for (unsigned int i = 0; i<num_measures; i++)
210 : {
211 1 : const struct TALER_EXCHANGE_MeasureInformation *mi = &measures[i];
212 : json_t *measure;
213 :
214 1 : measure = GNUNET_JSON_PACK (
215 : GNUNET_JSON_pack_string ("check_name",
216 : mi->check_name),
217 : GNUNET_JSON_pack_string ("prog_name",
218 : mi->prog_name),
219 : GNUNET_JSON_pack_allow_null (
220 : GNUNET_JSON_pack_object_incref ("context",
221 : (json_t *) mi->context))
222 : );
223 1 : GNUNET_break (0 ==
224 : json_object_set_new (jmeasures,
225 : mi->measure_name,
226 : measure));
227 : }
228 :
229 3 : new_rules = GNUNET_JSON_PACK (
230 : GNUNET_JSON_pack_timestamp ("expiration_time",
231 : expiration_time),
232 : GNUNET_JSON_pack_allow_null (
233 : GNUNET_JSON_pack_string ("successor_measure",
234 : successor_measure)),
235 : GNUNET_JSON_pack_array_steal ("rules",
236 : jrules),
237 : GNUNET_JSON_pack_object_steal ("custom_measures",
238 : jmeasures)
239 : );
240 :
241 3 : GNUNET_CRYPTO_eddsa_key_get_public (
242 : &officer_priv->eddsa_priv,
243 : &officer_pub.eddsa_pub);
244 3 : TALER_officer_aml_decision_sign (justification,
245 : decision_time,
246 : h_payto,
247 : new_rules,
248 : properties,
249 : new_measures,
250 : keep_investigating,
251 : officer_priv,
252 : &officer_sig);
253 3 : wh = GNUNET_new (struct TALER_EXCHANGE_AddAmlDecision);
254 3 : wh->cb = cb;
255 3 : wh->cb_cls = cb_cls;
256 3 : wh->ctx = ctx;
257 : {
258 : char *path;
259 : char opus[sizeof (officer_pub) * 2];
260 : char *end;
261 :
262 3 : end = GNUNET_STRINGS_data_to_string (
263 : &officer_pub,
264 : sizeof (officer_pub),
265 : opus,
266 : sizeof (opus));
267 3 : *end = '\0';
268 3 : GNUNET_asprintf (&path,
269 : "aml/%s/decision",
270 : opus);
271 3 : wh->url = TALER_url_join (url,
272 : path,
273 : NULL);
274 3 : GNUNET_free (path);
275 : }
276 3 : if (NULL == wh->url)
277 : {
278 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
279 : "Could not construct request URL.\n");
280 0 : GNUNET_free (wh);
281 0 : json_decref (new_rules);
282 0 : return NULL;
283 : }
284 3 : body = GNUNET_JSON_PACK (
285 : GNUNET_JSON_pack_string ("justification",
286 : justification),
287 : GNUNET_JSON_pack_data_auto ("h_payto",
288 : h_payto),
289 : GNUNET_JSON_pack_allow_null (
290 : TALER_JSON_pack_full_payto ("payto_uri",
291 : payto_uri)),
292 : GNUNET_JSON_pack_object_steal ("new_rules",
293 : new_rules),
294 : GNUNET_JSON_pack_object_incref ("properties",
295 : (json_t *) properties),
296 : GNUNET_JSON_pack_allow_null (
297 : GNUNET_JSON_pack_string ("new_measures",
298 : new_measures)),
299 : GNUNET_JSON_pack_bool ("keep_investigating",
300 : keep_investigating),
301 : GNUNET_JSON_pack_data_auto ("officer_sig",
302 : &officer_sig),
303 : GNUNET_JSON_pack_timestamp ("decision_time",
304 : decision_time));
305 3 : eh = TALER_EXCHANGE_curl_easy_get_ (wh->url);
306 6 : if ( (NULL == eh) ||
307 : (GNUNET_OK !=
308 3 : TALER_curl_easy_post (&wh->post_ctx,
309 : eh,
310 : body)) )
311 : {
312 0 : GNUNET_break (0);
313 0 : if (NULL != eh)
314 0 : curl_easy_cleanup (eh);
315 0 : json_decref (body);
316 0 : GNUNET_free (wh->url);
317 0 : return NULL;
318 : }
319 3 : json_decref (body);
320 3 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
321 : "Requesting URL '%s'\n",
322 : wh->url);
323 6 : wh->job = GNUNET_CURL_job_add2 (ctx,
324 : eh,
325 3 : wh->post_ctx.headers,
326 : &handle_add_aml_decision_finished,
327 : wh);
328 3 : if (NULL == wh->job)
329 : {
330 0 : TALER_EXCHANGE_post_aml_decision_cancel (wh);
331 0 : return NULL;
332 : }
333 3 : return wh;
334 : }
335 :
336 :
337 : void
338 3 : TALER_EXCHANGE_post_aml_decision_cancel (
339 : struct TALER_EXCHANGE_AddAmlDecision *wh)
340 : {
341 3 : if (NULL != wh->job)
342 : {
343 0 : GNUNET_CURL_job_cancel (wh->job);
344 0 : wh->job = NULL;
345 : }
346 3 : TALER_curl_easy_post_finished (&wh->post_ctx);
347 3 : GNUNET_free (wh->url);
348 3 : GNUNET_free (wh);
349 3 : }
|