exchange

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

commit b45be8fd69052a93dbe92a6e6de8e537d8c82e8b
parent 92f069d89d5421da00412a62920471bf26ef777a
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun,  7 Jul 2024 15:03:00 +0200

WIP: kyc-start

Diffstat:
Msrc/exchange/taler-exchange-httpd_kyc-start.c | 597+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/exchange/taler-exchange-httpd_kyc-upload.c | 10+++++-----
Msrc/include/taler_exchange_service.h | 3---
Msrc/include/taler_kyclogic_lib.h | 21+++++++++++++++++++++
Msrc/kyclogic/kyclogic_api.c | 13+++++++++++++
5 files changed, 258 insertions(+), 386 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_kyc-start.c b/src/exchange/taler-exchange-httpd_kyc-start.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021-2023 Taler Systems SA + Copyright (C) 2021-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -35,10 +35,21 @@ /** - * Reserve GET request that is long-polling. + * POST request in asynchronous processing. */ struct KycPoller { + + /** + * Access token for the KYC data of the account. + */ + struct TALER_AccountAccessTokenP access_token; + + /** + * Hash of the payto:// URI we are starting to the KYC for. + */ + struct TALER_PaytoHashP h_payto; + /** * Kept in a DLL. */ @@ -66,55 +77,39 @@ struct KycPoller struct TALER_KYCLOGIC_InitiateHandle *ih; /** - * Subscription for the database event we are - * waiting for. - */ - struct GNUNET_DB_EventHandler *eh; - - /** - * Row of the requirement being started. - */ - uint64_t requirement_row; - - /** - * Row of KYC process being initiated. + * Set of applicable KYC measures. */ - uint64_t process_row; + json_t *jmeasures; /** - * Hash of the payto:// URI we are confirming to - * have finished the KYC for. + * Where to redirect the user to start the KYC process. */ - struct TALER_PaytoHashP h_payto; + char *redirect_url; /** - * When will this request time out? + * Set to the name of the KYC provider. */ - struct GNUNET_TIME_Absolute timeout; + char *provider_name; /** - * Public limits that apply to the account, - * if @e have_token is true. - * NULL if no limits are public. + * Set to error details, on error (@ec not TALER_EC_NONE). */ - json_t *limits; + char *hint; /** - * Set to access token for a KYC process by the account, - * if @e have_token is true. + * Row of the requirement being started. */ - struct TALER_AccountAccessTokenP access_token; + unsigned long long legitimization_measure_serial_id; /** - * Signature by the account owner authorizing this - * operation. + * Row of KYC process being initiated. */ - union TALER_AccountSignatureP account_sig; + uint64_t process_row; /** - * Set to error details, on error (@ec not TALER_EC_NONE). + * Index of the measure this upload is for. */ - char *hint; + unsigned int measure_index; /** * Set to error encountered with KYC logic, if any. @@ -122,41 +117,20 @@ struct KycPoller enum TALER_ErrorCode ec; /** - * True if account is under AML review and this is - * exposed. - */ - bool aml_review; - - /** - * True if @e access_token was initialized. - */ - bool have_token; - - /** * True if we are still suspended. */ bool suspended; - /** - * False if KYC is not required. - */ - bool kyc_required; - - /** - * True if we once tried the KYC initiation. - */ - bool ih_done; - }; /** - * Head of list of requests in long polling. + * Head of list of requests in asynchronous processing. */ static struct KycPoller *kyp_head; /** - * Tail of list of requests in long polling. + * Tail of list of requests in asynchronous processing. */ static struct KycPoller *kyp_tail; @@ -197,27 +171,19 @@ kyp_cleanup (struct TEH_RequestContext *rc) struct KycPoller *kyp = rc->rh_ctx; GNUNET_assert (! kyp->suspended); - if (NULL != kyp->eh) - { - GNUNET_log (GNUNET_ERROR_TYPE_START, - "Cancelling DB event listening\n"); - TEH_plugin->event_listen_cancel (TEH_plugin->cls, - kyp->eh); - kyp->eh = NULL; - } if (NULL != kyp->ih) { kyp->ih_logic->initiate_cancel (kyp->ih); kyp->ih = NULL; } - json_decref (kyp->kyc_details); - GNUNET_free (kyp->kyc_url); + GNUNET_free (kyp->redirect_url); + GNUNET_free (kyp->provider_name); GNUNET_free (kyp->hint); + json_decref (kyp->jmeasures); GNUNET_free (kyp); } -#if FIXME /** * Function called with the result of a KYC initiation * operation. @@ -242,7 +208,6 @@ initiate_cb ( enum GNUNET_DB_QueryStatus qs; kyp->ih = NULL; - kyp->ih_done = true; GNUNET_log (GNUNET_ERROR_TYPE_START, "KYC initiation `%s' completed with ec=%d (%s)\n", provider_legitimization_id, @@ -253,16 +218,17 @@ initiate_cb ( kyp->ec = ec; if (TALER_EC_NONE == ec) { - kyp->kyc_url = GNUNET_strdup (redirect_url); + kyp->redirect_url = GNUNET_strdup (redirect_url); } else { kyp->hint = GNUNET_strdup (error_msg_hint); } + // FIXME: also store errors! qs = TEH_plugin->update_kyc_process_by_row ( TEH_plugin->cls, kyp->process_row, - kyp->section_name, + kyp->provider_name, &kyp->h_payto, provider_user_id, provider_legitimization_id, @@ -285,221 +251,6 @@ initiate_cb ( } -#endif - - -/** - * Function implementing database transaction to start wallet's KYC status. - * Runs the transaction logic; IF it returns a non-error code, the transaction - * logic MUST NOT queue a MHD response. IF it returns an hard error, the - * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it - * returns the soft error code, the function MAY be called again to retry and - * MUST not queue a MHD response. - * - * @param cls closure with a `struct KycPoller *` - * @param connection MHD request which triggered the transaction - * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -kyc_start (void *cls, - struct MHD_Connection *connection, - MHD_RESULT *mhd_ret) -{ -#if FIXME - struct KycPoller *kyp = cls; - enum GNUNET_DB_QueryStatus qs; - struct TALER_KYCLOGIC_ProviderDetails *pd; - enum GNUNET_GenericReturnValue ret; - struct TALER_PaytoHashP h_payto; - char *requirements; - char *redirect_url; - bool satisfied; - - qs = TEH_plugin->lookup_kyc_requirement_by_row ( - TEH_plugin->cls, - kyp->requirement_row, - &requirements, - &h_payto); - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_START, - "No KYC requirements open for %llu\n", - (unsigned long long) kyp->requirement_row); - return qs; - } - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); - return qs; - } - if (0 != - GNUNET_memcmp (&kyp->h_payto, - &h_payto)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Requirement %llu provided, but h_payto does not match\n", - (unsigned long long) kyp->requirement_row); - GNUNET_break_op (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_KYC_START_AUTHORIZATION_FAILED, - "h_payto"); - GNUNET_free (requirements); - return GNUNET_DB_STATUS_HARD_ERROR; - } - qs = TALER_KYCLOGIC_start_satisfied ( - &requirements, - &h_payto, - &kyp->kyc_details, - TEH_plugin->select_satisfied_kyc_processes, - TEH_plugin->cls, - &satisfied); - if (qs < 0) - { - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "kyc_test_required"); - GNUNET_free (requirements); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (satisfied) - { - GNUNET_log (GNUNET_ERROR_TYPE_START, - "KYC requirements `%s' already satisfied\n", - requirements); - GNUNET_free (requirements); - return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - } - - kyp->kyc_required = true; - ret = TALER_KYCLOGIC_requirements_to_logic (requirements, - &kyp->ih_logic, - &pd, - &kyp->section_name); - if (GNUNET_OK != ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "KYC requirements `%s' cannot be started, but are set as required in database!\n", - requirements); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_GONE, - requirements); - GNUNET_free (requirements); - return GNUNET_DB_STATUS_HARD_ERROR; - } - GNUNET_free (requirements); - - if (kyp->ih_done) - return qs; - qs = TEH_plugin->get_pending_kyc_requirement_process ( - TEH_plugin->cls, - &h_payto, - kyp->section_name, - &redirect_url); - if (qs < 0) - { - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_requirement_process"); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if ( (qs > 0) && - (NULL != redirect_url) ) - { - kyp->kyc_url = redirect_url; - return qs; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - /* set up new requirement process */ - qs = TEH_plugin->insert_kyc_requirement_process ( - TEH_plugin->cls, - &h_payto, - kyp->section_name, - NULL, - NULL, - &kyp->process_row); - if (qs < 0) - { - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_requirement_process"); - return GNUNET_DB_STATUS_HARD_ERROR; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_START, - "Initiating KYC start with logic %s\n", - kyp->ih_logic->name); - kyp->ih = kyp->ih_logic->initiate (kyp->ih_logic->cls, - pd, - &h_payto, - kyp->process_row, - &initiate_cb, - kyp); - GNUNET_break (NULL != kyp->ih); - return qs; -#else - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; -#endif -} - - -/** - * Function called on events received from Postgres. - * Wakes up long pollers. - * - * @param cls the `struct TEH_RequestContext *` - * @param extra additional event data provided - * @param extra_size number of bytes in @a extra - */ -static void -db_event_cb (void *cls, - const void *extra, - size_t extra_size) -{ - struct TEH_RequestContext *rc = cls; - struct KycPoller *kyp = rc->rh_ctx; - struct GNUNET_AsyncScopeSave old_scope; - - (void) extra; - (void) extra_size; - if (! kyp->suspended) - return; /* event triggered while main transaction - was still running, or got multiple wake-up events */ - GNUNET_log (GNUNET_ERROR_TYPE_START, - "Received KYC update event\n"); - kyp->suspended = false; - GNUNET_async_scope_enter (&rc->async_scope_id, - &old_scope); - TEH_start_invariants (); - GNUNET_log (GNUNET_ERROR_TYPE_START, - "Resuming from long-polling on KYC status\n"); - GNUNET_CONTAINER_DLL_remove (kyp_head, - kyp_tail, - kyp); - MHD_resume_connection (kyp->connection); - TALER_MHD_daemon_trigger (); - TEH_start_invariants (); - GNUNET_async_scope_restore (&old_scope); -} - - MHD_RESULT TEH_handler_kyc_start ( struct TEH_RequestContext *rc, @@ -511,6 +262,9 @@ TEH_handler_kyc_start ( if (NULL == kyp) { + enum GNUNET_DB_QueryStatus qs; + const struct TALER_KYCLOGIC_KycProvider *provider; + kyp = GNUNET_new (struct KycPoller); kyp->connection = rc->connection; rc->rh_ctx = kyp; @@ -519,88 +273,185 @@ TEH_handler_kyc_start ( { unsigned long long requirement_row; char dummy; + const char *slash; - if (1 != - sscanf (args[0], - "%llu%c", - &requirement_row, + slash = strchr (id, '-'); + if (NULL == slash) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + rc->url); + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (id, + slash - id, + &kyp->access_token, + sizeof (kyp->access_token))) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "Access token in ID is malformed"); + } + if (2 != + sscanf (slash + 1, + "%u-%llu%c", + &kyp->measure_index, + &kyp->legitimization_measure_serial_id, &dummy)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "requirement_row"); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "ID is malformed"); } - kyp->requirement_row = (uint64_t) requirement_row; } - TALER_MHD_parse_request_timeout (rc->connection, - &kyp->timeout); - } - if ( (NULL == kyp->eh) && - GNUNET_TIME_absolute_is_future (kyp->timeout) ) - { - struct TALER_KycCompletedEventP rep = { - .header.size = htons (sizeof (rep)), - .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), - .h_payto = kyp->h_payto - }; - - GNUNET_log (GNUNET_ERROR_TYPE_START, - "Starting DB event listening\n"); - kyp->eh = TEH_plugin->event_listen ( + // FIXME: bad check, cannot return jmeasures, + // we need a redirect_url AND an EC to see if + // we should restart 'initiate' anyway! + qs = TEH_plugin->lookup_pending_legitimization ( TEH_plugin->cls, - GNUNET_TIME_absolute_get_remaining (kyp->timeout), - &rep.header, - &db_event_cb, - rc); - } + kyp->legitimization_measure_serial_id, + &kyp->access_token, + &kyp->h_payto, + &kyp->jmeasures); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs); + return TALER_MHD_reply_with_ec ( + rc->connection, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_pending_legitimization"); + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + rc->url); + } - ret = TEH_DB_run_transaction (rc->connection, - "kyc start", - TEH_MT_REQUEST_OTHER, - &res, - &kyc_start, - kyp); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_START, - "Transaction failed.\n"); - return res; - } + { + const char *check_name; + const char *prog_name; + const json_t *context; + json_t *req; + + kyp->ec = TALER_KYCLOGIC_select_measure ( + kyp->jmeasures, + kyp->measure_index, + &check_name, + &prog_name); + if (TALER_EC_NONE != kyp->ec) + { + /* return EC in next call to this function */ + GNUNET_break_op (0); + kyp->hint + = GNUNET_strdup ("TALER_KYCLOGIC_select_measure"); + return MHD_YES; + } - if (TALER_EC_NONE != kyp->ec) - { - return TALER_MHD_reply_with_ec (rc->connection, - kyp->ec, - kyp->hint); - } + provider = TALER_KYCLOGIC_check_to_provider ( + check_name); + if (NULL == provider) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_CONFLICT, + 42, // FIXME: TALER_EC_EXCHANGE_BAD_CHECK_FOR_KYC_START, + check_name); + } + } - if (NULL != kyp->ih) - { - GNUNET_log (GNUNET_ERROR_TYPE_START, - "Suspending HTTP request on KYC logic...\n"); - kyp->suspended = true; - GNUNET_CONTAINER_DLL_insert (kyp_head, - kyp_tail, - kyp); - MHD_suspend_connection (kyp->connection); - return MHD_YES; - } + ret = TALER_KYCLOGIC_provider_to_logic ( + provider, + &kyp->ih_logic, + &pd, + &kyp->provider_name); + if (GNUNET_OK != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "KYC requirements `%s' cannot be started, but are set as required in database!\n", + requirements); + GNUNET_free (requirements); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_GONE, + NULL); + } - /* long polling for positive result? */ - if ( (kyp->kyc_required) && - GNUNET_TIME_absolute_is_future (kyp->timeout)) - { - GNUNET_log (GNUNET_ERROR_TYPE_START, - "Suspending HTTP request on timeout (%s) now...\n", - GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_remaining ( - kyp->timeout), - true)); - GNUNET_assert (NULL != kyp->eh); + /* FIXME: the next two DB interactions should be ONE + transaction */ + /* Check if we already initiated this process */ + qs = TEH_plugin->get_pending_kyc_requirement_process ( + TEH_plugin->cls, + &h_payto, + kyp->provider_name, + &kyp->redirect_url); + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return qs; + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "get_pending_kyc_requirement_process"); + } + if (NULL != kyp->redirect_url) + return MHD_YES; /* success, return the redirect URL + (in next call to this function) */ + + /* set up new requirement process */ + qs = TEH_plugin->insert_kyc_requirement_process ( + TEH_plugin->cls, + &h_payto, + kyp->measure_index, + kyp->legitimization_measure_serial_id, + kyp->provider_name, + NULL, /* provider_account_id */ + NULL, /* provider_legitimziation_id */ + &kyp->process_row); + if (qs < 0) + { + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_kyc_requirement_process"); + } + + kyp->ih = kyp->ih_logic->initiate ( + kyp->ih_logic->cls, + pd, + &h_payto, + kyp->process_row, + &initiate_cb, + kyp); + if (NULL == kyp->ih) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error ( + connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_BUG, + "initiate KYC process"); + } kyp->suspended = true; - kyp->section_name = NULL; GNUNET_CONTAINER_DLL_insert (kyp_head, kyp_tail, kyp); @@ -608,34 +459,24 @@ TEH_handler_kyc_start ( return MHD_YES; } - /* KYC plugin generated reply? */ - if (kyp->have_token) + if ( (TALER_EC_NONE != kyp->ec) || + (NULL == kyp->redirect_url) ) { - return TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - kyp->kyc_required - ? MHD_HTTP_ACCEPTED - : MHD_HTTP_OK, - GNUNET_JSON_pack_bool ("aml_review", - kyp->aml_review), - GNUNET_JSON_pack_data_auto ("access_token", - &kyp->access_token), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_array_steal ("limits", - kyp->limits))); + GNUNET_break (0); + if (TALER_EC_NONE == kyp->ec) + { + GNUNET_break (0); + kyp->ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + return TALER_MHD_reply_with_ec (rc->connection, + kyp->ec, + kyp->hint); } - l - /* KYC not required */ - GNUNET_log (GNUNET_ERROR_TYPE_START, - "KYC not required %llu\n", - (unsigned long long) kyp->requirement_row); - - return TALER_MHD_reply_static ( + return TALER_MHD_REPLY_JSON_PACK ( rc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); + MHD_HTTP_OK, + GNUNET_JSON_pack_string ("redirect_url", + kyp->redirect_url)); } diff --git a/src/exchange/taler-exchange-httpd_kyc-upload.c b/src/exchange/taler-exchange-httpd_kyc-upload.c @@ -44,7 +44,7 @@ struct UploadContext /** * Index of the measure this upload is for. */ - unsigned long long measure_index; + unsigned int measure_index; /** * Index in the legitimization measures table this ID @@ -294,7 +294,7 @@ TEH_handler_kyc_upload (struct TEH_RequestContext *rc, rc->rh_ctx = uc; rc->rh_cleaner = &upload_cleaner; - slash = strchr (id, '/'); + slash = strchr (id, '-'); if (NULL == slash) { GNUNET_break_op (0); @@ -313,12 +313,12 @@ TEH_handler_kyc_upload (struct TEH_RequestContext *rc, GNUNET_break_op (0); return TALER_MHD_reply_with_error ( rc->connection, - MHD_HTTP_NOT_FOUND, + MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, "Access token in ID is malformed"); } if (2 != sscanf (slash + 1, - "%llu/%llu%c", + "%u-%llu%c", &uc->measure_index, &uc->legitimization_measure_serial_id, &dummy)) @@ -326,7 +326,7 @@ TEH_handler_kyc_upload (struct TEH_RequestContext *rc, GNUNET_break_op (0); return TALER_MHD_reply_with_error ( rc->connection, - MHD_HTTP_NOT_FOUND, + MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, "ID is malformed"); } diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h @@ -4545,9 +4545,6 @@ void TALER_EXCHANGE_kyc_info_cancel (struct TALER_EXCHANGE_KycInfoHandle *kih); -// FIXME: /kyc-upload API? - - /** * Handle for an operation to start the KYC process. */ diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h @@ -417,6 +417,27 @@ TALER_KYCLOGIC_rules_to_limits (const json_t *jrules); /** + * Parse the given @a jmeasures and return the measure + * at the @a measure_index. + * + * @param jmeasures a LegitimizationMeasures object + * @param measure_index an index into the measures + * @param[out] check_name set to the name of the check + * @param[out] prog_name set to the name of the program + * @param[out] context set to the measure context + * (or NULL if there is no context) + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TALER_KYCLOGIC_select_measure ( + const json_t *jmeasures, + size_t measure_index, + const char **check_name, + const char **prog_name, + const json_t **context); + + +/** * Convert MeasureInformation into the * KycRequirementInformation used by the client. * diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c @@ -2326,4 +2326,17 @@ TALER_KYCLOGIC_get_measure_configuration ( } +enum TALER_ErrorCode +TALER_KYCLOGIC_select_measure ( + const json_t *jmeasures, + size_t measure_index, + const char **check_name, + const char **prog_name, + const json_t **context) +{ + // parse jmeasures + // check measure_index is valid (in bounds and of right type) +} + + /* end of kyclogic_api.c */