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
6 : under the terms of the GNU General Public License as published by
7 : the Free Software Foundation; either version 3, or (at your
8 : option) any later version.
9 :
10 : TALER is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not, see
17 : <http://www.gnu.org/licenses/>
18 : */
19 : /**
20 : * @file testing/testing_api_cmd_take_aml_decision.c
21 : * @brief command for testing /aml/$OFFICER_PUB/decision
22 : * @author Christian Grothoff
23 : */
24 : #include "taler/platform.h"
25 : #include "taler/taler_json_lib.h"
26 : #include <gnunet/gnunet_curl_lib.h>
27 : #include "taler/taler_testing_lib.h"
28 : #include "taler/taler_signatures.h"
29 : #include "taler/backoff.h"
30 :
31 :
32 : /**
33 : * State for a "take_aml_decision" CMD.
34 : */
35 : struct AmlDecisionState
36 : {
37 :
38 : /**
39 : * Auditor enable handle while operation is running.
40 : */
41 : struct TALER_EXCHANGE_AddAmlDecision *dh;
42 :
43 : /**
44 : * Our interpreter.
45 : */
46 : struct TALER_TESTING_Interpreter *is;
47 :
48 : /**
49 : * Reference to command to previous set officer command that gives
50 : * us an officer_priv trait.
51 : */
52 : const char *officer_ref_cmd;
53 :
54 : /**
55 : * Reference to command to previous AML-triggering event that gives
56 : * us a payto-hash trait.
57 : */
58 : const char *account_ref_cmd;
59 :
60 : /**
61 : * Payto hash of the account we are manipulating the AML settings for.
62 : */
63 : struct TALER_NormalizedPaytoHashP h_payto;
64 :
65 : /**
66 : * Justification given.
67 : */
68 : const char *justification;
69 :
70 : /**
71 : * Delay to apply to compute the expiration time
72 : * for the rules.
73 : */
74 : struct GNUNET_TIME_Relative expiration_delay;
75 :
76 : /**
77 : * Successor measure to activate upon expiration.
78 : */
79 : const char *successor_measure;
80 :
81 : /**
82 : * True to keep AML investigation open.
83 : */
84 : bool keep_investigating;
85 :
86 : /**
87 : * New rules to enforce.
88 : */
89 : json_t *new_rules;
90 :
91 : /**
92 : * Account properties to set.
93 : */
94 : json_t *properties;
95 :
96 : /**
97 : * Expected response code.
98 : */
99 : unsigned int expected_response;
100 : };
101 :
102 :
103 : /**
104 : * Callback to analyze the /aml-decision/$OFFICER_PUB response, just used to check
105 : * if the response code is acceptable.
106 : *
107 : * @param cls closure.
108 : * @param adr response details
109 : */
110 : static void
111 3 : take_aml_decision_cb (
112 : void *cls,
113 : const struct TALER_EXCHANGE_AddAmlDecisionResponse *adr)
114 : {
115 3 : struct AmlDecisionState *ds = cls;
116 3 : const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr;
117 :
118 3 : ds->dh = NULL;
119 3 : if (ds->expected_response != hr->http_status)
120 : {
121 0 : TALER_TESTING_unexpected_status (ds->is,
122 : hr->http_status,
123 : ds->expected_response);
124 0 : return;
125 : }
126 3 : TALER_TESTING_interpreter_next (ds->is);
127 : }
128 :
129 :
130 : /**
131 : * Run the command.
132 : *
133 : * @param cls closure.
134 : * @param cmd the command to execute.
135 : * @param is the interpreter state.
136 : */
137 : static void
138 3 : take_aml_decision_run (void *cls,
139 : const struct TALER_TESTING_Command *cmd,
140 : struct TALER_TESTING_Interpreter *is)
141 : {
142 3 : struct AmlDecisionState *ds = cls;
143 : struct GNUNET_TIME_Timestamp now;
144 : const struct TALER_NormalizedPaytoHashP *h_payto;
145 : const struct TALER_AmlOfficerPrivateKeyP *officer_priv;
146 : const struct TALER_TESTING_Command *ref;
147 : const char *exchange_url;
148 : const json_t *jrules;
149 3 : const json_t *jmeasures = NULL;
150 : struct GNUNET_TIME_Timestamp expiration_time
151 3 : = GNUNET_TIME_relative_to_timestamp (ds->expiration_delay);
152 3 : const char *new_measures = NULL;
153 : struct GNUNET_JSON_Specification spec[] = {
154 3 : GNUNET_JSON_spec_array_const ("rules",
155 : &jrules),
156 3 : GNUNET_JSON_spec_mark_optional (
157 : GNUNET_JSON_spec_object_const ("custom_measures",
158 : &jmeasures),
159 : NULL),
160 3 : GNUNET_JSON_spec_mark_optional (
161 : GNUNET_JSON_spec_string ("new_measures",
162 : &new_measures),
163 : NULL),
164 3 : GNUNET_JSON_spec_end ()
165 : };
166 : unsigned int num_rules;
167 : unsigned int num_measures;
168 :
169 : (void) cmd;
170 3 : if (GNUNET_OK !=
171 3 : GNUNET_JSON_parse (ds->new_rules,
172 : spec,
173 : NULL, NULL))
174 : {
175 0 : GNUNET_break_op (0);
176 0 : TALER_TESTING_interpreter_fail (is);
177 0 : return;
178 : }
179 :
180 : {
181 : const struct TALER_TESTING_Command *exchange_cmd;
182 :
183 3 : exchange_cmd = TALER_TESTING_interpreter_get_command (is,
184 : "exchange");
185 3 : if (NULL == exchange_cmd)
186 : {
187 0 : GNUNET_break (0);
188 0 : TALER_TESTING_interpreter_fail (is);
189 0 : return;
190 : }
191 3 : GNUNET_assert (GNUNET_OK ==
192 : TALER_TESTING_get_trait_exchange_url (exchange_cmd,
193 : &exchange_url));
194 : }
195 3 : now = GNUNET_TIME_timestamp_get ();
196 3 : ds->is = is;
197 3 : ref = TALER_TESTING_interpreter_lookup_command (is,
198 : ds->account_ref_cmd);
199 3 : if (NULL == ref)
200 : {
201 0 : GNUNET_break (0);
202 0 : TALER_TESTING_interpreter_fail (is);
203 0 : return;
204 : }
205 3 : if (GNUNET_OK !=
206 3 : TALER_TESTING_get_trait_h_normalized_payto (ref,
207 : &h_payto))
208 : {
209 0 : GNUNET_break (0);
210 0 : TALER_TESTING_interpreter_fail (is);
211 0 : return;
212 : }
213 3 : ref = TALER_TESTING_interpreter_lookup_command (is,
214 : ds->officer_ref_cmd);
215 3 : if (NULL == ref)
216 : {
217 0 : GNUNET_break (0);
218 0 : TALER_TESTING_interpreter_fail (is);
219 0 : return;
220 : }
221 3 : if (GNUNET_OK !=
222 3 : TALER_TESTING_get_trait_officer_priv (ref,
223 : &officer_priv))
224 : {
225 0 : GNUNET_break (0);
226 0 : TALER_TESTING_interpreter_fail (is);
227 0 : return;
228 : }
229 3 : ds->h_payto = *h_payto;
230 :
231 3 : num_rules = (unsigned int) json_array_size (jrules);
232 3 : num_measures = (unsigned int) json_object_size (jmeasures);
233 3 : {
234 3 : struct TALER_EXCHANGE_AccountRule rules[
235 3 : GNUNET_NZL (num_rules)];
236 3 : struct TALER_EXCHANGE_MeasureInformation measures[
237 3 : GNUNET_NZL (num_measures)];
238 : const json_t *jrule;
239 : size_t i;
240 : const json_t *jmeasure;
241 : const char *mname;
242 : unsigned int off;
243 :
244 3 : memset (rules,
245 : 0,
246 : sizeof (rules));
247 3 : memset (measures,
248 : 0,
249 : sizeof (measures));
250 6 : json_array_foreach ((json_t *) jrules, i, jrule)
251 : {
252 3 : struct TALER_EXCHANGE_AccountRule *rule = &rules[i];
253 3 : const json_t *jameasures = NULL;
254 : struct GNUNET_JSON_Specification ispec[] = {
255 3 : GNUNET_JSON_spec_relative_time ("timeframe",
256 : &rule->timeframe),
257 3 : TALER_JSON_spec_amount_any ("threshold",
258 : &rule->threshold),
259 3 : GNUNET_JSON_spec_mark_optional (
260 : GNUNET_JSON_spec_array_const ("measures",
261 : &jameasures),
262 : NULL),
263 3 : GNUNET_JSON_spec_mark_optional (
264 : GNUNET_JSON_spec_uint32 ("display_priority",
265 : &rule->display_priority),
266 : NULL),
267 3 : TALER_JSON_spec_kycte ("operation_type",
268 : &rule->operation_type),
269 3 : GNUNET_JSON_spec_mark_optional (
270 : GNUNET_JSON_spec_bool ("verboten",
271 : &rule->verboten),
272 : NULL),
273 3 : GNUNET_JSON_spec_mark_optional (
274 : GNUNET_JSON_spec_bool ("exposed",
275 : &rule->exposed),
276 : NULL),
277 3 : GNUNET_JSON_spec_mark_optional (
278 : GNUNET_JSON_spec_bool ("is_and_combinator",
279 : &rule->is_and_combinator),
280 : NULL),
281 3 : GNUNET_JSON_spec_end ()
282 : };
283 : const char *err_name;
284 : unsigned int err_line;
285 :
286 3 : if (GNUNET_OK !=
287 3 : GNUNET_JSON_parse (jrule,
288 : ispec,
289 : &err_name,
290 : &err_line))
291 : {
292 0 : GNUNET_break_op (0);
293 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
294 : "Malformed rule #%u in field %s\n",
295 : (unsigned int) i,
296 : err_name);
297 0 : TALER_TESTING_interpreter_fail (is);
298 0 : return;
299 : }
300 3 : if (NULL != jameasures)
301 : {
302 : rule->num_measures
303 1 : = (unsigned int) json_array_size (jameasures);
304 : rule->measures
305 1 : = GNUNET_new_array (rule->num_measures,
306 : const char *);
307 2 : for (unsigned int k = 0; k<rule->num_measures; k++)
308 1 : rule->measures[k]
309 1 : = json_string_value (
310 1 : json_array_get (jameasures,
311 : k));
312 : }
313 : }
314 :
315 3 : off = 0;
316 4 : json_object_foreach ((json_t *) jmeasures, mname, jmeasure)
317 : {
318 1 : struct TALER_EXCHANGE_MeasureInformation *mi = &measures[off++];
319 : struct GNUNET_JSON_Specification ispec[] = {
320 1 : GNUNET_JSON_spec_mark_optional (
321 : GNUNET_JSON_spec_string ("check_name",
322 : &mi->check_name),
323 : NULL),
324 1 : GNUNET_JSON_spec_mark_optional (
325 : GNUNET_JSON_spec_string ("prog_name",
326 : &mi->prog_name),
327 : NULL),
328 1 : GNUNET_JSON_spec_mark_optional (
329 : GNUNET_JSON_spec_object_const ("context",
330 : &mi->context),
331 : NULL),
332 1 : GNUNET_JSON_spec_end ()
333 : };
334 : const char *err_name;
335 : unsigned int err_line;
336 :
337 1 : mi->measure_name = mname;
338 1 : if (GNUNET_OK !=
339 1 : GNUNET_JSON_parse (jmeasure,
340 : ispec,
341 : &err_name,
342 : &err_line))
343 : {
344 0 : GNUNET_break_op (0);
345 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
346 : "Malformed measure %s in field %s\n",
347 : mname,
348 : err_name);
349 0 : TALER_TESTING_interpreter_fail (is);
350 0 : return;
351 : }
352 : }
353 3 : GNUNET_assert (off == num_measures);
354 :
355 : {
356 3 : struct TALER_FullPayto null_payto = {
357 : .full_payto = NULL
358 : };
359 :
360 3 : ds->dh = TALER_EXCHANGE_post_aml_decision (
361 : TALER_TESTING_interpreter_get_context (is),
362 : exchange_url,
363 : h_payto,
364 : null_payto,
365 : now,
366 : ds->successor_measure,
367 : new_measures,
368 : expiration_time,
369 : num_rules,
370 : rules,
371 : num_measures,
372 : measures,
373 3 : ds->properties,
374 3 : ds->keep_investigating,
375 : ds->justification,
376 : officer_priv,
377 : 0, NULL, /* no events */
378 : &take_aml_decision_cb,
379 : ds);
380 : }
381 6 : for (unsigned int j = 0; j<num_rules; j++)
382 : {
383 3 : struct TALER_EXCHANGE_AccountRule *rule = &rules[j];
384 :
385 3 : GNUNET_free (rule->measures);
386 : }
387 : }
388 :
389 3 : if (NULL == ds->dh)
390 : {
391 0 : GNUNET_break (0);
392 0 : TALER_TESTING_interpreter_fail (is);
393 0 : return;
394 : }
395 : }
396 :
397 :
398 : /**
399 : * Free the state of a "take_aml_decision" CMD, and possibly cancel a
400 : * pending operation thereof.
401 : *
402 : * @param cls closure, must be a `struct AmlDecisionState`.
403 : * @param cmd the command which is being cleaned up.
404 : */
405 : static void
406 3 : take_aml_decision_cleanup (void *cls,
407 : const struct TALER_TESTING_Command *cmd)
408 : {
409 3 : struct AmlDecisionState *ds = cls;
410 :
411 3 : if (NULL != ds->dh)
412 : {
413 0 : TALER_TESTING_command_incomplete (ds->is,
414 : cmd->label);
415 0 : TALER_EXCHANGE_post_aml_decision_cancel (ds->dh);
416 0 : ds->dh = NULL;
417 : }
418 3 : json_decref (ds->new_rules);
419 3 : json_decref (ds->properties);
420 3 : GNUNET_free (ds);
421 3 : }
422 :
423 :
424 : /**
425 : * Offer internal data of a "AML decision" CMD state to other
426 : * commands.
427 : *
428 : * @param cls closure
429 : * @param[out] ret result (could be anything)
430 : * @param trait name of the trait
431 : * @param index index number of the object to offer.
432 : * @return #GNUNET_OK on success
433 : */
434 : static enum GNUNET_GenericReturnValue
435 4 : take_aml_decision_traits (void *cls,
436 : const void **ret,
437 : const char *trait,
438 : unsigned int index)
439 : {
440 4 : struct AmlDecisionState *ws = cls;
441 : struct TALER_TESTING_Trait traits[] = {
442 4 : TALER_TESTING_make_trait_h_normalized_payto (&ws->h_payto),
443 4 : TALER_TESTING_make_trait_aml_justification (ws->justification),
444 4 : TALER_TESTING_trait_end ()
445 : };
446 :
447 4 : return TALER_TESTING_get_trait (traits,
448 : ret,
449 : trait,
450 : index);
451 : }
452 :
453 :
454 : struct TALER_TESTING_Command
455 3 : TALER_TESTING_cmd_take_aml_decision (
456 : const char *label,
457 : const char *ref_officer,
458 : const char *ref_operation,
459 : bool keep_investigating,
460 : struct GNUNET_TIME_Relative expiration_delay,
461 : const char *successor_measure,
462 : const char *new_rules,
463 : const char *properties,
464 : const char *justification,
465 : unsigned int expected_response)
466 : {
467 : struct AmlDecisionState *ds;
468 : json_error_t err;
469 :
470 3 : ds = GNUNET_new (struct AmlDecisionState);
471 3 : ds->officer_ref_cmd = ref_officer;
472 3 : ds->account_ref_cmd = ref_operation;
473 3 : ds->keep_investigating = keep_investigating;
474 3 : ds->expiration_delay = expiration_delay;
475 3 : ds->successor_measure = successor_measure;
476 3 : ds->new_rules = json_loads (new_rules,
477 : JSON_DECODE_ANY,
478 : &err);
479 3 : if (NULL == ds->new_rules)
480 : {
481 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
482 : "Invalid JSON in new rules of %s: %s\n",
483 : label,
484 : err.text);
485 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
486 : "Input was: `%s'\n",
487 : new_rules);
488 0 : GNUNET_assert (0);
489 : }
490 3 : GNUNET_assert (NULL != ds->new_rules);
491 3 : ds->properties = json_loads (properties,
492 : 0,
493 : &err);
494 3 : if (NULL == ds->properties)
495 : {
496 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
497 : "Invalid JSON in properties of %s: %s\n",
498 : label,
499 : err.text);
500 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
501 : "Input was: `%s'\n",
502 : properties);
503 0 : GNUNET_assert (0);
504 : }
505 3 : ds->justification = justification;
506 3 : ds->expected_response = expected_response;
507 : {
508 3 : struct TALER_TESTING_Command cmd = {
509 : .cls = ds,
510 : .label = label,
511 : .run = &take_aml_decision_run,
512 : .cleanup = &take_aml_decision_cleanup,
513 : .traits = &take_aml_decision_traits
514 : };
515 :
516 3 : return cmd;
517 : }
518 : }
519 :
520 :
521 : /* end of testing_api_cmd_take_aml_decision.c */
|