Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2026 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 <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file exchangedb/test_kyc_attributes.c
18 : * @brief tests for the exchangedb functions whose primary table is
19 : * `kyc_attributes`
20 : * @author Christian Grothoff
21 : *
22 : * Covers #TALER_EXCHANGEDB_do_insert_kyc_attributes(),
23 : * #TALER_EXCHANGEDB_iterate_kyc_attributes(),
24 : * #TALER_EXCHANGEDB_iterate_all_kyc_attributes(),
25 : * #TALER_EXCHANGEDB_iterate_aml_attributes(),
26 : * #TALER_EXCHANGEDB_iterate_kyc_history(),
27 : * #TALER_EXCHANGEDB_kyc_history_builder() and
28 : * #TALER_EXCHANGEDB_current_attributes_builder().
29 : *
30 : * A `kyc_attributes` row is the (encrypted) personal data one KYC process
31 : * collected, so every row needs a `legitimization_processes` row to hang
32 : * off; those in turn need a legitimization measure on a known account.
33 : * That is what setup_account() builds before the first insert. The AML
34 : * officer path is exercised too, because it is the only way to get a row
35 : * with `by_aml_officer` set, which is what the AML attribute iterator
36 : * reports on.
37 : */
38 : #include "test_common.h"
39 : #include "exchange-database/account_history.h"
40 : #include "exchange-database/do_insert_kyc_attributes.h"
41 : #include "exchange-database/do_trigger_kyc_rule_for_account.h"
42 : #include "exchange-database/insert_aml_decision.h"
43 : #include "exchange-database/insert_aml_officer.h"
44 : #include "exchange-database/insert_legitimization_process.h"
45 : #include "exchange-database/iterate_all_kyc_attributes.h"
46 : #include "exchange-database/iterate_aml_attributes.h"
47 : #include "exchange-database/iterate_kyc_attributes.h"
48 : #include "exchange-database/iterate_kyc_history.h"
49 :
50 :
51 : /**
52 : * Account the attributes are about.
53 : */
54 : static struct TDB_Account account;
55 :
56 :
57 : /**
58 : * Row of the legitimization measures on @e account.
59 : */
60 : static uint64_t measure_row;
61 :
62 :
63 : /**
64 : * Rows of the two KYC processes we run on @e account.
65 : */
66 : static uint64_t process_row[2];
67 :
68 :
69 : /**
70 : * Key the attributes are encrypted with.
71 : */
72 : static struct TALER_AttributeEncryptionKeyP attribute_key;
73 :
74 :
75 : /**
76 : * Name the AML officer is on file under.
77 : */
78 : #define OFFICER_NAME "Alex Officer"
79 :
80 :
81 : /**
82 : * Build the attribute set of @a name for storage.
83 : *
84 : * @param name value of the NAME attribute
85 : * @param[out] enc set to the encrypted attributes, to be freed by the
86 : * caller
87 : * @param[out] enc_size set to the number of bytes in @a enc
88 : */
89 : static void
90 3 : encrypt_attributes (const char *name,
91 : void **enc,
92 : size_t *enc_size)
93 : {
94 : json_t *attrs;
95 :
96 3 : attrs = GNUNET_JSON_PACK (
97 : GNUNET_JSON_pack_string ("FORM_ID",
98 : "test-form"),
99 : GNUNET_JSON_pack_string ("NAME",
100 : name));
101 3 : GNUNET_assert (NULL != attrs);
102 3 : TALER_CRYPTO_kyc_attributes_encrypt (&attribute_key,
103 : attrs,
104 : enc,
105 : enc_size);
106 3 : json_decref (attrs);
107 3 : }
108 :
109 :
110 : /**
111 : * Closure for the various attribute callbacks.
112 : */
113 : struct AttributeContext
114 : {
115 : /**
116 : * How many rows did the callback see?
117 : */
118 : unsigned int total;
119 :
120 : /**
121 : * Stop after this many rows; 0 for no limit.
122 : */
123 : unsigned int stop_after;
124 :
125 : /**
126 : * Value of the NAME attribute of the last row seen, owned by this
127 : * struct.
128 : */
129 : char *name;
130 :
131 : /**
132 : * Provider of the last row seen, owned by this struct.
133 : */
134 : char *provider;
135 :
136 : /**
137 : * Officer of the last row seen, owned by this struct.
138 : */
139 : char *officer;
140 :
141 : /**
142 : * Row ID of the last row seen.
143 : */
144 : uint64_t row_id;
145 :
146 : /**
147 : * Was the last row filed by an AML officer?
148 : */
149 : bool by_aml_officer;
150 :
151 : /**
152 : * Was the KYC process of the last row finished?
153 : */
154 : bool finished;
155 : };
156 :
157 :
158 : /**
159 : * Release what a `struct AttributeContext` owns.
160 : *
161 : * @param[in,out] ctx context to clean up
162 : */
163 : static void
164 9 : ac_free (struct AttributeContext *ctx)
165 : {
166 9 : GNUNET_free (ctx->name);
167 9 : GNUNET_free (ctx->provider);
168 9 : GNUNET_free (ctx->officer);
169 9 : }
170 :
171 :
172 : /**
173 : * Decrypt @a enc and remember its NAME attribute in @a ctx.
174 : *
175 : * @param[in,out] ctx context to update
176 : * @param enc_size number of bytes in @a enc
177 : * @param enc encrypted attributes
178 : */
179 : static void
180 12 : remember_name (struct AttributeContext *ctx,
181 : size_t enc_size,
182 : const void *enc)
183 : {
184 : json_t *attrs;
185 : const char *name;
186 :
187 12 : GNUNET_free (ctx->name);
188 12 : ctx->name = NULL;
189 12 : attrs = TALER_CRYPTO_kyc_attributes_decrypt (&attribute_key,
190 : enc,
191 : enc_size);
192 12 : if (NULL == attrs)
193 0 : return;
194 12 : name = json_string_value (json_object_get (attrs,
195 : "NAME"));
196 12 : if (NULL != name)
197 12 : ctx->name = GNUNET_strdup (name);
198 12 : json_decref (attrs);
199 : }
200 :
201 :
202 : /**
203 : * Callback for #TALER_EXCHANGEDB_iterate_kyc_attributes().
204 : *
205 : * @param cls a `struct AttributeContext *`
206 : * @param h_payto account the attributes are about
207 : * @param provider_name provider that collected them
208 : * @param collection_time when they were collected
209 : * @param expiration_time when they expire
210 : * @param enc_attributes_size number of bytes in @a enc_attributes
211 : * @param enc_attributes the encrypted attributes
212 : */
213 : static void
214 2 : attribute_cb (void *cls,
215 : const struct TALER_NormalizedPaytoHashP *h_payto,
216 : const char *provider_name,
217 : struct GNUNET_TIME_Timestamp collection_time,
218 : struct GNUNET_TIME_Timestamp expiration_time,
219 : size_t enc_attributes_size,
220 : const void *enc_attributes)
221 : {
222 2 : struct AttributeContext *ctx = cls;
223 :
224 : (void) h_payto;
225 : (void) collection_time;
226 : (void) expiration_time;
227 2 : ctx->total++;
228 2 : GNUNET_free (ctx->provider);
229 2 : ctx->provider = (NULL == provider_name)
230 : ? NULL
231 2 : : GNUNET_strdup (provider_name);
232 2 : remember_name (ctx,
233 : enc_attributes_size,
234 : enc_attributes);
235 2 : }
236 :
237 :
238 : /**
239 : * Callback for #TALER_EXCHANGEDB_iterate_all_kyc_attributes().
240 : *
241 : * @param cls a `struct AttributeContext *`
242 : * @param row_id row of the attributes
243 : * @param h_payto account the attributes are about
244 : * @param provider_name provider that collected them
245 : * @param collection_time when they were collected
246 : * @param expiration_time when they expire
247 : * @param properties properties set for the account
248 : * @param enc_attributes_size number of bytes in @a enc_attributes
249 : * @param enc_attributes the encrypted attributes
250 : * @return true to continue iterating
251 : */
252 : static bool
253 4 : all_attributes_cb (void *cls,
254 : uint64_t row_id,
255 : const struct TALER_NormalizedPaytoHashP *h_payto,
256 : const char *provider_name,
257 : struct GNUNET_TIME_Timestamp collection_time,
258 : struct GNUNET_TIME_Timestamp expiration_time,
259 : const json_t *properties,
260 : size_t enc_attributes_size,
261 : const void *enc_attributes)
262 : {
263 4 : struct AttributeContext *ctx = cls;
264 :
265 : (void) h_payto;
266 : (void) collection_time;
267 : (void) expiration_time;
268 : (void) properties;
269 4 : ctx->total++;
270 4 : ctx->row_id = row_id;
271 4 : GNUNET_free (ctx->provider);
272 4 : ctx->provider = (NULL == provider_name)
273 : ? NULL
274 4 : : GNUNET_strdup (provider_name);
275 4 : remember_name (ctx,
276 : enc_attributes_size,
277 : enc_attributes);
278 5 : return ! ( (0 != ctx->stop_after) &&
279 1 : (ctx->total >= ctx->stop_after) );
280 : }
281 :
282 :
283 : /**
284 : * Callback for #TALER_EXCHANGEDB_iterate_aml_attributes().
285 : *
286 : * @param cls a `struct AttributeContext *`
287 : * @param row_id row of the attributes
288 : * @param collection_time when they were collected
289 : * @param by_aml_officer were they filed by an AML officer
290 : * @param officer_name name of that officer, NULL if none
291 : * @param enc_attributes_size number of bytes in @a enc_attributes
292 : * @param enc_attributes the encrypted attributes
293 : */
294 : static void
295 4 : aml_attribute_cb (void *cls,
296 : uint64_t row_id,
297 : struct GNUNET_TIME_Timestamp collection_time,
298 : bool by_aml_officer,
299 : const char *officer_name,
300 : size_t enc_attributes_size,
301 : const void *enc_attributes)
302 : {
303 4 : struct AttributeContext *ctx = cls;
304 :
305 : (void) collection_time;
306 4 : ctx->total++;
307 4 : ctx->row_id = row_id;
308 4 : ctx->by_aml_officer = by_aml_officer;
309 4 : GNUNET_free (ctx->officer);
310 4 : ctx->officer = (NULL == officer_name)
311 : ? NULL
312 4 : : GNUNET_strdup (officer_name);
313 4 : remember_name (ctx,
314 : enc_attributes_size,
315 : enc_attributes);
316 4 : }
317 :
318 :
319 : /**
320 : * Callback for #TALER_EXCHANGEDB_iterate_kyc_history().
321 : *
322 : * @param cls a `struct AttributeContext *`
323 : * @param provider_name provider that ran the process
324 : * @param finished did the process finish
325 : * @param error_code error the process ran into
326 : * @param error_message message for @a error_code
327 : * @param provider_user_id account ID at the provider
328 : * @param provider_legitimization_id process ID at the provider
329 : * @param collection_time when the data was collected
330 : * @param expiration_time when the data expires
331 : * @param encrypted_attributes_len number of bytes in @a encrypted_attributes
332 : * @param encrypted_attributes the encrypted attributes
333 : */
334 : static void
335 2 : kyc_history_cb (void *cls,
336 : const char *provider_name,
337 : bool finished,
338 : enum TALER_ErrorCode error_code,
339 : const char *error_message,
340 : const char *provider_user_id,
341 : const char *provider_legitimization_id,
342 : struct GNUNET_TIME_Timestamp collection_time,
343 : struct GNUNET_TIME_Absolute expiration_time,
344 : size_t encrypted_attributes_len,
345 : const void *encrypted_attributes)
346 : {
347 2 : struct AttributeContext *ctx = cls;
348 :
349 : (void) error_code;
350 : (void) error_message;
351 : (void) provider_user_id;
352 : (void) provider_legitimization_id;
353 : (void) collection_time;
354 : (void) expiration_time;
355 2 : ctx->total++;
356 2 : ctx->finished = finished;
357 2 : GNUNET_free (ctx->provider);
358 2 : ctx->provider = (NULL == provider_name)
359 : ? NULL
360 2 : : GNUNET_strdup (provider_name);
361 2 : remember_name (ctx,
362 : encrypted_attributes_len,
363 : encrypted_attributes);
364 2 : }
365 :
366 :
367 : /**
368 : * Create the account, put a legitimization measure on it and start the
369 : * two KYC processes the attributes will belong to.
370 : *
371 : * @param pg the database context
372 : */
373 : static void
374 1 : setup_account (struct TALER_EXCHANGEDB_PostgresContext *pg)
375 : {
376 : json_t *jmeasures;
377 : bool bad_kyc_auth;
378 :
379 1 : if (0 != measure_row)
380 0 : return;
381 1 : TDB_FILL (attribute_key,
382 : 42);
383 1 : TDB_account (pg,
384 : 10,
385 : &account);
386 1 : jmeasures = GNUNET_JSON_PACK (
387 : GNUNET_JSON_pack_array_steal (
388 : "measures",
389 : json_pack ("[{s:s,s:s,s:s}]",
390 : "check_name",
391 : "verify-id",
392 : "prog_name",
393 : "skip",
394 : "context",
395 : "none")),
396 : GNUNET_JSON_pack_bool ("is_and_combinator",
397 : true));
398 1 : GNUNET_assert (NULL != jmeasures);
399 1 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
400 : TALER_EXCHANGEDB_do_trigger_kyc_rule_for_account (
401 : pg,
402 : account.payto,
403 : &account.h_normalized,
404 : NULL,
405 : NULL,
406 : jmeasures,
407 : 1,
408 : &measure_row,
409 : &bad_kyc_auth));
410 1 : json_decref (jmeasures);
411 1 : GNUNET_assert (0 != measure_row);
412 3 : for (unsigned int i = 0; i < 2; i++)
413 : {
414 : char *legi_id;
415 :
416 2 : GNUNET_asprintf (&legi_id,
417 : "legi-%u",
418 : i);
419 2 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
420 : TALER_EXCHANGEDB_insert_legitimization_process (
421 : pg,
422 : &account.h_normalized,
423 : i,
424 : measure_row,
425 : (0 == i) ? "provider-a" : "provider-b",
426 : "provider-account",
427 : legi_id,
428 : &process_row[i]));
429 2 : GNUNET_free (legi_id);
430 2 : GNUNET_assert (0 != process_row[i]);
431 : }
432 : }
433 :
434 :
435 : /**
436 : * Nothing is known while the table is empty.
437 : *
438 : * @param pg the database context
439 : * @return 0 on success
440 : */
441 : static int
442 1 : check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
443 : {
444 1 : struct AttributeContext ctx = { 0 };
445 : struct TALER_EXCHANGEDB_HistoryBuilderContext hbc;
446 : json_t *j;
447 :
448 1 : setup_account (pg);
449 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
450 : TALER_EXCHANGEDB_iterate_kyc_attributes (pg,
451 : &account.h_normalized,
452 : &attribute_cb,
453 : &ctx));
454 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
455 : TALER_EXCHANGEDB_iterate_all_kyc_attributes (pg,
456 : 0,
457 : &all_attributes_cb,
458 : &ctx));
459 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
460 : TALER_EXCHANGEDB_iterate_aml_attributes (pg,
461 : &account.h_normalized,
462 : 0,
463 : 16,
464 : &aml_attribute_cb,
465 : &ctx));
466 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
467 : TALER_EXCHANGEDB_iterate_kyc_history (pg,
468 : &account.h_normalized,
469 : &kyc_history_cb,
470 : &ctx));
471 1 : FAILIF (0 != ctx.total);
472 :
473 1 : hbc.account = &account.h_normalized;
474 1 : hbc.pg = pg;
475 1 : hbc.attribute_key = &attribute_key;
476 1 : hbc.is_wallet = false;
477 1 : j = TALER_EXCHANGEDB_kyc_history_builder (&hbc);
478 1 : FAILIF (NULL == j);
479 1 : FAILIF_C (0 != json_array_size (j),
480 : json_decref (j));
481 1 : json_decref (j);
482 : /* with no attributes at all, the "current" ones are an empty object */
483 1 : j = TALER_EXCHANGEDB_current_attributes_builder (&hbc);
484 1 : FAILIF (NULL == j);
485 1 : FAILIF_C (! json_is_object (j),
486 : json_decref (j));
487 1 : FAILIF_C (0 != json_object_size (j),
488 : json_decref (j));
489 1 : json_decref (j);
490 1 : return 0;
491 : }
492 :
493 :
494 : /**
495 : * Storing attributes creates the row and finishes the KYC process they
496 : * were collected by.
497 : *
498 : * @param pg the database context
499 : * @return 0 on success
500 : */
501 : static int
502 1 : check_insert (struct TALER_EXCHANGEDB_PostgresContext *pg)
503 : {
504 : void *enc;
505 : size_t enc_size;
506 : enum GNUNET_DB_QueryStatus qs;
507 :
508 1 : encrypt_attributes ("Alice",
509 : &enc,
510 : &enc_size);
511 1 : qs = TALER_EXCHANGEDB_do_insert_kyc_attributes (
512 : pg,
513 : process_row[0],
514 : &account.h_normalized,
515 : "provider-a",
516 : "provider-account",
517 : "legi-0",
518 : 0,
519 : GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS),
520 : "test-form",
521 : enc_size,
522 : enc);
523 1 : GNUNET_free (enc);
524 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
525 1 : FAILIF (1 != TDB_count (pg,
526 : "FROM kyc_attributes"));
527 1 : FAILIF (1 != TDB_count (pg,
528 : "FROM kyc_attributes"
529 : " WHERE NOT by_aml_officer"
530 : " AND form_name='test-form'"));
531 : /* the KYC process the attributes came from is now finished */
532 1 : FAILIF (1 != TDB_count (pg,
533 : "FROM legitimization_processes"
534 : " WHERE legitimization_process_serial_id=%llu"
535 : " AND finished",
536 : (unsigned long long) process_row[0]));
537 : /* ... and the aggregator was told to have another look */
538 1 : FAILIF (0 == TDB_count (pg,
539 : "FROM kyc_alerts"));
540 1 : return 0;
541 : }
542 :
543 :
544 : /**
545 : * Naming a provider that does not own the KYC process stores the
546 : * attributes but leaves the process unfinished.
547 : *
548 : * @param pg the database context
549 : * @return 0 on success
550 : */
551 : static int
552 1 : check_insert_wrong_provider (struct TALER_EXCHANGEDB_PostgresContext *pg)
553 : {
554 : void *enc;
555 : size_t enc_size;
556 : enum GNUNET_DB_QueryStatus qs;
557 :
558 1 : encrypt_attributes ("Bob",
559 : &enc,
560 : &enc_size);
561 1 : qs = TALER_EXCHANGEDB_do_insert_kyc_attributes (
562 : pg,
563 : process_row[1],
564 : &account.h_normalized,
565 : /* process_row[1] belongs to provider-b */
566 : "provider-a",
567 : "provider-account",
568 : "legi-1",
569 : 0,
570 : GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS),
571 : NULL,
572 : enc_size,
573 : enc);
574 1 : GNUNET_free (enc);
575 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
576 1 : FAILIF (2 != TDB_count (pg,
577 : "FROM kyc_attributes"));
578 : /* the attributes were stored, but the process was not finished; the
579 : stored procedure reports that through out_ok, which the C wrapper
580 : drops on the floor (EDBT-16) */
581 1 : FAILIF (0 != TDB_count (pg,
582 : "FROM legitimization_processes"
583 : " WHERE legitimization_process_serial_id=%llu"
584 : " AND finished",
585 : (unsigned long long) process_row[1]));
586 1 : FAILIF (1 != TDB_count (pg,
587 : "FROM kyc_attributes"
588 : " WHERE form_name IS NULL"));
589 1 : return 0;
590 : }
591 :
592 :
593 : /**
594 : * The per-account and the global iterator both find the stored rows.
595 : *
596 : * @param pg the database context
597 : * @return 0 on success
598 : */
599 : static int
600 1 : check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
601 : {
602 : struct AttributeContext ctx;
603 : struct TALER_NormalizedPaytoHashP other;
604 :
605 1 : memset (&ctx,
606 : 0,
607 : sizeof (ctx));
608 1 : FAILIF (0 >=
609 : TALER_EXCHANGEDB_iterate_kyc_attributes (pg,
610 : &account.h_normalized,
611 : &attribute_cb,
612 : &ctx));
613 1 : FAILIF_C (2 != ctx.total,
614 : ac_free (&ctx));
615 1 : FAILIF_C (NULL == ctx.name,
616 : ac_free (&ctx));
617 1 : ac_free (&ctx);
618 :
619 : /* an account without attributes has none */
620 1 : TDB_FILL (other,
621 : 99);
622 1 : memset (&ctx,
623 : 0,
624 : sizeof (ctx));
625 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
626 : TALER_EXCHANGEDB_iterate_kyc_attributes (pg,
627 : &other,
628 : &attribute_cb,
629 : &ctx));
630 1 : FAILIF_C (0 != ctx.total,
631 : ac_free (&ctx));
632 1 : ac_free (&ctx);
633 :
634 : /* the global iterator sees both rows, in ascending row order */
635 1 : memset (&ctx,
636 : 0,
637 : sizeof (ctx));
638 1 : FAILIF (0 >=
639 : TALER_EXCHANGEDB_iterate_all_kyc_attributes (pg,
640 : 0,
641 : &all_attributes_cb,
642 : &ctx));
643 1 : FAILIF_C (2 != ctx.total,
644 : ac_free (&ctx));
645 1 : FAILIF_C (0 != strcmp (ctx.name,
646 : "Bob"),
647 : ac_free (&ctx));
648 : {
649 1 : uint64_t first = ctx.row_id - 1;
650 :
651 1 : ac_free (&ctx);
652 : /* ... and honours the minimum row ID */
653 1 : memset (&ctx,
654 : 0,
655 : sizeof (ctx));
656 1 : FAILIF (0 >=
657 : TALER_EXCHANGEDB_iterate_all_kyc_attributes (pg,
658 : first,
659 : &all_attributes_cb,
660 : &ctx));
661 1 : FAILIF_C (1 != ctx.total,
662 : ac_free (&ctx));
663 1 : ac_free (&ctx);
664 : }
665 :
666 : /* a callback that says stop is not called again */
667 1 : memset (&ctx,
668 : 0,
669 : sizeof (ctx));
670 1 : ctx.stop_after = 1;
671 1 : FAILIF (0 >=
672 : TALER_EXCHANGEDB_iterate_all_kyc_attributes (pg,
673 : 0,
674 : &all_attributes_cb,
675 : &ctx));
676 1 : FAILIF_C (1 != ctx.total,
677 : ac_free (&ctx));
678 1 : FAILIF_C (0 != strcmp (ctx.name,
679 : "Alice"),
680 : ac_free (&ctx));
681 1 : ac_free (&ctx);
682 1 : return 0;
683 : }
684 :
685 :
686 : /**
687 : * The KYC history reports the state of the process each set of
688 : * attributes came from.
689 : *
690 : * @param pg the database context
691 : * @return 0 on success
692 : */
693 : static int
694 1 : check_kyc_history (struct TALER_EXCHANGEDB_PostgresContext *pg)
695 : {
696 : struct AttributeContext ctx;
697 1 : struct TALER_EXCHANGEDB_HistoryBuilderContext hbc = {
698 : .account = &account.h_normalized,
699 : .pg = pg,
700 : .attribute_key = &attribute_key,
701 : .is_wallet = false
702 : };
703 : json_t *j;
704 :
705 1 : memset (&ctx,
706 : 0,
707 : sizeof (ctx));
708 1 : FAILIF (0 >=
709 : TALER_EXCHANGEDB_iterate_kyc_history (pg,
710 : &account.h_normalized,
711 : &kyc_history_cb,
712 : &ctx));
713 1 : FAILIF_C (2 != ctx.total,
714 : ac_free (&ctx));
715 1 : ac_free (&ctx);
716 :
717 1 : j = TALER_EXCHANGEDB_kyc_history_builder (&hbc);
718 1 : FAILIF (NULL == j);
719 1 : FAILIF_C (2 != json_array_size (j),
720 : json_decref (j));
721 : {
722 1 : json_t *e = json_array_get (j,
723 : 0);
724 1 : const json_t *attrs = json_object_get (e,
725 : "attributes");
726 :
727 1 : FAILIF_C (NULL == attrs,
728 : json_decref (j));
729 1 : FAILIF_C (NULL == json_object_get (attrs,
730 : "NAME"),
731 : json_decref (j));
732 : }
733 1 : json_decref (j);
734 1 : return 0;
735 : }
736 :
737 :
738 : /**
739 : * Attributes filed by an AML officer are reported as such, together with
740 : * the officer's name.
741 : *
742 : * @param pg the database context
743 : * @return 0 on success
744 : */
745 : static int
746 1 : check_aml_attributes (struct TALER_EXCHANGEDB_PostgresContext *pg)
747 : {
748 : struct TALER_AmlOfficerPublicKeyP officer_pub;
749 : struct TALER_AmlOfficerSignatureP officer_sig;
750 : struct TALER_MasterSignatureP master_sig;
751 : struct GNUNET_TIME_Timestamp previous_change;
752 1 : struct TALER_FullPayto null_payto = { NULL };
753 : struct GNUNET_HashCode attributes_hash;
754 1 : const char *no_events[1] = { NULL };
755 : struct AttributeContext ctx;
756 : json_t *new_rules;
757 : void *enc;
758 : size_t enc_size;
759 : bool invalid_officer;
760 : bool unknown_account;
761 : bool is_wallet;
762 : uint64_t lms;
763 : struct GNUNET_TIME_Timestamp last_date;
764 : enum GNUNET_DB_QueryStatus qs;
765 :
766 1 : TDB_FILL (officer_pub,
767 : 20);
768 1 : TDB_FILL (officer_sig,
769 : 21);
770 1 : TDB_FILL (master_sig,
771 : 22);
772 1 : TDB_FILL (attributes_hash,
773 : 23);
774 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
775 : TALER_EXCHANGEDB_insert_aml_officer (pg,
776 : &officer_pub,
777 : &master_sig,
778 : OFFICER_NAME,
779 : true,
780 : false,
781 : GNUNET_TIME_timestamp_get (),
782 : &previous_change));
783 1 : encrypt_attributes ("Carol",
784 : &enc,
785 : &enc_size);
786 1 : new_rules = GNUNET_JSON_PACK (
787 : GNUNET_JSON_pack_array_steal ("rules",
788 : json_array ()));
789 1 : GNUNET_assert (NULL != new_rules);
790 1 : qs = TALER_EXCHANGEDB_insert_aml_decision (
791 : pg,
792 : null_payto,
793 : &account.h_normalized,
794 : GNUNET_TIME_timestamp_get (),
795 : GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_HOURS),
796 : NULL,
797 : new_rules,
798 : false,
799 : NULL,
800 : NULL,
801 : "collected in person",
802 : &officer_pub,
803 : &officer_sig,
804 : 0,
805 : no_events,
806 : "test-form",
807 : enc_size,
808 : enc,
809 : &attributes_hash,
810 : GNUNET_TIME_relative_to_timestamp (GNUNET_TIME_UNIT_DAYS),
811 : &invalid_officer,
812 : &unknown_account,
813 : &last_date,
814 : &lms,
815 : &is_wallet);
816 1 : json_decref (new_rules);
817 1 : GNUNET_free (enc);
818 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
819 1 : FAILIF (invalid_officer);
820 1 : FAILIF (3 != TDB_count (pg,
821 : "FROM kyc_attributes"));
822 1 : FAILIF (1 != TDB_count (pg,
823 : "FROM kyc_attributes"
824 : " WHERE by_aml_officer"));
825 :
826 : /* ascending from the start: all three rows, the officer's last */
827 1 : memset (&ctx,
828 : 0,
829 : sizeof (ctx));
830 1 : FAILIF (0 >=
831 : TALER_EXCHANGEDB_iterate_aml_attributes (pg,
832 : &account.h_normalized,
833 : 0,
834 : 16,
835 : &aml_attribute_cb,
836 : &ctx));
837 1 : FAILIF_C (3 != ctx.total,
838 : ac_free (&ctx));
839 1 : FAILIF_C (! ctx.by_aml_officer,
840 : ac_free (&ctx));
841 1 : FAILIF_C (NULL == ctx.officer,
842 : ac_free (&ctx));
843 1 : FAILIF_C (0 != strcmp (ctx.officer,
844 : OFFICER_NAME),
845 : ac_free (&ctx));
846 1 : FAILIF_C (0 != strcmp (ctx.name,
847 : "Carol"),
848 : ac_free (&ctx));
849 1 : ac_free (&ctx);
850 :
851 : /* descending from the end: only the newest row */
852 1 : memset (&ctx,
853 : 0,
854 : sizeof (ctx));
855 1 : FAILIF (0 >=
856 : TALER_EXCHANGEDB_iterate_aml_attributes (pg,
857 : &account.h_normalized,
858 : INT64_MAX,
859 : -1,
860 : &aml_attribute_cb,
861 : &ctx));
862 1 : FAILIF_C (1 != ctx.total,
863 : ac_free (&ctx));
864 1 : FAILIF_C (! ctx.by_aml_officer,
865 : ac_free (&ctx));
866 1 : ac_free (&ctx);
867 :
868 : /* ascending past the last row: nothing */
869 1 : memset (&ctx,
870 : 0,
871 : sizeof (ctx));
872 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
873 : TALER_EXCHANGEDB_iterate_aml_attributes (pg,
874 : &account.h_normalized,
875 : INT64_MAX,
876 : 16,
877 : &aml_attribute_cb,
878 : &ctx));
879 1 : FAILIF_C (0 != ctx.total,
880 : ac_free (&ctx));
881 1 : ac_free (&ctx);
882 1 : return 0;
883 : }
884 :
885 :
886 : /**
887 : * The "current attributes" builder returns the newest set, decrypted.
888 : *
889 : * @param pg the database context
890 : * @return 0 on success
891 : */
892 : static int
893 1 : check_current_attributes (struct TALER_EXCHANGEDB_PostgresContext *pg)
894 : {
895 1 : struct TALER_EXCHANGEDB_HistoryBuilderContext hbc = {
896 : .account = &account.h_normalized,
897 : .pg = pg,
898 : .attribute_key = &attribute_key,
899 : .is_wallet = false
900 : };
901 : json_t *j;
902 : const char *name;
903 :
904 1 : j = TALER_EXCHANGEDB_current_attributes_builder (&hbc);
905 1 : FAILIF (NULL == j);
906 1 : name = json_string_value (json_object_get (j,
907 : "NAME"));
908 1 : FAILIF_C (NULL == name,
909 : json_decref (j));
910 1 : FAILIF_C (0 != strcmp (name,
911 : "Carol"),
912 : json_decref (j));
913 1 : json_decref (j);
914 1 : return 0;
915 : }
916 :
917 :
918 : /**
919 : * The checks to run, in order.
920 : */
921 : static const struct TDB_Test tests[] = {
922 : { "kyc-attributes-empty",
923 : &check_empty },
924 : { "kyc-attributes-insert",
925 : &check_insert },
926 : { "kyc-attributes-insert-wrong-provider",
927 : &check_insert_wrong_provider },
928 : { "kyc-attributes-iterate",
929 : &check_iterate },
930 : { "kyc-attributes-kyc-history",
931 : &check_kyc_history },
932 : { "kyc-attributes-aml-attributes",
933 : &check_aml_attributes },
934 : { "kyc-attributes-current-attributes",
935 : &check_current_attributes },
936 : { NULL, NULL }
937 : };
938 :
939 :
940 : int
941 1 : main (int argc,
942 : char *const *argv)
943 : {
944 : int ret;
945 :
946 1 : ret = TDB_main (argc,
947 : argv,
948 : "test-kyc-attributes",
949 : "Tests for the exchangedb `kyc_attributes' table",
950 : tests);
951 1 : TDB_account_free (&account);
952 1 : return ret;
953 : }
954 :
955 :
956 : /* end of test_kyc_attributes.c */
|