kyc logic with birthdate setting test added
1. Added age-requirement check in withdraw-handler (like in batch-withdraw) 2. In test_exchange_api_age_restriction: - kyc-oauth2 started, with static birthdate in answers - withdraw triggers kyc - second withdraw fails due to age restriction requirements
This commit is contained in:
parent
e230eaad76
commit
5bf90c3505
@ -26,6 +26,7 @@
|
||||
#include "platform.h"
|
||||
#include <gnunet/gnunet_util_lib.h>
|
||||
#include <jansson.h>
|
||||
#include "taler-exchange-httpd.h"
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
@ -180,6 +181,8 @@ withdraw_transaction (void *cls,
|
||||
bool found = false;
|
||||
bool balance_ok = false;
|
||||
bool nonce_ok = false;
|
||||
bool age_ok = false;
|
||||
uint16_t allowed_maximum_age = 0;
|
||||
uint64_t ruuid;
|
||||
const struct TALER_CsNonce *nonce;
|
||||
const struct TALER_BlindedPlanchet *bp;
|
||||
@ -342,9 +345,12 @@ withdraw_transaction (void *cls,
|
||||
nonce,
|
||||
&wc->collectable,
|
||||
wc->now,
|
||||
TEH_age_restriction_enabled,
|
||||
&found,
|
||||
&balance_ok,
|
||||
&nonce_ok,
|
||||
&age_ok,
|
||||
&allowed_maximum_age,
|
||||
&ruuid);
|
||||
if (0 > qs)
|
||||
{
|
||||
@ -366,6 +372,20 @@ withdraw_transaction (void *cls,
|
||||
NULL);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if (! age_ok)
|
||||
{
|
||||
/* We respond with the lowest age in the corresponding age group
|
||||
* of the required age */
|
||||
uint16_t lowest_age = TALER_get_lowest_age (
|
||||
&TEH_age_restriction_config.mask,
|
||||
allowed_maximum_age);
|
||||
|
||||
TEH_plugin->rollback (TEH_plugin->cls);
|
||||
*mhd_ret = TEH_RESPONSE_reply_reserve_age_restriction_required (
|
||||
connection,
|
||||
lowest_age);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if (! balance_ok)
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls);
|
||||
|
@ -26,20 +26,22 @@ CREATE OR REPLACE FUNCTION exchange_do_withdraw(
|
||||
IN denom_sig BYTEA,
|
||||
IN now INT8,
|
||||
IN min_reserve_gc INT8,
|
||||
IN do_age_check BOOLEAN,
|
||||
OUT reserve_found BOOLEAN,
|
||||
OUT balance_ok BOOLEAN,
|
||||
OUT nonce_ok BOOLEAN,
|
||||
OUT age_ok BOOLEAN,
|
||||
OUT allowed_maximum_age INT2, -- in years
|
||||
OUT ruuid INT8)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
reserve_gc INT8;
|
||||
DECLARE
|
||||
denom_serial INT8;
|
||||
DECLARE
|
||||
reserve_val INT8;
|
||||
DECLARE
|
||||
reserve_frac INT4;
|
||||
reserve_birthday INT4;
|
||||
not_before date;
|
||||
BEGIN
|
||||
-- Shards: reserves by reserve_pub (SELECT)
|
||||
-- reserves_out (INSERT, with CONFLICT detection) by wih
|
||||
@ -57,6 +59,8 @@ THEN
|
||||
-- denomination unknown, should be impossible!
|
||||
reserve_found=FALSE;
|
||||
balance_ok=FALSE;
|
||||
age_ok=FALSE;
|
||||
allowed_maximum_age=0;
|
||||
ruuid=0;
|
||||
ASSERT false, 'denomination unknown';
|
||||
RETURN;
|
||||
@ -67,11 +71,13 @@ SELECT
|
||||
current_balance_val
|
||||
,current_balance_frac
|
||||
,gc_date
|
||||
,birthday
|
||||
,reserve_uuid
|
||||
INTO
|
||||
reserve_val
|
||||
,reserve_frac
|
||||
,reserve_gc
|
||||
,reserve_birthday
|
||||
,ruuid
|
||||
FROM exchange.reserves
|
||||
WHERE reserves.reserve_pub=rpub;
|
||||
@ -82,10 +88,33 @@ THEN
|
||||
reserve_found=FALSE;
|
||||
balance_ok=FALSE;
|
||||
nonce_ok=TRUE;
|
||||
age_ok=FALSE;
|
||||
allowed_maximum_age=0;
|
||||
ruuid=2;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- Check if age requirements are present
|
||||
IF ((NOT do_age_check) OR (reserve_birthday = 0))
|
||||
THEN
|
||||
age_ok = TRUE;
|
||||
allowed_maximum_age = -1;
|
||||
ELSE
|
||||
-- Age requirements are formally not met: The exchange is setup to support
|
||||
-- age restrictions (do_age_check == TRUE) and the reserve has a
|
||||
-- birthday set (reserve_birthday != 0), but the client called the
|
||||
-- batch-withdraw endpoint instead of the age-withdraw endpoint, which it
|
||||
-- should have.
|
||||
not_before=date '1970-01-01' + reserve_birthday;
|
||||
allowed_maximum_age = extract(year from age(current_date, not_before));
|
||||
|
||||
reserve_found=TRUE;
|
||||
nonce_ok=TRUE; -- we do not really know
|
||||
balance_ok=TRUE;-- we do not really know
|
||||
age_ok = FALSE;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
-- We optimistically insert, and then on conflict declare
|
||||
-- the query successful due to idempotency.
|
||||
INSERT INTO exchange.reserves_out
|
||||
@ -194,6 +223,6 @@ END IF;
|
||||
END $$;
|
||||
|
||||
|
||||
COMMENT ON FUNCTION exchange_do_withdraw(BYTEA, INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
|
||||
IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and if so updates the database with the result';
|
||||
COMMENT ON FUNCTION exchange_do_withdraw(BYTEA, INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8, BOOLEAN)
|
||||
IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and if the age requirements are formally met. If so updates the database with the result';
|
||||
|
||||
|
@ -363,6 +363,8 @@ run (void *cls)
|
||||
bool found;
|
||||
bool nonce_ok;
|
||||
bool balance_ok;
|
||||
bool age_ok;
|
||||
uint16_t allowed_minimum_age;
|
||||
uint64_t ruuid;
|
||||
struct GNUNET_TIME_Timestamp now;
|
||||
|
||||
@ -372,9 +374,12 @@ run (void *cls)
|
||||
NULL,
|
||||
&cbc,
|
||||
now,
|
||||
false,
|
||||
&found,
|
||||
&balance_ok,
|
||||
&nonce_ok,
|
||||
&age_ok,
|
||||
&allowed_minimum_age,
|
||||
&ruuid));
|
||||
}
|
||||
{
|
||||
|
@ -32,9 +32,12 @@ TEH_PG_do_withdraw (
|
||||
const struct TALER_CsNonce *nonce,
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||
struct GNUNET_TIME_Timestamp now,
|
||||
bool do_age_check,
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
bool *nonce_ok,
|
||||
bool *age_ok,
|
||||
uint16_t *allowed_maximum_age,
|
||||
uint64_t *ruuid)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
@ -51,6 +54,7 @@ TEH_PG_do_withdraw (
|
||||
TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
|
||||
GNUNET_PQ_query_param_timestamp (&now),
|
||||
GNUNET_PQ_query_param_timestamp (&gc),
|
||||
GNUNET_PQ_query_param_bool (do_age_check),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
@ -60,6 +64,10 @@ TEH_PG_do_withdraw (
|
||||
balance_ok),
|
||||
GNUNET_PQ_result_spec_bool ("nonce_ok",
|
||||
nonce_ok),
|
||||
GNUNET_PQ_result_spec_bool ("age_ok",
|
||||
age_ok),
|
||||
GNUNET_PQ_result_spec_uint16 ("allowed_maximum_age",
|
||||
allowed_maximum_age),
|
||||
GNUNET_PQ_result_spec_uint64 ("ruuid",
|
||||
ruuid),
|
||||
GNUNET_PQ_result_spec_end
|
||||
@ -71,9 +79,11 @@ TEH_PG_do_withdraw (
|
||||
" reserve_found"
|
||||
",balance_ok"
|
||||
",nonce_ok"
|
||||
",age_ok"
|
||||
",allowed_maximum_age"
|
||||
",ruuid"
|
||||
" FROM exchange_do_withdraw"
|
||||
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);");
|
||||
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11);");
|
||||
gc = GNUNET_TIME_absolute_to_timestamp (
|
||||
GNUNET_TIME_absolute_add (now.abs_time,
|
||||
pg->legal_reserve_expiration_time));
|
||||
|
@ -33,9 +33,12 @@
|
||||
* @param nonce client-contributed input for CS denominations that must be checked for idempotency, or NULL for non-CS withdrawals
|
||||
* @param[in,out] collectable corresponding collectable coin (blind signature) if a coin is found; possibly updated if a (different) signature exists already
|
||||
* @param now current time (rounded)
|
||||
* @param do_age_check set to true if age requirements must be verified
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] nonce_ok set to false if the nonce was reused
|
||||
* @param[out] age_ok set to true if age requirements are met
|
||||
* @param[out] allowed_maximum_age if @e age_ok is false, the maximum age (in years) that is allowed during age-withdraw
|
||||
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||
* @return query execution status
|
||||
*/
|
||||
@ -45,9 +48,12 @@ TEH_PG_do_withdraw (
|
||||
const struct TALER_CsNonce *nonce,
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||
struct GNUNET_TIME_Timestamp now,
|
||||
bool do_age_check,
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
bool *nonce_ok,
|
||||
bool *age_ok,
|
||||
uint16_t *allowed_maximum_age,
|
||||
uint64_t *ruuid);
|
||||
|
||||
#endif
|
||||
|
@ -3745,9 +3745,12 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
* @param nonce client-contributed input for CS denominations that must be checked for idempotency, or NULL for non-CS withdrawals
|
||||
* @param collectable corresponding collectable coin (blind signature)
|
||||
* @param now current time (rounded)
|
||||
* @param do_age_check set to true if age requirements must be checked.
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] nonce_ok set to false if the nonce was reused
|
||||
* @param[out] age_ok set to true if no age requirements were defined on the reserve or @e do_age_check was false
|
||||
* @param[out] allowed_maximum_age when @e age_ok is false, set to the allowed maximum age for withdrawal from the reserve. The client MUST then use the age-withdraw endpoint
|
||||
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||
* @return query execution status
|
||||
*/
|
||||
@ -3757,9 +3760,12 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
const struct TALER_CsNonce *nonce,
|
||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||
struct GNUNET_TIME_Timestamp now,
|
||||
bool do_age_check,
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
bool *nonce_ok,
|
||||
bool *age_ok,
|
||||
uint16_t *allowed_maximum_age,
|
||||
uint64_t *ruuid);
|
||||
|
||||
|
||||
|
@ -922,9 +922,6 @@ data2attributes (const struct TALER_KYCLOGIC_ProviderDetails *pd,
|
||||
JSON_INDENT (2));
|
||||
return NULL;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"XXXXXXXX plugin_kyc_logic SETTING ATTERIBUTES TO\n\t%s\n",
|
||||
json_dumps (data, JSON_INDENT (2)));
|
||||
ret = json_loadb (attr_data,
|
||||
attr_size,
|
||||
JSON_REJECT_DUPLICATES,
|
||||
|
@ -285,6 +285,12 @@ handle_reserve_withdraw_finished (void *cls,
|
||||
w2r.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_CONFLICT:
|
||||
w2r.hr.ec = TALER_JSON_get_error_code (j);
|
||||
w2r.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
|
||||
if (TALER_EC_EXCHANGE_RESERVES_AGE_RESTRICTION_REQUIRED == w2r.hr.ec)
|
||||
break;
|
||||
|
||||
/* The exchange says that the reserve has insufficient funds;
|
||||
check the signatures in the history... */
|
||||
if (GNUNET_OK !=
|
||||
@ -295,11 +301,6 @@ handle_reserve_withdraw_finished (void *cls,
|
||||
w2r.hr.http_status = 0;
|
||||
w2r.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
}
|
||||
else
|
||||
{
|
||||
w2r.hr.ec = TALER_JSON_get_error_code (j);
|
||||
w2r.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
}
|
||||
break;
|
||||
case MHD_HTTP_GONE:
|
||||
/* could happen if denomination was revoked */
|
||||
|
@ -1,4 +1,4 @@
|
||||
# This file is in the public domain.
|
||||
#
|
||||
@INLINE@ test_exchange_api_age_restriction.conf
|
||||
@INLINE@ coins-cs.conf
|
||||
@INLINE@ test_exchange_api.conf
|
||||
|
@ -302,7 +302,7 @@ run (void *cls,
|
||||
true,
|
||||
true),
|
||||
TALER_TESTING_cmd_oauth_with_birthdate ("oauth-service-with-birthdate",
|
||||
"2022-00-00", /* enough for a while */
|
||||
"2015-00-00", /* enough for a while */
|
||||
6666),
|
||||
TALER_TESTING_cmd_batch ("withdraw-age",
|
||||
withdraw_age),
|
||||
|
Loading…
Reference in New Issue
Block a user