commit 610fe62eec4f407313c0387702dbe33ed2b24aa4 parent feb57a2ebc251cb3cbe687875575174db4571863 Author: Christian Grothoff <christian@grothoff.org> Date: Sun, 8 Dec 2024 13:56:39 +0100 persist KYC attributes before calling AML program Diffstat:
35 files changed, 953 insertions(+), 395 deletions(-)
diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c @@ -55,31 +55,11 @@ struct TEH_KycMeasureRunContext struct TALER_NormalizedPaytoHashP account_id; /** - * until when is the KYC data valid - */ - struct GNUNET_TIME_Absolute expiration; - - /** * legitimization process the KYC data is about */ uint64_t process_row; /** - * Name of the provider with the logic that was run - */ - char *provider_name; - - /** - * set to user ID at the provider, or NULL if not supported or unknown - */ - char *provider_user_id; - - /** - * provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown - */ - char *provider_legitimization_id; - - /** * function to call with the result */ TEH_KycMeasureRunContextCallback cb; @@ -100,11 +80,6 @@ struct TEH_KycMeasureRunContext char *fallback_name; /** - * user attributes returned by the provider - */ - json_t *attributes; - - /** * Measures this KYC process is responding to. */ json_t *jmeasures; @@ -133,14 +108,14 @@ struct TEH_KycMeasureRunContext * fallback measure. * * @param cls a `struct TEH_KycMeasureRunContext *` - * @param result true if the fallback was activated + * @param fallback_ok true if the fallback was activated * successfully * @param requirement_row row of * new KYC requirement that was created, 0 for none */ static void fallback_result_cb (void *cls, - bool result, + bool fallback_ok, uint64_t requirement_row) { struct TEH_KycMeasureRunContext *kat = cls; @@ -150,14 +125,18 @@ fallback_result_cb (void *cls, (void) requirement_row; GNUNET_async_scope_enter (&kat->scope, &old_scope); - if (result) + if (fallback_ok) { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Fallback completed (row %llu)\n", + (unsigned long long) requirement_row); kat->cb (kat->cb_cls, TALER_EC_EXCHANGE_KYC_AML_PROGRAM_FAILURE, NULL); } else { + GNUNET_break (0); kat->cb (kat->cb_cls, TALER_EC_EXCHANGE_GENERIC_KYC_FALLBACK_FAILED, kat->fallback_name); @@ -180,46 +159,17 @@ kyc_aml_finished ( { struct TEH_KycMeasureRunContext *kat = cls; enum GNUNET_DB_QueryStatus qs; - unsigned int birthday = 0; struct GNUNET_AsyncScopeSave old_scope; kat->kyc_aml = NULL; GNUNET_async_scope_enter (&kat->scope, &old_scope); - if (TEH_age_restriction_enabled) - { - const char *birthdate; - - birthdate = json_string_value ( - json_object_get (kat->attributes, - TALER_ATTRIBUTE_BIRTHDATE)); - if (NULL != birthdate) - { - enum GNUNET_GenericReturnValue ret; - - ret = TALER_parse_coarse_date (birthdate, - &TEH_age_restriction_config.mask, - &birthday); - - if (GNUNET_OK != ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse birthdate `%s' from KYC attributes\n", - birthdate); - } - } - } - + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "AML program finished with status %d\n", + (int) apr->status); qs = TALER_EXCHANGEDB_persist_aml_program_result ( TEH_plugin, kat->process_row, - kat->provider_name, - kat->provider_user_id, - kat->provider_legitimization_id, - kat->attributes, - &TEH_attribute_key, - birthday, - kat->expiration, &kat->account_id, apr); switch (qs) @@ -245,6 +195,7 @@ kyc_aml_finished ( { /* Not sure this can happen (fallback required?), but report AML program failure to client */ + GNUNET_break (0); kat->cb (kat->cb_cls, TALER_EC_EXCHANGE_KYC_AML_PROGRAM_FAILURE, NULL); @@ -253,12 +204,14 @@ kyc_aml_finished ( kat->fallback_name = GNUNET_strdup ( apr->details.failure.fallback_measure); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "AML program failed, running fallback %s\n", + kat->fallback_name); kat->fb = TEH_kyc_fallback ( &kat->scope, &kat->account_id, kat->process_row, - kat->attributes, kat->fallback_name, &fallback_result_cb, kat); @@ -303,58 +256,113 @@ TEH_kyc_run_measure_cancel (struct TEH_KycMeasureRunContext *kat) GNUNET_SCHEDULER_cancel (kat->async_task); kat->async_task = NULL; } - GNUNET_free (kat->provider_name); - GNUNET_free (kat->provider_user_id); - GNUNET_free (kat->provider_legitimization_id); GNUNET_free (kat->fallback_name); json_decref (kat->jmeasures); - json_decref (kat->attributes); GNUNET_free (kat); } -struct TEH_KycMeasureRunContext * -TEH_kyc_run_measure_for_attributes ( - const struct GNUNET_AsyncScopeId *scope, +enum GNUNET_DB_QueryStatus +TEH_kyc_store_attributes ( uint64_t process_row, const struct TALER_NormalizedPaytoHashP *account_id, const char *provider_name, const char *provider_user_id, const char *provider_legitimization_id, struct GNUNET_TIME_Absolute expiration, - const json_t *new_attributes, + const json_t *new_attributes) +{ + enum GNUNET_DB_QueryStatus qs; + unsigned int birthday = 0; + size_t eas = 0; + void *ea = NULL; + + if (TEH_age_restriction_enabled) + { + const char *birthdate; + + birthdate = json_string_value ( + json_object_get (new_attributes, + TALER_ATTRIBUTE_BIRTHDATE)); + if (NULL != birthdate) + { + enum GNUNET_GenericReturnValue ret; + + ret = TALER_parse_coarse_date (birthdate, + &TEH_age_restriction_config.mask, + &birthday); + + if (GNUNET_OK != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse birthdate `%s' from KYC attributes of %s\n", + birthdate, + provider_name); + } + } + } + if (NULL != new_attributes) + { + TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, + new_attributes, + &ea, + &eas); + } + qs = TEH_plugin->persist_kyc_attributes ( + TEH_plugin->cls, + process_row, + account_id, + provider_name, + provider_user_id, + provider_legitimization_id, + birthday, + expiration, + eas, + ea); + GNUNET_free (ea); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + break; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Stored encrypted KYC process #%llu attributes: %d\n", + (unsigned long long) process_row, + qs); + return qs; +} + + +struct TEH_KycMeasureRunContext * +TEH_kyc_run_measure_for_attributes ( + const struct GNUNET_AsyncScopeId *scope, + uint64_t process_row, + const struct TALER_NormalizedPaytoHashP *account_id, TEH_KycMeasureRunContextCallback cb, void *cb_cls) { - /* FIXME(fdold, 2024-11-07): - #9303 - Consider storing the attributes *before* we run the AMP. - Also, we're only passing *new* attributes here, but the AMP - should receive *all* attributes. */ - struct TEH_KycMeasureRunContext *kat; enum GNUNET_DB_QueryStatus qs; kat = GNUNET_new (struct TEH_KycMeasureRunContext); kat->scope = *scope; - kat->provider_name = GNUNET_strdup (provider_name); kat->process_row = process_row; kat->account_id = *account_id; - if (NULL != provider_user_id) - kat->provider_user_id - = GNUNET_strdup (provider_user_id); - if (NULL != provider_legitimization_id) - kat->provider_legitimization_id - = GNUNET_strdup (provider_legitimization_id); - kat->expiration = expiration; kat->cb = cb; kat->cb_cls = cb_cls; - kat->attributes = json_incref ((json_t*) new_attributes); - qs = TEH_plugin->lookup_active_legitimization ( TEH_plugin->cls, process_row, &kat->measure_index, - &kat->provider_name, &kat->jmeasures); switch (qs) { @@ -381,7 +389,8 @@ TEH_kyc_run_measure_for_attributes ( = TALER_KYCLOGIC_run_aml_program ( kat->jmeasures, kat->measure_index, - kat->attributes, + &TALER_EXCHANGEDB_current_attributes_builder, + &hbc, &TALER_EXCHANGEDB_current_rule_builder, &hbc, &TALER_EXCHANGEDB_aml_history_builder, @@ -438,11 +447,9 @@ TEH_kyc_run_measure_directly ( kat = GNUNET_new (struct TEH_KycMeasureRunContext); kat->jmeasures = TALER_KYCLOGIC_measure_to_jmeasures (instant_ms); - kat->provider_name = GNUNET_strdup ("SKIP"); kat->measure_index = 0; kat->scope = *scope; kat->account_id = *account_id; - kat->expiration = GNUNET_TIME_UNIT_FOREVER_ABS; kat->cb = cb; kat->cb_cls = cb_cls; @@ -511,7 +518,8 @@ TEH_kyc_run_measure_directly ( kat->kyc_aml = TALER_KYCLOGIC_run_aml_program3 ( instant_ms, - NULL, /* no attributes */ + &TALER_EXCHANGEDB_current_attributes_builder, + &hbc, &TALER_EXCHANGEDB_current_rule_builder, &hbc, &TALER_EXCHANGEDB_aml_history_builder, @@ -634,19 +642,12 @@ handle_aml_fallback_result ( TEH_plugin->cls, fb->orig_requirement_row, &fb->account_id, - 0, - GNUNET_TIME_timestamp_get (), - "NONE", - NULL, - NULL, - GNUNET_TIME_UNIT_FOREVER_ABS, + apr->details.success.expiration_time, apr->details.success.account_properties, apr->details.success.new_rules, apr->details.success.to_investigate, apr->details.success.num_events, - apr->details.success.events, - 0 /* enc attr size */, - NULL /* enc attr */); + apr->details.success.events); if (qs < 0) { GNUNET_break (0); @@ -694,7 +695,6 @@ TEH_kyc_fallback ( const struct GNUNET_AsyncScopeId *scope, const struct TALER_NormalizedPaytoHashP *account_id, uint64_t orig_requirement_row, - const json_t *attributes, const char *fallback_measure, TEH_KycAmlFallbackCallback cb, void *cb_cls) @@ -729,8 +729,9 @@ TEH_kyc_fallback ( fb->aprh = TALER_KYCLOGIC_run_aml_program2 ( kcc.prog_name, - attributes, kcc.context, + &TALER_EXCHANGEDB_current_attributes_builder, + &hbc, &TALER_EXCHANGEDB_current_rule_builder, &hbc, &TALER_EXCHANGEDB_aml_history_builder, @@ -976,6 +977,7 @@ legi_fail (struct TEH_LegitimizationCheckHandle *lch, enum TALER_ErrorCode ec, const char *details) { + GNUNET_break (0); lch->lcr.http_status = TALER_ErrorCode_get_http_status (ec); lch->lcr.response @@ -1017,6 +1019,7 @@ legi_check_aml_trigger_cb ( lch->kat = NULL; if (TALER_EC_NONE != ec) { + GNUNET_break (0); lch->lcr.http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; lch->lcr.response = TALER_MHD_make_error ( ec, diff --git a/src/exchange/taler-exchange-httpd_common_kyc.h b/src/exchange/taler-exchange-httpd_common_kyc.h @@ -52,14 +52,10 @@ struct TEH_KycMeasureRunContext; /** - * Run measure after storing attributes from the given - * provider. - * - * Only works when a process is active. + * Store attributes from the given KYC provider. * * FIXME: Isn't the account_id redundant via the process_row? * - * @param scope the HTTP request logging scope * @param process_row legitimization process the data provided is about * @param account_id account the the data provided is about * @param provider_name name of the provider that provided the attributes @@ -67,20 +63,38 @@ struct TEH_KycMeasureRunContext; * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown * @param expiration until when is the KYC check valid * @param new_attributes user attributes returned by the provider + * @return handle to cancel the operation + */ +enum GNUNET_DB_QueryStatus +TEH_kyc_store_attributes ( + uint64_t process_row, + const struct TALER_NormalizedPaytoHashP *account_id, + const char *provider_name, + const char *provider_user_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Absolute expiration, + const json_t *new_attributes); + + +/** + * Run measure after storing attributes from the given + * provider. + * + * Only works when a process is active. + * + * @param scope the HTTP request logging scope + * @param process_row legitimization process the data provided is about + * @param account_id account the the data provided is about * @param cb function to call with the result * @param cb_cls closure for @a cb * @return handle to cancel the operation */ +// FIXME: function should probably be renamed... struct TEH_KycMeasureRunContext * TEH_kyc_run_measure_for_attributes ( const struct GNUNET_AsyncScopeId *scope, uint64_t process_row, const struct TALER_NormalizedPaytoHashP *account_id, - const char *provider_name, - const char *provider_user_id, - const char *provider_legitimization_id, - struct GNUNET_TIME_Absolute expiration, - const json_t *new_attributes, TEH_KycMeasureRunContextCallback cb, void *cb_cls); @@ -149,7 +163,6 @@ typedef void * @param account_id account to activate fallback for * @param orig_requirement_row original requirement * row that now triggered the fallback - * @param attributes attributes to run with * @param fallback_measure fallback to activate * @param cb callback to call with result * @param cb_cls closure for @a cb @@ -161,7 +174,6 @@ TEH_kyc_fallback ( const struct GNUNET_AsyncScopeId *scope, const struct TALER_NormalizedPaytoHashP *account_id, uint64_t orig_requirement_row, - const json_t *attributes, const char *fallback_measure, TEH_KycAmlFallbackCallback cb, void *cb_cls); diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c @@ -234,7 +234,12 @@ proof_finish ( if (TALER_EC_NONE != ec) { kpc->response_code = TALER_ErrorCode_get_http_status (ec); + GNUNET_break (5 != kpc->response_code / 100); GNUNET_assert (kpc->response_code != UINT_MAX); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Templating error response for %d and HTTP status %u\n", + (int) ec, + kpc->response_code); kpc->response = make_html_error ( kpc->rc->connection, "kyc-proof-internal-error", @@ -251,6 +256,10 @@ proof_finish ( kpc->proof_response_code = 0; } GNUNET_assert (NULL != kpc->response); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Resuming with response %p and status %u\n", + kpc->response, + kpc->response_code); kpc_resume (kpc); } @@ -319,6 +328,7 @@ proof_cb ( struct KycProofContext *kpc = cls; struct TEH_RequestContext *rc = kpc->rc; struct GNUNET_AsyncScopeSave old_scope; + enum GNUNET_DB_QueryStatus qs; kpc->ph = NULL; kpc->proof_response = response; @@ -331,19 +341,32 @@ proof_cb ( GNUNET_log (GNUNET_ERROR_TYPE_INFO, "KYC process #%llu succeeded with KYC provider\n", (unsigned long long) kpc->process_row); - kpc->kat = TEH_kyc_run_measure_for_attributes ( - &rc->async_scope_id, + qs = TEH_kyc_store_attributes ( kpc->process_row, &kpc->h_payto, provider_name, provider_user_id, provider_legitimization_id, expiration, - attributes, + attributes); + if (0 >= qs) + { + GNUNET_break (0); + proof_finish (kpc, + TALER_EC_GENERIC_DB_STORE_FAILED, + "kyc_store_attributes"); + return; + } + + kpc->kat = TEH_kyc_run_measure_for_attributes ( + &rc->async_scope_id, + kpc->process_row, + &kpc->h_payto, &proof_finish, kpc); if (NULL == kpc->kat) { + GNUNET_break (0); proof_finish (kpc, TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, "[exchange] AML_KYC_TRIGGER"); @@ -365,6 +388,7 @@ proof_cb ( char *msg; /* OAuth2 server had a problem, do NOT log this as a KYC failure */ + GNUNET_break (0); GNUNET_asprintf (&msg, "Failure by KYC provider (HTTP status %u)\n", http_status); @@ -389,11 +413,11 @@ proof_cb ( proof_finish ( kpc, TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_failure"); + "TEH_kyc_failed"); } else { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "KYC process #%llu failed with status %d\n", (unsigned long long) kpc->process_row, status); @@ -527,6 +551,7 @@ TEH_handler_kyc_proof ( { case GNUNET_DB_STATUS_HARD_ERROR: case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); return respond_html_ec ( rc, MHD_HTTP_INTERNAL_SERVER_ERROR, diff --git a/src/exchange/taler-exchange-httpd_kyc-upload.c b/src/exchange/taler-exchange-httpd_kyc-upload.c @@ -580,7 +580,7 @@ TEH_handler_kyc_upload ( &h_payto, uc->measure_index, uc->legitimization_measure_serial_id, - "FORM", + "FORM", /* FIXME: get specific form name in DB interaction above! */ NULL, /* provider account ID */ NULL, /* provider legi ID */ &legi_process_row); @@ -593,16 +593,28 @@ TEH_handler_kyc_upload ( TALER_EC_GENERIC_DB_STORE_FAILED, "insert_kyc_requirement_process"); } - - uc->kat = TEH_kyc_run_measure_for_attributes ( - &rc->async_scope_id, + qs = TEH_kyc_store_attributes ( legi_process_row, &h_payto, "FORM", /* FIXME: get specific form name in DB interaction above! */ NULL /* provider account */, NULL /* provider legi ID */, GNUNET_TIME_UNIT_FOREVER_ABS, /* expiration time */ - uc->result, + uc->result); + if (0 >= qs) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "kyc_store_attributes"); + } + + uc->kat = TEH_kyc_run_measure_for_attributes ( + &rc->async_scope_id, + legi_process_row, + &h_payto, &aml_trigger_callback, uc); if (NULL == uc->kat) diff --git a/src/exchange/taler-exchange-httpd_kyc-webhook.c b/src/exchange/taler-exchange-httpd_kyc-webhook.c @@ -230,26 +230,36 @@ webhook_finished_cb ( struct MHD_Response *response) { struct KycWebhookContext *kwh = cls; + enum GNUNET_DB_QueryStatus qs; kwh->wh = NULL; kwh->webhook_response = response; kwh->webhook_response_code = http_status; - switch (status) { case TALER_KYCLOGIC_STATUS_SUCCESS: - kwh->kat = TEH_kyc_run_measure_for_attributes ( - &kwh->rc->async_scope_id, + qs = TEH_kyc_store_attributes ( process_row, account_id, provider_name, provider_user_id, provider_legitimization_id, expiration, - attributes, + attributes); + if (0 >= qs) + { + GNUNET_break (0); + kyc_aml_webhook_finished (kwh, + TALER_EC_GENERIC_DB_STORE_FAILED, + "kyc_store_attributes"); + return; + } + kwh->kat = TEH_kyc_run_measure_for_attributes ( + &kwh->rc->async_scope_id, + process_row, + account_id, &kyc_aml_webhook_finished, - kwh - ); + kwh); if (NULL == kwh->kat) { kyc_aml_webhook_finished (kwh, @@ -279,7 +289,7 @@ webhook_finished_cb ( GNUNET_break (0); kyc_aml_webhook_finished (kwh, TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_failure"); + "TEH_kyc_failed"); } break; default: diff --git a/src/exchangedb/0002-legitimization_processes.sql b/src/exchangedb/0002-legitimization_processes.sql @@ -55,8 +55,8 @@ BEGIN ,shard_suffix ); PERFORM comment_partitioned_column( - 'time when the KYC check was initiated, useful for garbage collection' - ,'expiration_time' + 'time when the KYC check was initiated, useful for garbage collection (absolute time, not rounded)' + ,'start_time' ,'legitimization_processes' ,shard_suffix ); @@ -67,7 +67,7 @@ BEGIN ,shard_suffix ); PERFORM comment_partitioned_column( - 'in the future if the respective KYC check was passed successfully' + 'in the future if the respective KYC check was passed successfully; an absolute time (not rounded)' ,'expiration_time' ,'legitimization_processes' ,shard_suffix diff --git a/src/exchangedb/0005-kyc_attributes.sql b/src/exchangedb/0005-kyc_attributes.sql @@ -74,7 +74,7 @@ BEGIN EXECUTE FORMAT ( 'ALTER TABLE ' || table_name || ' ADD CONSTRAINT ' || table_name || '_foreign_legitimization_outcomes' - ' FOREIGN KEY (trigger_outcome_serial) ' + ' FOREIGN KEY (trigger_outcome_serial)' ' REFERENCES legitimization_outcomes (outcome_serial_id) ON DELETE CASCADE' ); END $$; diff --git a/src/exchangedb/0005-kyc_events.sql b/src/exchangedb/0005-kyc_events.sql @@ -24,6 +24,8 @@ COMMENT ON TABLE kyc_events IS 'Records of key events for statistics. Populated via triggers.'; COMMENT ON COLUMN kyc_events.event_type IS 'Name of the event, such as account-open or sar-filed'; +COMMENT ON COLUMN kyc_events.event_timestamp + IS 'When did the event occur; timestamp in rounded absolute time'; CREATE INDEX kyc_event_index ON kyc_events(event_type,event_timestamp); diff --git a/src/exchangedb/0005-legitimization_outcomes.sql b/src/exchangedb/0005-legitimization_outcomes.sql @@ -55,7 +55,7 @@ BEGIN ,partition_suffix ); PERFORM comment_partitioned_column( - 'when was this outcome decided' + 'when was this outcome decided, rounded timestamp' ,'decision_time' ,'legitimization_outcomes' ,partition_suffix diff --git a/src/exchangedb/0007-kyc_attributes.sql b/src/exchangedb/0007-kyc_attributes.sql @@ -0,0 +1,48 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2024 Taler Systems SA +-- +-- TALER is free software; you can redistribute it and/or modify it under the +-- terms of the GNU General Public License as published by the Free Software +-- Foundation; either version 3, or (at your option) any later version. +-- +-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +-- + +CREATE FUNCTION alter_table_kyc_attributes7( + IN partition_suffix TEXT DEFAULT NULL +) +RETURNS VOID +LANGUAGE plpgsql +AS $$ +DECLARE + table_name TEXT DEFAULT 'kyc_attributes'; +BEGIN + PERFORM create_partitioned_table( + 'ALTER TABLE %I' + ' DROP COLUMN trigger_outcome_serial' + ';' + ,table_name + ,'' + ,partition_suffix + ); +END $$; + + +INSERT INTO exchange_tables + (name + ,version + ,action + ,partitioned + ,by_range) + VALUES + ('kyc_attributes7' + ,'exchange-0007' + ,'alter' + ,TRUE + ,FALSE); diff --git a/src/exchangedb/0007-wire_targets.sql b/src/exchangedb/0007-wire_targets.sql @@ -23,6 +23,8 @@ BEGIN 'ALTER TABLE wire_targets' ' ADD COLUMN h_normalized_payto BYTEA CHECK(LENGTH(h_normalized_payto)=32)' ' DEFAULT NULL' + ',ADD COLUMN aml_program_lock_timeout INT8' + ' DEFAULT NULL' ';' ); @@ -32,6 +34,12 @@ BEGIN ,'wire_targets' ,NULL ); + PERFORM comment_partitioned_column( + 'If non-NULL, an AML program should be running and it holds a lock on this account, thus other AML programs should not be started concurrently. Given the possibility of crashes, the lock automatically expires at the time value given in this column. At that time, the lock can be considered stale.' + ,'aml_program_lock_timeout' + ,'wire_targets' + ,NULL + ); END $$; diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am @@ -29,6 +29,7 @@ sqlinputs = \ 0004-*.sql \ 0005-*.sql \ 0006-*.sql \ + 0007-*.sql \ exchange-0002.sql.in \ exchange-0003.sql.in \ exchange-0004.sql.in \ @@ -198,6 +199,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_insert_active_legitimization_measure.h pg_insert_active_legitimization_measure.c \ pg_insert_aml_decision.h pg_insert_aml_decision.c \ pg_insert_aml_officer.h pg_insert_aml_officer.c \ + pg_insert_aml_program_failure.h pg_insert_aml_program_failure.c \ pg_insert_auditor.h pg_insert_auditor.c \ pg_insert_auditor_denom_sig.h pg_insert_auditor_denom_sig.c \ pg_insert_close_request.c pg_insert_close_request.h \ @@ -252,6 +254,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_lookup_wire_fee_by_time.h pg_lookup_wire_fee_by_time.c \ pg_lookup_wire_timestamp.h pg_lookup_wire_timestamp.c \ pg_lookup_wire_transfer.h pg_lookup_wire_transfer.c \ + pg_persist_kyc_attributes.h pg_persist_kyc_attributes.c \ pg_persist_policy_details.h pg_persist_policy_details.c \ pg_preflight.h pg_preflight.c \ pg_profit_drains_get_pending.h pg_profit_drains_get_pending.c \ diff --git a/src/exchangedb/exchange-0007.sql.in b/src/exchangedb/exchange-0007.sql.in @@ -22,6 +22,7 @@ SET search_path TO exchange; #include "0007-wire_targets.sql" #include "0007-legitimization_outcomes.sql" #include "0007-batch_deposits.sql" +#include "0007-kyc_attributes.sql" COMMIT; diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -68,7 +68,7 @@ SELECT decision_time IF FOUND THEN - IF out_last_date >= in_decision_time + IF out_last_date > in_decision_time THEN -- Refuse to insert older decision. RETURN; diff --git a/src/exchangedb/exchange_do_insert_kyc_measure_result.sql b/src/exchangedb/exchange_do_insert_kyc_measure_result.sql @@ -18,17 +18,11 @@ DROP FUNCTION IF EXISTS exchange_do_insert_kyc_measure_result; CREATE FUNCTION exchange_do_insert_kyc_measure_result( IN in_process_row INT8, IN in_h_payto BYTEA, - IN in_birthday INT4, - IN in_provider_name TEXT, - IN in_provider_account_id TEXT, - IN in_provider_legitimization_id TEXT, - IN in_collection_time_ts INT8, - IN in_expiration_time INT8, + IN in_decision_time INT8, IN in_expiration_time_ts INT8, IN in_account_properties TEXT, IN in_new_rules TEXT, IN ina_events TEXT[], - IN in_enc_attributes BYTEA, IN in_to_investigate BOOLEAN, IN in_kyc_completed_notify_s TEXT, OUT out_ok BOOLEAN) @@ -41,14 +35,15 @@ DECLARE ini_event TEXT; BEGIN - +-- Disactivate all previous outcomes. UPDATE legitimization_outcomes SET is_active=FALSE WHERE h_payto=in_h_payto -- this clause is a minor optimization to avoid -- updating outcomes that have long expired. - AND expiration_time >= in_collection_time_ts; + AND expiration_time >= in_decision_time; +-- Insert new rules INSERT INTO legitimization_outcomes (h_payto ,decision_time @@ -58,7 +53,7 @@ INSERT INTO legitimization_outcomes ,jnew_rules) VALUES (in_h_payto - ,in_collection_time_ts + ,in_decision_time ,in_expiration_time_ts ,in_account_properties ,in_to_investigate @@ -68,61 +63,17 @@ RETURNING INTO my_trigger_outcome_serial; -IF (in_process_row IS NOT NULL) AND - (in_enc_attributes IS NOT NULL) -THEN - INSERT INTO kyc_attributes - (h_payto - ,collection_time - ,expiration_time - ,encrypted_attributes - ,legitimization_serial - ,trigger_outcome_serial - ) VALUES - (in_h_payto - ,in_collection_time_ts - ,in_expiration_time_ts - ,in_enc_attributes - ,in_process_row - ,my_trigger_outcome_serial); -END IF; - -UPDATE legitimization_processes - SET provider_user_id=in_provider_account_id - ,provider_legitimization_id=in_provider_legitimization_id - ,expiration_time=GREATEST(expiration_time,in_expiration_time) - ,finished=TRUE - WHERE h_payto=in_h_payto - AND legitimization_process_serial_id=in_process_row - AND provider_name=in_provider_name - RETURNING legitimization_measure_serial_id - INTO my_lmsi; -out_ok=FOUND; - -IF out_ok -THEN - UPDATE legitimization_measures - SET is_finished=TRUE - WHERE legitimization_measure_serial_id=my_lmsi; -END IF; - -UPDATE reserves - SET birthday=in_birthday - WHERE (reserve_pub IN - (SELECT reserve_pub - FROM reserves_in - WHERE wire_source_h_payto IN - (SELECT wire_source_h_payto - FROM wire_targets - WHERE h_normalized_payto=in_h_payto) ) ) --- The next 3 clauses primarily serve to limit --- unnecessary updates for reserves we do not --- care about anymore. - AND ( ((current_balance).frac > 0) OR - ((current_balance).val > 0 ) ) - AND (expiration_date > in_collection_time_ts); - +-- Mark measure as complete +UPDATE legitimization_measures + SET is_finished=TRUE + WHERE legitimization_measure_serial_id= + (SELECT legitimization_measure_serial_id + FROM legitimization_processes + WHERE h_payto=in_h_payto + AND legitimization_process_serial_id=in_process_row); +out_ok = FOUND; +-- Trigger events FOR i IN 1..COALESCE(array_length(ina_events,1),0) LOOP ini_event = ina_events[i]; @@ -130,10 +81,11 @@ LOOP (event_timestamp ,event_type) VALUES - (in_collection_time_ts + (in_decision_time ,ini_event); END LOOP; +-- Notify about KYC update EXECUTE FORMAT ( 'NOTIFY %s' ,in_kyc_completed_notify_s); @@ -148,5 +100,5 @@ INSERT INTO kyc_alerts END $$; -COMMENT ON FUNCTION exchange_do_insert_kyc_measure_result(INT8, BYTEA, INT4, TEXT, TEXT, TEXT, INT8, INT8, INT8, TEXT, TEXT, TEXT[], BYTEA, BOOL, TEXT) - IS 'Inserts new KYC attributes and updates the status of the legitimization process and the AML status for the account'; +COMMENT ON FUNCTION exchange_do_insert_kyc_measure_result(INT8, BYTEA, INT8, INT8, TEXT, TEXT, TEXT[], BOOL, TEXT) + IS 'Inserts AML program outcome and updates the status of the legitimization process and the AML status for the account'; diff --git a/src/exchangedb/exchange_do_insert_successor_measure.sql b/src/exchangedb/exchange_do_insert_successor_measure.sql @@ -45,7 +45,7 @@ SELECT decision_time IF FOUND THEN - IF out_last_date >= in_decision_time + IF out_last_date > in_decision_time THEN -- Refuse to insert older decision. RETURN; diff --git a/src/exchangedb/exchange_do_persist_kyc_attributes.sql b/src/exchangedb/exchange_do_persist_kyc_attributes.sql @@ -0,0 +1,90 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2023, 2024 Taler Systems SA +-- +-- TALER is free software; you can redistribute it and/or modify it under the +-- terms of the GNU General Public License as published by the Free Software +-- Foundation; either version 3, or (at your option) any later version. +-- +-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +-- + +DROP FUNCTION IF EXISTS exchange_do_persist_kyc_attributes; +CREATE FUNCTION exchange_do_persist_kyc_attributes( + IN in_process_row INT8, + IN in_h_payto BYTEA, + IN in_birthday INT4, + IN in_provider_name TEXT, + IN in_provider_account_id TEXT, -- can be NULL + IN in_provider_legitimization_id TEXT, -- can be NULL + IN in_collection_time_ts INT8, + IN in_expiration_time INT8, -- not rounded + IN in_expiration_time_ts INT8, -- rounded to timestamp + IN in_enc_attributes BYTEA, + IN in_kyc_completed_notify_s TEXT, + OUT out_ok BOOLEAN) -- set to true if we had a legi process matching in_process_row and in_provider_name for this account +LANGUAGE plpgsql +AS $$ +BEGIN + +INSERT INTO kyc_attributes + (h_payto + ,collection_time + ,expiration_time + ,encrypted_attributes + ,legitimization_serial + ) VALUES + (in_h_payto + ,in_collection_time_ts + ,in_expiration_time_ts + ,in_enc_attributes + ,in_process_row); + +UPDATE legitimization_processes + SET provider_user_id=in_provider_account_id + ,provider_legitimization_id=in_provider_legitimization_id + ,expiration_time=GREATEST(expiration_time,in_expiration_time) + ,finished=TRUE + WHERE h_payto=in_h_payto + AND legitimization_process_serial_id=in_process_row + AND provider_name=in_provider_name; +out_ok=FOUND; + +UPDATE reserves + SET birthday=in_birthday + WHERE (reserve_pub IN + (SELECT reserve_pub + FROM reserves_in + WHERE wire_source_h_payto IN + (SELECT wire_source_h_payto + FROM wire_targets + WHERE h_normalized_payto=in_h_payto) ) ) +-- The next 3 clauses primarily serve to limit +-- unnecessary updates for reserves we do not +-- care about anymore. + AND ( ((current_balance).frac > 0) OR + ((current_balance).val > 0 ) ) + AND (expiration_date > in_collection_time_ts); + + +EXECUTE FORMAT ( + 'NOTIFY %s' + ,in_kyc_completed_notify_s); + +INSERT INTO kyc_alerts + (h_payto + ,trigger_type) + VALUES + (in_h_payto,1) + ON CONFLICT DO NOTHING; + +END $$; + + +COMMENT ON FUNCTION exchange_do_persist_kyc_attributes(INT8, BYTEA, INT4, TEXT, TEXT, TEXT, INT8, INT8, INT8, BYTEA, TEXT) + IS 'Inserts new KYC attributes and updates the status of the legitimization process'; diff --git a/src/exchangedb/exchangedb_aml.c b/src/exchangedb/exchangedb_aml.c @@ -36,65 +36,40 @@ enum GNUNET_DB_QueryStatus TALER_EXCHANGEDB_persist_aml_program_result ( struct TALER_EXCHANGEDB_Plugin *plugin, uint64_t process_row, - const char *provider_name, - const char *provider_user_id, - const char *provider_legitimization_id, - const json_t *attributes, - const struct TALER_AttributeEncryptionKeyP *attribute_key, - unsigned int birthday, - struct GNUNET_TIME_Absolute expiration, const struct TALER_NormalizedPaytoHashP *account_id, const struct TALER_KYCLOGIC_AmlProgramResult *apr) { enum GNUNET_DB_QueryStatus qs; - size_t eas = 0; - void *ea = NULL; +#if 0 /* TODO: also clear lock on AML program (#9303) */ + qs = plugin->clear_aml_lock ( + plugin->cls, + account_id, + lock_id); +#endif switch (apr->status) { case TALER_KYCLOGIC_AMLR_FAILURE: - qs = plugin->insert_kyc_failure ( + qs = plugin->insert_aml_program_failure ( plugin->cls, process_row, account_id, - provider_name, - provider_user_id, - provider_legitimization_id, apr->details.failure.error_message, apr->details.failure.ec); GNUNET_break (qs > 0); return qs; case TALER_KYCLOGIC_AMLR_SUCCESS: - if (NULL != attributes) - { - TALER_CRYPTO_kyc_attributes_encrypt (attribute_key, - attributes, - &ea, - &eas); - } qs = plugin->insert_kyc_measure_result ( plugin->cls, process_row, account_id, - birthday, - GNUNET_TIME_timestamp_get (), - provider_name, - provider_user_id, - provider_legitimization_id, - expiration, + apr->details.success.expiration_time, apr->details.success.account_properties, apr->details.success.new_rules, apr->details.success.to_investigate, apr->details.success.num_events, - apr->details.success.events, - eas, - ea); - GNUNET_free (ea); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Stored encrypted KYC process #%llu attributes: %d\n", - (unsigned long long) process_row, - qs); + apr->details.success.events); GNUNET_break (qs > 0); return qs; } @@ -279,14 +254,7 @@ aml_result_callback ( /* Update database update based on result */ qs = TALER_EXCHANGEDB_persist_aml_program_result ( ru->plugin, - 0, // FIXME: process row - #9303 may give us something here!? - NULL /* no provider */, - NULL /* no user ID */, - NULL /* no legi ID */, - NULL /* no attributes */, - &ru->attribute_key, - 0 /* no birthday */, - GNUNET_TIME_UNIT_FOREVER_ABS, + 0, // FIXME: process row NEEDED! - #9303 may give us something here!? &ru->account, apr); switch (qs) @@ -398,7 +366,8 @@ run_measure (struct TALER_EXCHANGEDB_RuleUpdater *ru, m->prog_name); ru->amlh = TALER_KYCLOGIC_run_aml_program3 ( m, - NULL /* no attributes */, + &TALER_EXCHANGEDB_current_attributes_builder, + &hbc, &TALER_EXCHANGEDB_current_rule_builder, &hbc, &TALER_EXCHANGEDB_aml_history_builder, diff --git a/src/exchangedb/exchangedb_history.c b/src/exchangedb/exchangedb_history.c @@ -265,3 +265,87 @@ TALER_EXCHANGEDB_current_rule_builder (void *cls) } return jlrs; } + + +/** + * Closure for decrypt_attributes(). + */ +struct DecryptContext +{ + /** + * Overall context. + */ + const struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc; + + /** + * Where to return the attributes. + */ + json_t *attr; +}; + + +/** + * Decrypt and return AML attribute information. + * + * @param cls a `struct DecryptContext *` + * @param row_id current row in kyc_attributes table + * @param collection_time when were the attributes collected + * @param enc_attributes_size size of @a enc_attributes + * @param enc_attributes the encrypted collected attributes + */ +static void +decrypt_attributes ( + void *cls, + uint64_t row_id, + struct GNUNET_TIME_Timestamp collection_time, + size_t enc_attributes_size, + const void *enc_attributes) +{ + struct DecryptContext *decon = cls; + + (void) row_id; + (void) collection_time; + decon->attr + = TALER_CRYPTO_kyc_attributes_decrypt (decon->hbc->attribute_key, + enc_attributes, + enc_attributes_size); + GNUNET_break (NULL != decon->attr); +} + + +json_t * +TALER_EXCHANGEDB_current_attributes_builder (void *cls) +{ + struct TALER_EXCHANGEDB_HistoryBuilderContext *hbc = cls; + const struct TALER_NormalizedPaytoHashP *acc = hbc->account; + enum GNUNET_DB_QueryStatus qs; + struct DecryptContext decon = { + .hbc = hbc + }; + + qs = hbc->db_plugin->select_aml_attributes ( + hbc->db_plugin->cls, + acc, + INT64_MAX, + -1, /* we only fetch the latest ones */ + &decrypt_attributes, + &decon); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "select_aml_attributes returned %d\n", + (int) qs); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return NULL; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + decon.attr = json_object (); + GNUNET_break (NULL != decon.attr); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + GNUNET_break (NULL != decon.attr); + break; + } + return decon.attr; +} diff --git a/src/exchangedb/pg_insert_aml_program_failure.c b/src/exchangedb/pg_insert_aml_program_failure.c @@ -0,0 +1,76 @@ +/* + This file is part of TALER + Copyright (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file exchangedb/pg_insert_aml_program_failure.c + * @brief Implementation of the insert_aml_program_failure function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_insert_aml_program_failure.h" +#include "pg_helper.h" +#include "pg_event_notify.h" + + +enum GNUNET_DB_QueryStatus +TEH_PG_insert_aml_program_failure ( + void *cls, + uint64_t process_row, + const struct TALER_NormalizedPaytoHashP *h_payto, + const char *error_message, + enum TALER_ErrorCode ec) +{ + struct PostgresClosure *pg = cls; + uint32_t ec32 = (uint32_t) ec; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&process_row), + GNUNET_PQ_query_param_auto_from_type (h_payto), + GNUNET_PQ_query_param_uint32 (&ec32), + GNUNET_PQ_query_param_string (error_message), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE (pg, + "insert_aml_program_failure", + "UPDATE legitimization_processes" + " SET" + " finished=TRUE" + " ,error_code=$3" + " ,error_message=$4" + " WHERE h_payto=$2" + " AND legitimization_process_serial_id=$1;"); + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "insert_aml_program_failure", + params); + if (qs > 0) + { + /* FIXME: might want to do this eventually in the same transaction... */ + struct TALER_KycCompletedEventP rep = { + .header.size = htons (sizeof (rep)), + .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), + .h_payto = *h_payto + }; + + TEH_PG_event_notify (pg, + &rep.header, + NULL, + 0); + } + return qs; +} diff --git a/src/exchangedb/pg_insert_aml_program_failure.h b/src/exchangedb/pg_insert_aml_program_failure.h @@ -0,0 +1,48 @@ +/* + This file is part of TALER + Copyright (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file exchangedb/pg_insert_aml_program_failure.h + * @brief implementation of the insert_aml_program_failure function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_INSERT_AML_PROGRAM_FAILURE_H +#define PG_INSERT_AML_PROGRAM_FAILURE_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Update AML program status to finished (and failed). + * + * @param cls closure + * @param process_row KYC process row to update + * @param h_payto account for which the attribute data is stored + * @param error_message details about what went wrong + * @param ec error code about the failure + * @return database transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_insert_aml_program_failure ( + void *cls, + uint64_t process_row, + const struct TALER_NormalizedPaytoHashP *h_payto, + const char *error_message, + enum TALER_ErrorCode ec); + + +#endif diff --git a/src/exchangedb/pg_insert_kyc_measure_result.c b/src/exchangedb/pg_insert_kyc_measure_result.c @@ -31,46 +31,30 @@ TEH_PG_insert_kyc_measure_result ( void *cls, uint64_t process_row, const struct TALER_NormalizedPaytoHashP *h_payto, - uint32_t birthday, - struct GNUNET_TIME_Timestamp collection_time, - const char *provider_name, - const char *provider_account_id, - const char *provider_legitimization_id, - struct GNUNET_TIME_Absolute expiration_time, + struct GNUNET_TIME_Timestamp expiration_time, const json_t *account_properties, const json_t *new_rules, bool to_investigate, unsigned int num_events, - const char **events, - size_t enc_attributes_size, - const void *enc_attributes) + const char **events) { struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp expiration - = GNUNET_TIME_absolute_to_timestamp (expiration_time); struct TALER_KycCompletedEventP rep = { .header.size = htons (sizeof (rep)), .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), .h_payto = *h_payto }; + struct GNUNET_TIME_Timestamp now + = GNUNET_TIME_timestamp_get (); char *kyc_completed_notify_s = GNUNET_PQ_get_event_notify_channel (&rep.header); struct GNUNET_PQ_QueryParam params[] = { (0 == process_row) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_uint64 (&process_row), - GNUNET_PQ_query_param_auto_from_type (h_payto), - GNUNET_PQ_query_param_uint32 (&birthday), - GNUNET_PQ_query_param_string (provider_name), - (NULL == provider_account_id) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (provider_account_id), - (NULL == provider_legitimization_id) ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (provider_legitimization_id), - GNUNET_PQ_query_param_timestamp (&collection_time), - GNUNET_PQ_query_param_absolute_time (&expiration_time), - GNUNET_PQ_query_param_timestamp (&expiration), + : GNUNET_PQ_query_param_uint64 (&process_row), + GNUNET_PQ_query_param_auto_from_type (h_payto), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_timestamp (&expiration_time), NULL == account_properties ? GNUNET_PQ_query_param_null () : TALER_PQ_query_param_json (account_properties), @@ -80,10 +64,6 @@ TEH_PG_insert_kyc_measure_result ( GNUNET_PQ_query_param_array_ptrs_string (num_events, events, pg->conn), - (NULL == enc_attributes) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_fixed_size (enc_attributes, - enc_attributes_size), GNUNET_PQ_query_param_bool (to_investigate), GNUNET_PQ_query_param_string (kyc_completed_notify_s), GNUNET_PQ_query_param_end @@ -106,7 +86,7 @@ TEH_PG_insert_kyc_measure_result ( "SELECT " " out_ok" " FROM exchange_do_insert_kyc_measure_result " - "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);"); + "($1, $2, $3, $4, $5, $6, $7, $8, $9);"); qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "insert_kyc_measure_result", params, @@ -114,20 +94,6 @@ TEH_PG_insert_kyc_measure_result ( GNUNET_PQ_cleanup_query_params_closures (params); GNUNET_free (kyc_completed_notify_s); GNUNET_PQ_event_do_poll (pg->conn); - if (qs <= 0) - { - GNUNET_break (qs < 0); - GNUNET_break (0); - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - GNUNET_break (0); - qs = GNUNET_DB_STATUS_HARD_ERROR; - } - return qs; - } - if (! ok) - { - return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - } + GNUNET_break (ok); return qs; } diff --git a/src/exchangedb/pg_insert_kyc_measure_result.h b/src/exchangedb/pg_insert_kyc_measure_result.h @@ -27,25 +27,18 @@ /** - * Store KYC attribute data, update KYC process status and - * AML status for the given account. + * Update KYC process status and AML status for the given account based on AML + * program result. * * @param cls closure * @param process_row KYC process row to update * @param h_payto account for which the attribute data is stored - * @param birthday birthdate of user, in days after 1990, or 0 if unknown or definitively adult - * @param collection_time when was the data collected - * @param provider_name name of the provider that provided the attributes - * @param provider_account_id provider account ID - * @param provider_legitimization_id provider legitimization ID - * @param expiration_time when does the data expire + * @param expiration_time when do the @a new_rules expire * @param account_properties new account properties * @param new_rules new KYC rules to apply to the account * @param to_investigate true to flag account for investigation * @param num_events length of the @a events array * @param events array of KYC events to trigger - * @param enc_attributes_size number of bytes in @a enc_attributes - * @param enc_attributes encrypted attribute data * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -53,19 +46,12 @@ TEH_PG_insert_kyc_measure_result ( void *cls, uint64_t process_row, const struct TALER_NormalizedPaytoHashP *h_payto, - uint32_t birthday, - struct GNUNET_TIME_Timestamp collection_time, - const char *provider_name, - const char *provider_account_id, - const char *provider_legitimization_id, - struct GNUNET_TIME_Absolute expiration_time, + struct GNUNET_TIME_Timestamp expiration_time, const json_t *account_properties, const json_t *new_rules, bool to_investigate, unsigned int num_events, - const char **events, - size_t enc_attributes_size, - const void *enc_attributes); + const char **events); #endif diff --git a/src/exchangedb/pg_lookup_active_legitimization.c b/src/exchangedb/pg_lookup_active_legitimization.c @@ -31,7 +31,6 @@ TEH_PG_lookup_active_legitimization ( void *cls, uint64_t legitimization_process_serial_id, uint32_t *measure_index, - char **provider_name, json_t **jmeasures) { struct PostgresClosure *pg = cls; @@ -46,9 +45,6 @@ TEH_PG_lookup_active_legitimization ( GNUNET_PQ_result_spec_uint32 ( "measure_index", measure_index), - GNUNET_PQ_result_spec_string ( - "provider_name", - provider_name), GNUNET_PQ_result_spec_end }; @@ -57,7 +53,6 @@ TEH_PG_lookup_active_legitimization ( "SELECT " " lm.jmeasures" ",lp.measure_index" - ",lp.provider_name" " FROM legitimization_processes lp" " JOIN legitimization_measures lm" " USING (legitimization_measure_serial_id)" diff --git a/src/exchangedb/pg_lookup_active_legitimization.h b/src/exchangedb/pg_lookup_active_legitimization.h @@ -34,7 +34,6 @@ * row in legitimization_processes table to access * @param[out] measure_index set to the measure the * process is trying to satisfy - * @param[out] provider_name name of the provider, must be freed by caller * @param[out] jmeasures set to the legitimization * measures that were put on the account * @return database transaction status @@ -44,7 +43,6 @@ TEH_PG_lookup_active_legitimization ( void *cls, uint64_t legitimization_process_serial_id, uint32_t *measure_index, - char **provider_name, json_t **jmeasures); diff --git a/src/exchangedb/pg_persist_kyc_attributes.c b/src/exchangedb/pg_persist_kyc_attributes.c @@ -0,0 +1,103 @@ +/* + This file is part of TALER + Copyright (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file exchangedb/pg_persist_kyc_attributes.c + * @brief Implementation of the persist_kyc_attributes function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_persist_kyc_attributes.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +TEH_PG_persist_kyc_attributes ( + void *cls, + uint64_t process_row, + const struct TALER_NormalizedPaytoHashP *h_payto, + const char *provider_name, + const char *provider_account_id, + const char *provider_legitimization_id, + uint32_t birthday, + struct GNUNET_TIME_Absolute expiration_time, + size_t enc_attributes_size, + const void *enc_attributes) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_TIME_Timestamp collection_time + = GNUNET_TIME_timestamp_get (); + struct GNUNET_TIME_Timestamp expiration + = GNUNET_TIME_absolute_to_timestamp (expiration_time); + struct TALER_KycCompletedEventP rep = { + .header.size = htons (sizeof (rep)), + .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), + .h_payto = *h_payto + }; + char *kyc_completed_notify_s + = GNUNET_PQ_get_event_notify_channel (&rep.header); + struct GNUNET_PQ_QueryParam params[] = { + (0 == process_row) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_uint64 (&process_row), + GNUNET_PQ_query_param_auto_from_type (h_payto), + GNUNET_PQ_query_param_uint32 (&birthday), + GNUNET_PQ_query_param_string (provider_name), + (NULL == provider_account_id) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string (provider_account_id), + (NULL == provider_legitimization_id) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string (provider_legitimization_id), + GNUNET_PQ_query_param_timestamp (&collection_time), + GNUNET_PQ_query_param_absolute_time (&expiration_time), + GNUNET_PQ_query_param_timestamp (&expiration), + (NULL == enc_attributes) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_fixed_size (enc_attributes, + enc_attributes_size), + GNUNET_PQ_query_param_string (kyc_completed_notify_s), + GNUNET_PQ_query_param_end + }; + bool ok; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("out_ok", + &ok), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Inserting KYC attributes, wake up on %s\n", + kyc_completed_notify_s); + GNUNET_break (NULL != h_payto); + PREPARE (pg, + "persist_kyc_attributes", + "SELECT " + " out_ok" + " FROM exchange_do_persist_kyc_attributes " + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11);"); + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "persist_kyc_attributes", + params, + rs); + GNUNET_PQ_cleanup_query_params_closures (params); + GNUNET_free (kyc_completed_notify_s); + GNUNET_PQ_event_do_poll (pg->conn); + return qs; +} diff --git a/src/exchangedb/pg_persist_kyc_attributes.h b/src/exchangedb/pg_persist_kyc_attributes.h @@ -0,0 +1,58 @@ +/* + This file is part of TALER + Copyright (C) 2024 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file exchangedb/pg_persist_kyc_attributes.h + * @brief implementation of the persist_kyc_attributes function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_PERSIST_KYC_ATTRIBUTES_H +#define PG_PERSIST_KYC_ATTRIBUTES_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Store KYC attribute data. + * + * @param cls closure + * @param process_row KYC process row to update + * @param h_payto account for which the attribute data is stored + * @param provider_name name of the provider that provided the attributes + * @param provider_account_id provider account ID + * @param provider_legitimization_id provider legitimization ID + * @param birthday birthdate of user, in days after 1990, or 0 if unknown or definitively adult + * @param expiration_time when does the data expire + * @param enc_attributes_size number of bytes in @a enc_attributes + * @param enc_attributes encrypted attribute data + * @return database transaction status + */ +enum GNUNET_DB_QueryStatus +TEH_PG_persist_kyc_attributes ( + void *cls, + uint64_t process_row, + const struct TALER_NormalizedPaytoHashP *h_payto, + const char *provider_name, + const char *provider_account_id, + const char *provider_legitimization_id, + uint32_t birthday, + struct GNUNET_TIME_Absolute expiration_time, + size_t enc_attributes_size, + const void *enc_attributes); + + +#endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c @@ -103,6 +103,7 @@ #include "pg_insert_active_legitimization_measure.h" #include "pg_insert_aml_decision.h" #include "pg_insert_aml_officer.h" +#include "pg_insert_aml_program_failure.h" #include "pg_insert_auditor.h" #include "pg_insert_auditor_denom_sig.h" #include "pg_insert_close_request.h" @@ -157,6 +158,7 @@ #include "pg_lookup_wire_fee_by_time.h" #include "pg_lookup_wire_timestamp.h" #include "pg_lookup_wire_transfer.h" +#include "pg_persist_kyc_attributes.h" #include "pg_persist_policy_details.h" #include "pg_preflight.h" #include "pg_profit_drains_get_pending.h" @@ -765,6 +767,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_inject_auditor_triggers; plugin->insert_successor_measure = &TEH_PG_insert_successor_measure; + plugin->insert_aml_program_failure + = &TEH_PG_insert_aml_program_failure; + plugin->persist_kyc_attributes + = &TEH_PG_persist_kyc_attributes; return plugin; } diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in @@ -56,5 +56,6 @@ SET search_path TO exchange; #include "exchange_do_lookup_kyc_requirement_by_row.sql" #include "exchange_do_insert_active_legitimization_measure.sql" #include "exchange_do_select_aggregations_above_serial.sql" +#include "exchange_do_persist_kyc_attributes.sql" COMMIT; diff --git a/src/include/taler_exchangedb_lib.h b/src/include/taler_exchangedb_lib.h @@ -260,6 +260,17 @@ TALER_EXCHANGEDB_current_rule_builder (void *cls); /** + * Function called to obtain the latest KYC attributes + * in JSON for an account on-demand if needed. + * + * @param cls must be a `struct TALER_EXCHANGEDB_HistoryBuilderContext *` + * @return KYC attributes in JSON format, NULL on error + */ +json_t * +TALER_EXCHANGEDB_current_attributes_builder (void *cls); + + +/** * Handle for helper logic that advances rules to the currently * valid rule set. */ @@ -355,13 +366,6 @@ TALER_EXCHANGEDB_update_rules_cancel ( * * @param plugin database API handle * @param process_row row identifying the AML process that was run - * @param provider_name name of the provider that provided the attributes - * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown - * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown - * @param attributes set of KYC attributes, possibly NULL - * @param attribute_key key to use to encrypt the KYC attributes - * @param birthday birthday of the user, 0 for unknown - * @param expiration until when is the KYC check valid * @param account_id hash of account the result is about * @param apr AML program result to persist */ @@ -369,13 +373,6 @@ enum GNUNET_DB_QueryStatus TALER_EXCHANGEDB_persist_aml_program_result ( struct TALER_EXCHANGEDB_Plugin *plugin, uint64_t process_row, - const char *provider_name, - const char *provider_user_id, - const char *provider_legitimization_id, - const json_t *attributes, - const struct TALER_AttributeEncryptionKeyP *attribute_key, - unsigned int birthday, - struct GNUNET_TIME_Absolute expiration, const struct TALER_NormalizedPaytoHashP *account_id, const struct TALER_KYCLOGIC_AmlProgramResult *apr); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -7297,25 +7297,19 @@ struct TALER_EXCHANGEDB_Plugin /** - * Store KYC attribute data, update KYC process status and - * AML status for the given account. + * Update KYC process status and + * AML status for the given account based on + * AML program outcome. * * @param cls closure * @param process_row KYC process row to update * @param h_payto account for which the attribute data is stored - * @param birthday birthdate of user, in days after 1990, or 0 if unknown or definitively adult - * @param collection_time when was the data collected - * @param provider_name name of the provider that collected the data - * @param provider_account_id provider account ID - * @param provider_legitimization_id provider legitimization ID - * @param expiration_time when does the data expire + * @param expiration_time when do the @a new_rules expire * @param account_properties new account properties * @param new_rules new KYC rules to apply to the account * @param to_investigate true to flag account for investigation * @param num_events length of the @a events array * @param events array of KYC events to trigger - * @param enc_attributes_size number of bytes in @a enc_attributes - * @param enc_attributes encrypted attribute data * @return database transaction status */ enum GNUNET_DB_QueryStatus @@ -7323,19 +7317,31 @@ struct TALER_EXCHANGEDB_Plugin void *cls, uint64_t process_row, const struct TALER_NormalizedPaytoHashP *h_payto, - uint32_t birthday, - struct GNUNET_TIME_Timestamp collection_time, - const char *provider_name, - const char *provider_account_id, - const char *provider_legitimization_id, - struct GNUNET_TIME_Absolute expiration_time, + struct GNUNET_TIME_Timestamp expiration_time, const json_t *account_properties, const json_t *new_rules, bool to_investigate, unsigned int num_events, - const char **events, - size_t enc_attributes_size, - const void *enc_attributes); + const char **events); + + + /** + * Update AML program status to finished (and failed). + * + * @param cls closure + * @param process_row KYC process row to update + * @param h_payto account for which the attribute data is stored + * @param error_message details about what went wrong + * @param ec error code about the failure + * @return database transaction status + */ + enum GNUNET_DB_QueryStatus + (*insert_aml_program_failure) ( + void *cls, + uint64_t process_row, + const struct TALER_NormalizedPaytoHashP *h_payto, + const char *error_message, + enum TALER_ErrorCode ec); /** @@ -7568,28 +7574,28 @@ struct TALER_EXCHANGEDB_Plugin /** - * Lookup measure data for a legitimization process. - * - * @param cls closure - * @param legitimization_measure_serial_id - * row in legitimization_measures table to access - * @param measure_index index of the measure to return - * attribute data for - * @param[out] access_token - * set to token for access control that must match - * @param[out] h_payto set to the the hash of the - * payto URI of the account undergoing legitimization - * @param[out] jmeasures set to the legitimization - * measures that were put on the account - * @param[out] is_finished set to true if the legitimization was - * already finished - * @param[out] encrypted_attributes_len set to length of - * @a encrypted_attributes - * @param[out] encrypted_attributes set to the attributes - * obtained for the legitimization process, if it - * succeeded, otherwise set to NULL - * @return database transaction status - */ + * Lookup measure data for a legitimization process. + * + * @param cls closure + * @param legitimization_measure_serial_id + * row in legitimization_measures table to access + * @param measure_index index of the measure to return + * attribute data for + * @param[out] access_token + * set to token for access control that must match + * @param[out] h_payto set to the the hash of the + * payto URI of the account undergoing legitimization + * @param[out] jmeasures set to the legitimization + * measures that were put on the account + * @param[out] is_finished set to true if the legitimization was + * already finished + * @param[out] encrypted_attributes_len set to length of + * @a encrypted_attributes + * @param[out] encrypted_attributes set to the attributes + * obtained for the legitimization process, if it + * succeeded, otherwise set to NULL + * @return database transaction status + */ enum GNUNET_DB_QueryStatus (*lookup_completed_legitimization)( void *cls, @@ -7647,7 +7653,6 @@ struct TALER_EXCHANGEDB_Plugin * row in legitimization_processes table to access * @param[out] measure_index set to the measure the * process is trying to satisfy - * @param[out] provider_name name of the provider, must be freed by caller * @param[out] jmeasures set to the legitimization * measures that were put on the account * @return database transaction status @@ -7657,7 +7662,6 @@ struct TALER_EXCHANGEDB_Plugin void *cls, uint64_t legitimization_process_serial_id, uint32_t *measure_index, - char **provider_name, json_t **jmeasures); @@ -7730,6 +7734,35 @@ struct TALER_EXCHANGEDB_Plugin /** + * Store KYC attribute data. + * + * @param cls closure + * @param process_row KYC process row to update + * @param h_payto account for which the attribute data is stored + * @param provider_name name of the provider that provided the attributes + * @param provider_account_id provider account ID + * @param provider_legitimization_id provider legitimization ID + * @param birthday birthdate of user, in days after 1990, or 0 if unknown or definitively adult + * @param expiration_time when does the data expire + * @param enc_attributes_size number of bytes in @a enc_attributes + * @param enc_attributes encrypted attribute data + * @return database transaction status + */ + enum GNUNET_DB_QueryStatus + (*persist_kyc_attributes) ( + void *cls, + uint64_t process_row, + const struct TALER_NormalizedPaytoHashP *h_payto, + const char *provider_name, + const char *provider_account_id, + const char *provider_legitimization_id, + uint32_t birthday, + struct GNUNET_TIME_Absolute expiration_time, + size_t enc_attributes_size, + const void *enc_attributes); + + + /** * Update KYC process status to finished (and failed). * * @param cls closure diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h @@ -908,6 +908,11 @@ struct TALER_KYCLOGIC_AmlProgramResult const json_t *new_rules; /** + * When do the new rules expire. + */ + struct GNUNET_TIME_Timestamp expiration_time; + + /** * Length of the @e events array. */ unsigned int num_events; @@ -978,7 +983,8 @@ typedef json_t * * provide the context * @param measure_index which KYC measure yielded the * @a attributes - * @param attributes KYC attributes newly obtained + * @param current_attributes_cb function to get current KYC attributes + * @param current_attributes_cb_cls closure for @a current_attributes_cb * @param current_rules_cb callback to get current KYC rules that apply to the account * @param current_rules_cb_cls closure for @a current_rules_cb * @param aml_history_cb callback to get the AML history of the account @@ -994,7 +1000,8 @@ struct TALER_KYCLOGIC_AmlProgramRunnerHandle * TALER_KYCLOGIC_run_aml_program ( const json_t *jmeasures, unsigned int measure_index, - const json_t *attributes, + TALER_KYCLOGIC_HistoryBuilderCallback current_attributes_cb, + void *current_attributes_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback current_rules_cb, void *current_rules_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback aml_history_cb, @@ -1009,8 +1016,9 @@ TALER_KYCLOGIC_run_aml_program ( * Run AML program @a prog_name with the given @a context. * * @param prog_name name of AML program to run - * @param attributes attributes to run with * @param context context to run with + * @param current_attributes_cb function to get current KYC attributes + * @param current_attributes_cb_cls closure for @a current_attributes_cb * @param current_rules_cb callback to get current KYC rules that apply to the account * @param current_rules_cb_cls closure for @a current_rules_cb * @param aml_history_cb callback to get the AML history of the account @@ -1025,8 +1033,9 @@ TALER_KYCLOGIC_run_aml_program ( struct TALER_KYCLOGIC_AmlProgramRunnerHandle * TALER_KYCLOGIC_run_aml_program2 ( const char *prog_name, - const json_t *attributes, const json_t *context, + TALER_KYCLOGIC_HistoryBuilderCallback current_attributes_cb, + void *current_attributes_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback current_rules_cb, void *current_rules_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback aml_history_cb, @@ -1043,7 +1052,8 @@ TALER_KYCLOGIC_run_aml_program2 ( * * @param measure measure with program name and context * to run - * @param attributes attributes to run with + * @param current_attributes_cb function to get current KYC attributes + * @param current_attributes_cb_cls closure for @a current_attributes_cb * @param current_rules_cb callback to get current KYC rules that apply to the account * @param current_rules_cb_cls closure for @a current_rules_cb * @param aml_history_cb callback to get the AML history of the account @@ -1058,7 +1068,8 @@ TALER_KYCLOGIC_run_aml_program2 ( struct TALER_KYCLOGIC_AmlProgramRunnerHandle * TALER_KYCLOGIC_run_aml_program3 ( const struct TALER_KYCLOGIC_Measure *measure, - const json_t *attributes, + TALER_KYCLOGIC_HistoryBuilderCallback current_attributes_cb, + void *current_attributes_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback current_rules_cb, void *current_rules_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback aml_history_cb, diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c @@ -3941,6 +3941,8 @@ handle_aml_output ( = TALER_EC_EXCHANGE_KYC_AML_PROGRAM_MALFORMED_RESULT; goto ready; } + apr->details.success.expiration_time + = lrs->expiration_time; TALER_KYCLOGIC_rules_free (lrs); } } @@ -3974,7 +3976,8 @@ struct TALER_KYCLOGIC_AmlProgramRunnerHandle * TALER_KYCLOGIC_run_aml_program ( const json_t *jmeasures, unsigned int measure_index, - const json_t *attributes, + TALER_KYCLOGIC_HistoryBuilderCallback current_attributes_cb, + void *current_attributes_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback current_rules_cb, void *current_rules_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback aml_history_cb, @@ -4003,8 +4006,9 @@ TALER_KYCLOGIC_run_aml_program ( } } return TALER_KYCLOGIC_run_aml_program2 (prog_name, - attributes, context, + current_attributes_cb, + current_attributes_cb_cls, current_rules_cb, current_rules_cb_cls, aml_history_cb, @@ -4019,8 +4023,9 @@ TALER_KYCLOGIC_run_aml_program ( struct TALER_KYCLOGIC_AmlProgramRunnerHandle * TALER_KYCLOGIC_run_aml_program2 ( const char *prog_name, - const json_t *attributes, const json_t *context, + TALER_KYCLOGIC_HistoryBuilderCallback current_attributes_cb, + void *current_attributes_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback current_rules_cb, void *current_rules_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback aml_history_cb, @@ -4036,6 +4041,7 @@ TALER_KYCLOGIC_run_aml_program2 ( json_t *current_rules; json_t *aml_history; json_t *kyc_history; + json_t *attributes; prog = find_program (prog_name); if (NULL == prog) @@ -4049,6 +4055,15 @@ TALER_KYCLOGIC_run_aml_program2 ( aprh->program = prog; if (0 != (API_ATTRIBUTES & prog->input_mask)) { + attributes = current_attributes_cb (current_attributes_cb_cls); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "KYC attributes for AML program %s are:\n", + prog_name); + json_dumpf (attributes, + stderr, + JSON_INDENT (2)); + fprintf (stderr, + "\n"); for (unsigned int i = 0; i<prog->num_required_attributes; i++) { const char *rattr = prog->required_attributes[i]; @@ -4186,7 +4201,8 @@ TALER_KYCLOGIC_run_aml_program2 ( struct TALER_KYCLOGIC_AmlProgramRunnerHandle * TALER_KYCLOGIC_run_aml_program3 ( const struct TALER_KYCLOGIC_Measure *measure, - const json_t *attributes, + TALER_KYCLOGIC_HistoryBuilderCallback current_attributes_cb, + void *current_attributes_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback current_rules_cb, void *current_rules_cb_cls, TALER_KYCLOGIC_HistoryBuilderCallback aml_history_cb, @@ -4198,8 +4214,9 @@ TALER_KYCLOGIC_run_aml_program3 ( { return TALER_KYCLOGIC_run_aml_program2 ( measure->prog_name, - attributes, measure->context, + current_attributes_cb, + current_attributes_cb_cls, current_rules_cb, current_rules_cb_cls, aml_history_cb, diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c @@ -1047,7 +1047,13 @@ converted_proof_cb (void *cls, ph); return; } - + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Attribute conversion output is:\n"); + json_dumpf (attr, + stderr, + JSON_INDENT (2)); + fprintf (stderr, + "\n"); { const char *id; struct GNUNET_JSON_Specification ispec[] = { diff --git a/src/kyclogic/taler-exchange-helper-converter-oauth2-test-full_name b/src/kyclogic/taler-exchange-helper-converter-oauth2-test-full_name @@ -28,16 +28,54 @@ set -eu echo "Running $0" 1>&2 -# FIXME: use getopt properly here, also support -# -h, -v, -V, ... - -if [ "${1:-no}" = "-o" ] -then - # This converter produces a full name and birthdate. - echo "full_name" - echo "birthdate" - exit 0 -fi +# Exit, with error message (hard failure) +function exit_fail() { + echo " FAIL: " "$@" >&2 + EXIT_STATUS=1 + exit "$EXIT_STATUS" +} + +CONF="$HOME/.config/taler-exchange.conf" +VERBOSE=0 + +while getopts 'c:hovV' OPTION; +do + case "$OPTION" in + c) + # shellcheck disable=SC2034 + CONF="$OPTARG" + ;; + h) + echo "This is a KYC measure program that freezes the account and flags it for manual investigation. This is the ultimate fallback measure." + echo 'Supported options:' + echo ' -a -- show required attributes' + # shellcheck disable=SC2016 + echo ' -c $CONF -- set configuration' + echo ' -h -- print this help' + echo ' -i -- show required inputs' + echo ' -r -- show required context' + echo ' -v -- show version' + echo ' -V -- be verbose' + exit 0 + ;; + o) + # This converter produces a full name and birthdate. + echo "full_name" + echo "birthdate" + exit 0 + ;; + v) + echo "$0 v0.0.0" + exit 0 + ;; + V) + VERBOSE=1 + ;; + ?) + exit_fail "Unrecognized command line option" + ;; + esac +done # First, check everything we expect is in stdin. J=$(jq -r 'def get($k):