exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 77c374a395800a1b98cbc6d30bcb4493422f8098
parent 39f7da9c11a0e6ba2993e23465a38bf4ebc68ec4
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat,  6 Dec 2025 19:32:42 +0100

fix #10741

Diffstat:
Mcontrib/typst/vqf_902_11.typ | 10+++++-----
Mcontrib/typst/vqf_902_11_customer.typ | 10+++++-----
Mcontrib/typst/vqf_902_11_officer.typ | 10+++++-----
Mcontrib/typst/vqf_902_9.typ | 10+++++-----
Mcontrib/typst/vqf_902_9_customer.typ | 10+++++-----
Mcontrib/typst/vqf_902_9_officer.typ | 10+++++-----
Msrc/exchange/taler-exchange-httpd_aml-attributes-get.c | 13+++++++++++++
Msrc/exchangedb/exchange_do_insert_aml_decision.sql | 2+-
Msrc/exchangedb/exchangedb_history.c | 3+++
Msrc/exchangedb/pg_select_aml_attributes.c | 52++++++++++++++++++++++++++++++++++++----------------
Msrc/include/taler/taler_exchangedb_plugin.h | 2++
11 files changed, 85 insertions(+), 47 deletions(-)

diff --git a/contrib/typst/vqf_902_11.typ b/contrib/typst/vqf_902_11.typ @@ -183,9 +183,9 @@ v(0.5em) // Signature Section - let submitted_by = get("SUBMITTED_BY") + let submitted_by_officer = get("BY_AML_OFFICER") - if submitted_by == "CUSTOMER" { + if submitted_by_officer == false { table( columns: (40%, 10%, 50%), stroke: 0.5pt + black, @@ -203,7 +203,7 @@ text(size: 9pt, style: "italic")[ It is a criminal offence to deliberately provide false information on this form (article 251 of the Swiss Criminal Code, documents forgery). ] - } else if submitted_by == "AML_OFFICER" { + } else if submitted_by_officer == true { text(weight: "bold")[Signed declaration by the customer] v(0.5em) @@ -224,7 +224,7 @@ [#if get("ATTACHMENT_SIGNED_DOCUMENT") != "" [Document attached] else [No document]] ) } else { - text(weight: "bold")[Invalid submitter (#submitted_by)] + text(weight: "bold")[Invalid submitter (#submitted_by_officer)] } } @@ -241,7 +241,7 @@ ), ), "THIRD_PARTY_OWNERSHIP": false, - "SUBMITTED_BY": "CUSTOMER", + "BY_AML_OFFICER": false, "SIGNATURE": "Jane Smith", "SIGN_DATE": "10.11.2025", )) \ No newline at end of file diff --git a/contrib/typst/vqf_902_11_customer.typ b/contrib/typst/vqf_902_11_customer.typ @@ -179,9 +179,9 @@ v(0.5em) // Signature Section - let submitted_by = get("SUBMITTED_BY") + let submitted_by_officer = get("BY_AML_OFFICER") - if submitted_by == "CUSTOMER" { + if submitted_by_officer == false { table( columns: (40%, 10%, 50%), stroke: 0.5pt + black, @@ -199,7 +199,7 @@ text(size: 9pt, style: "italic")[ It is a criminal offence to deliberately provide false information on this form (article 251 of the Swiss Criminal Code, documents forgery). ] - } else if submitted_by == "AML_OFFICER" { + } else if submitted_by_officer == true { text(weight: "bold")[Signed declaration by the customer] v(0.5em) @@ -220,7 +220,7 @@ [#if get("ATTACHMENT_SIGNED_DOCUMENT") != "" [Document attached] else [No document]] ) } else { - text(weight: "bold")[Invalid submitter (#submitted_by)] + text(weight: "bold")[Invalid submitter (#submitted_by_officer)] } } @@ -237,7 +237,7 @@ ), ), "THIRD_PARTY_OWNERSHIP": false, - "SUBMITTED_BY": "CUSTOMER", + "BY_AML_OFFICER": false, "SIGNATURE": "Jane Smith", "SIGN_DATE": "10.11.2025", )) \ No newline at end of file diff --git a/contrib/typst/vqf_902_11_officer.typ b/contrib/typst/vqf_902_11_officer.typ @@ -179,9 +179,9 @@ v(0.5em) // Signature Section - let submitted_by = get("SUBMITTED_BY") + let submitted_by_officer = get("BY_AML_OFFICER") - if submitted_by == "CUSTOMER" { + if submitted_by_officer == false { table( columns: (40%, 10%, 50%), stroke: 0.5pt + black, @@ -199,7 +199,7 @@ text(size: 9pt, style: "italic")[ It is a criminal offence to deliberately provide false information on this form (article 251 of the Swiss Criminal Code, documents forgery). ] - } else if submitted_by == "AML_OFFICER" { + } else if submitted_by_officer == true { text(weight: "bold")[Signed declaration by the customer] v(0.5em) @@ -220,7 +220,7 @@ [#if get("ATTACHMENT_SIGNED_DOCUMENT") != "" [Document attached] else [No document]] ) } else { - text(weight: "bold")[Invalid submitter (#submitted_by)] + text(weight: "bold")[Invalid submitter (#submitted_by_officer)] } } @@ -238,7 +238,7 @@ ), ), "THIRD_PARTY_OWNERSHIP": false, - "SUBMITTED_BY": "OFFICER", + "BY_AML_OFFICER": true, "SIGNATURE": "Jane Smith", "SIGN_DATE": "10.11.2025", )) \ No newline at end of file diff --git a/contrib/typst/vqf_902_9.typ b/contrib/typst/vqf_902_9.typ @@ -141,9 +141,9 @@ v(1.5em) // Signature Section - let submitted_by = get("SUBMITTED_BY") + let submitted_by_officer = get("BY_AML_OFFICER") - if submitted_by == "CUSTOMER" { + if submitted_by_officer == false { table( columns: (40%, 10%, 50%), stroke: 0.5pt + black, @@ -161,7 +161,7 @@ text(size: 9pt, style: "italic")[ It is a criminal offence to deliberately provide false information on this form (article 251 of the Swiss Criminal Code, document forgery) ] - } else if submitted_by == "AML_OFFICER" { + } else if submitted_by_officer == true { text(weight: "bold")[Signed declaration by the customer] v(0.5em) @@ -187,7 +187,7 @@ ] ) } else { - text(weight: "bold")[Invalid submitter (#submitted_by)] + text(weight: "bold")[Invalid submitter (#submitted_by_officer)] } } @@ -204,7 +204,7 @@ "DOMICILE_ADDRESS": "Teststrasse 123\n8001 Zurich" ), ), - "SUBMITTED_BY": "CUSTOMER", + "BY_AML_OFFICER": false, "SIGNATURE": "John Doe", "SIGN_DATE": "10.11.2025", )) \ No newline at end of file diff --git a/contrib/typst/vqf_902_9_customer.typ b/contrib/typst/vqf_902_9_customer.typ @@ -137,9 +137,9 @@ v(1.5em) // Signature Section - let submitted_by = get("SUBMITTED_BY") + let submitted_by_officer = get("BY_AML_OFFICER") - if submitted_by == "CUSTOMER" { + if submitted_by_officer == false { table( columns: (40%, 10%, 50%), stroke: 0.5pt + black, @@ -157,7 +157,7 @@ text(size: 9pt, style: "italic")[ It is a criminal offence to deliberately provide false information on this form (article 251 of the Swiss Criminal Code, document forgery) ] - } else if submitted_by == "AML_OFFICER" { + } else if submitted_by_officer == true { text(weight: "bold")[Signed declaration by the customer] v(0.5em) @@ -183,7 +183,7 @@ ] ) } else { - text(weight: "bold")[Invalid submitter (#submitted_by)] + text(weight: "bold")[Invalid submitter (#submitted_by_officer)] } } @@ -200,7 +200,7 @@ "DOMICILE_ADDRESS": "Teststrasse 123\n8001 Zurich" ), ), - "SUBMITTED_BY": "CUSTOMER", + "BY_AML_OFFICER": false, "SIGNATURE": "John Doe", "SIGN_DATE": "10.11.2025", )) \ No newline at end of file diff --git a/contrib/typst/vqf_902_9_officer.typ b/contrib/typst/vqf_902_9_officer.typ @@ -137,9 +137,9 @@ v(1.5em) // Signature Section - let submitted_by = get("SUBMITTED_BY") + let submitted_by_officer = get("BY_AML_OFFICER") - if submitted_by == "CUSTOMER" { + if submitted_by_officer == false { table( columns: (40%, 10%, 50%), stroke: 0.5pt + black, @@ -157,7 +157,7 @@ text(size: 9pt, style: "italic")[ It is a criminal offence to deliberately provide false information on this form (article 251 of the Swiss Criminal Code, document forgery) ] - } else if submitted_by == "AML_OFFICER" { + } else if submitted_by_officer == true { text(weight: "bold")[Signed declaration by the customer] v(0.5em) @@ -183,7 +183,7 @@ ] ) } else { - text(weight: "bold")[Invalid submitter (#submitted_by)] + text(weight: "bold")[Invalid submitter (#submitted_by_officer)] } } @@ -201,7 +201,7 @@ "DOMICILE_ADDRESS": "Teststrasse 123\n8001 Zurich" ), ), - "SUBMITTED_BY": "OFFICER", + "BY_AML_OFFICER": true, "SIGNATURE": "John Doe", "SIGN_DATE": "10.11.2025", )) \ No newline at end of file diff --git a/src/exchange/taler-exchange-httpd_aml-attributes-get.c b/src/exchange/taler-exchange-httpd_aml-attributes-get.c @@ -273,6 +273,7 @@ dump_attachments (struct ResponseContext *rc, * @param row_id current row in kyc_attributes table * @param collection_time when were the attributes collected * @param by_aml_officer was the attribute set filed by the AML officer + * @param staff_name name of the officer, NULL if not @a by_aml_officer * @param enc_attributes_size length of @a enc_attributes * @param enc_attributes the encrypted collected attributes */ @@ -282,6 +283,7 @@ detail_cb ( uint64_t row_id, struct GNUNET_TIME_Timestamp collection_time, bool by_aml_officer, + const char *staff_name, size_t enc_attributes_size, const void *enc_attributes) { @@ -344,6 +346,17 @@ detail_cb ( json_object_set_new (attrs, "BY_AML_OFFICER", json_boolean (by_aml_officer))); + GNUNET_assert (0 == + json_object_set_new (attrs, + "FILING_DATE", + json_string ( + GNUNET_STRINGS_timestamp_to_string ( + collection_time)))); + if (by_aml_officer) + GNUNET_assert (0 == + json_object_set_new (attrs, + "AML_STAFF_NAME", + json_string (staff_name))); { char *have; diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -239,7 +239,7 @@ THEN -- Process starts and finishes instantly ,in_decision_time ,'aml-officer' - ,in_decider_pub + ,ENCODE(in_decider_pub, 'base64') ,TRUE ) RETURNING legitimization_process_serial_id diff --git a/src/exchangedb/exchangedb_history.c b/src/exchangedb/exchangedb_history.c @@ -296,6 +296,7 @@ struct DecryptContext * @param row_id current row in kyc_attributes table * @param collection_time when were the attributes collected * @param by_aml_officer true if filed by AML officer + * @param officer_name name of the officer, NULL if not @a by_aml_officer * @param enc_attributes_size size of @a enc_attributes * @param enc_attributes the encrypted collected attributes */ @@ -305,6 +306,7 @@ decrypt_attributes ( uint64_t row_id, struct GNUNET_TIME_Timestamp collection_time, bool by_aml_officer, + const char *officer_name, size_t enc_attributes_size, const void *enc_attributes) { @@ -312,6 +314,7 @@ decrypt_attributes ( (void) row_id; (void) collection_time; + (void) officer_name; decon->attr = TALER_CRYPTO_kyc_attributes_decrypt (decon->hbc->attribute_key, enc_attributes, diff --git a/src/exchangedb/pg_select_aml_attributes.c b/src/exchangedb/pg_select_aml_attributes.c @@ -73,6 +73,7 @@ handle_aml_attributes (void *cls, { uint64_t rowid; struct GNUNET_TIME_Timestamp collection_time; + char *officer_name = NULL; bool by_aml_officer; size_t enc_attributes_size; void *enc_attributes; @@ -83,6 +84,10 @@ handle_aml_attributes (void *cls, &collection_time), GNUNET_PQ_result_spec_bool ("by_aml_officer", &by_aml_officer), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("decider_name", + &officer_name), + NULL), GNUNET_PQ_result_spec_variable_size ("encrypted_attributes", &enc_attributes, &enc_attributes_size), @@ -103,6 +108,7 @@ handle_aml_attributes (void *cls, rowid, collection_time, by_aml_officer, + officer_name, enc_attributes_size, enc_attributes); GNUNET_PQ_cleanup_result (rs); @@ -141,26 +147,40 @@ TEH_PG_select_aml_attributes ( PREPARE (pg, "select_aml_attributes_inc", "SELECT" - " kyc_attributes_serial_id" - ",collection_time" - ",by_aml_officer" - ",encrypted_attributes" - " FROM kyc_attributes" - " WHERE h_payto=$1" - " AND kyc_attributes_serial_id > $2" - " ORDER BY kyc_attributes_serial_id ASC" + " ka.kyc_attributes_serial_id" + ",ka.collection_time" + ",ka.by_aml_officer" + ",astaff.decider_name" + ",ka.encrypted_attributes" + " FROM kyc_attributes ka" + " LEFT JOIN legitimization_processes lp" + " ON (ka.by_aml_officer AND" + " (ka.legitimization_serial = lp.legitimization_process_serial_id))" + " LEFT JOIN aml_staff astaff" + " ON (ka.by_aml_officer AND" + " (DECODE(lp.provider_user_id, 'base64') = astaff.decider_pub))" + " WHERE ka.h_payto=$1" + " AND ka.kyc_attributes_serial_id > $2" + " ORDER BY ka.kyc_attributes_serial_id ASC" " LIMIT $3"); PREPARE (pg, "select_aml_attributes_dec", "SELECT" - " kyc_attributes_serial_id" - ",collection_time" - ",by_aml_officer" - ",encrypted_attributes" - " FROM kyc_attributes" - " WHERE h_payto=$1" - " AND kyc_attributes_serial_id < $2" - " ORDER BY kyc_attributes_serial_id DESC" + " ka.kyc_attributes_serial_id" + ",ka.collection_time" + ",ka.by_aml_officer" + ",astaff.decider_name" + ",ka.encrypted_attributes" + " FROM kyc_attributes ka" + " LEFT JOIN legitimization_processes lp" + " ON (ka.by_aml_officer AND" + " (ka.legitimization_serial = lp.legitimization_process_serial_id))" + " LEFT JOIN aml_staff astaff" + " ON (ka.by_aml_officer AND" + " (DECODE(lp.provider_user_id, 'base64') = astaff.decider_pub))" + " WHERE ka.h_payto=$1" + " AND ka.kyc_attributes_serial_id < $2" + " ORDER BY ka.kyc_attributes_serial_id DESC" " LIMIT $3"); qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, stmt, diff --git a/src/include/taler/taler_exchangedb_plugin.h b/src/include/taler/taler_exchangedb_plugin.h @@ -3905,6 +3905,7 @@ typedef void * @param row_id current row in kyc_attributes table * @param collection_time when were the attributes collected * @param by_aml_officer true if the data was filed by an AML officer + * @param officer_name name of the officer, NULL if not @a by_aml_officer * @param enc_attributes_size size of @a enc_attributes * @param enc_attributes the encrypted collected attributes */ @@ -3914,6 +3915,7 @@ typedef void uint64_t row_id, struct GNUNET_TIME_Timestamp collection_time, bool by_aml_officer, + const char *officer_name, size_t enc_attributes_size, const void *enc_attributes);