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 "platform.h"
|
||||||
#include <gnunet/gnunet_util_lib.h>
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
|
#include "taler-exchange-httpd.h"
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_kyclogic_lib.h"
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
@ -180,6 +181,8 @@ withdraw_transaction (void *cls,
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
bool balance_ok = false;
|
bool balance_ok = false;
|
||||||
bool nonce_ok = false;
|
bool nonce_ok = false;
|
||||||
|
bool age_ok = false;
|
||||||
|
uint16_t allowed_maximum_age = 0;
|
||||||
uint64_t ruuid;
|
uint64_t ruuid;
|
||||||
const struct TALER_CsNonce *nonce;
|
const struct TALER_CsNonce *nonce;
|
||||||
const struct TALER_BlindedPlanchet *bp;
|
const struct TALER_BlindedPlanchet *bp;
|
||||||
@ -342,9 +345,12 @@ withdraw_transaction (void *cls,
|
|||||||
nonce,
|
nonce,
|
||||||
&wc->collectable,
|
&wc->collectable,
|
||||||
wc->now,
|
wc->now,
|
||||||
|
TEH_age_restriction_enabled,
|
||||||
&found,
|
&found,
|
||||||
&balance_ok,
|
&balance_ok,
|
||||||
&nonce_ok,
|
&nonce_ok,
|
||||||
|
&age_ok,
|
||||||
|
&allowed_maximum_age,
|
||||||
&ruuid);
|
&ruuid);
|
||||||
if (0 > qs)
|
if (0 > qs)
|
||||||
{
|
{
|
||||||
@ -366,6 +372,20 @@ withdraw_transaction (void *cls,
|
|||||||
NULL);
|
NULL);
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
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)
|
if (! balance_ok)
|
||||||
{
|
{
|
||||||
TEH_plugin->rollback (TEH_plugin->cls);
|
TEH_plugin->rollback (TEH_plugin->cls);
|
||||||
|
@ -26,20 +26,22 @@ CREATE OR REPLACE FUNCTION exchange_do_withdraw(
|
|||||||
IN denom_sig BYTEA,
|
IN denom_sig BYTEA,
|
||||||
IN now INT8,
|
IN now INT8,
|
||||||
IN min_reserve_gc INT8,
|
IN min_reserve_gc INT8,
|
||||||
|
IN do_age_check BOOLEAN,
|
||||||
OUT reserve_found BOOLEAN,
|
OUT reserve_found BOOLEAN,
|
||||||
OUT balance_ok BOOLEAN,
|
OUT balance_ok BOOLEAN,
|
||||||
OUT nonce_ok BOOLEAN,
|
OUT nonce_ok BOOLEAN,
|
||||||
|
OUT age_ok BOOLEAN,
|
||||||
|
OUT allowed_maximum_age INT2, -- in years
|
||||||
OUT ruuid INT8)
|
OUT ruuid INT8)
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
reserve_gc INT8;
|
reserve_gc INT8;
|
||||||
DECLARE
|
|
||||||
denom_serial INT8;
|
denom_serial INT8;
|
||||||
DECLARE
|
|
||||||
reserve_val INT8;
|
reserve_val INT8;
|
||||||
DECLARE
|
|
||||||
reserve_frac INT4;
|
reserve_frac INT4;
|
||||||
|
reserve_birthday INT4;
|
||||||
|
not_before date;
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Shards: reserves by reserve_pub (SELECT)
|
-- Shards: reserves by reserve_pub (SELECT)
|
||||||
-- reserves_out (INSERT, with CONFLICT detection) by wih
|
-- reserves_out (INSERT, with CONFLICT detection) by wih
|
||||||
@ -57,6 +59,8 @@ THEN
|
|||||||
-- denomination unknown, should be impossible!
|
-- denomination unknown, should be impossible!
|
||||||
reserve_found=FALSE;
|
reserve_found=FALSE;
|
||||||
balance_ok=FALSE;
|
balance_ok=FALSE;
|
||||||
|
age_ok=FALSE;
|
||||||
|
allowed_maximum_age=0;
|
||||||
ruuid=0;
|
ruuid=0;
|
||||||
ASSERT false, 'denomination unknown';
|
ASSERT false, 'denomination unknown';
|
||||||
RETURN;
|
RETURN;
|
||||||
@ -67,11 +71,13 @@ SELECT
|
|||||||
current_balance_val
|
current_balance_val
|
||||||
,current_balance_frac
|
,current_balance_frac
|
||||||
,gc_date
|
,gc_date
|
||||||
|
,birthday
|
||||||
,reserve_uuid
|
,reserve_uuid
|
||||||
INTO
|
INTO
|
||||||
reserve_val
|
reserve_val
|
||||||
,reserve_frac
|
,reserve_frac
|
||||||
,reserve_gc
|
,reserve_gc
|
||||||
|
,reserve_birthday
|
||||||
,ruuid
|
,ruuid
|
||||||
FROM exchange.reserves
|
FROM exchange.reserves
|
||||||
WHERE reserves.reserve_pub=rpub;
|
WHERE reserves.reserve_pub=rpub;
|
||||||
@ -82,10 +88,33 @@ THEN
|
|||||||
reserve_found=FALSE;
|
reserve_found=FALSE;
|
||||||
balance_ok=FALSE;
|
balance_ok=FALSE;
|
||||||
nonce_ok=TRUE;
|
nonce_ok=TRUE;
|
||||||
|
age_ok=FALSE;
|
||||||
|
allowed_maximum_age=0;
|
||||||
ruuid=2;
|
ruuid=2;
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
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
|
-- We optimistically insert, and then on conflict declare
|
||||||
-- the query successful due to idempotency.
|
-- the query successful due to idempotency.
|
||||||
INSERT INTO exchange.reserves_out
|
INSERT INTO exchange.reserves_out
|
||||||
@ -194,6 +223,6 @@ END IF;
|
|||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION exchange_do_withdraw(BYTEA, INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
|
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 so updates the database with the result';
|
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 found;
|
||||||
bool nonce_ok;
|
bool nonce_ok;
|
||||||
bool balance_ok;
|
bool balance_ok;
|
||||||
|
bool age_ok;
|
||||||
|
uint16_t allowed_minimum_age;
|
||||||
uint64_t ruuid;
|
uint64_t ruuid;
|
||||||
struct GNUNET_TIME_Timestamp now;
|
struct GNUNET_TIME_Timestamp now;
|
||||||
|
|
||||||
@ -372,9 +374,12 @@ run (void *cls)
|
|||||||
NULL,
|
NULL,
|
||||||
&cbc,
|
&cbc,
|
||||||
now,
|
now,
|
||||||
|
false,
|
||||||
&found,
|
&found,
|
||||||
&balance_ok,
|
&balance_ok,
|
||||||
&nonce_ok,
|
&nonce_ok,
|
||||||
|
&age_ok,
|
||||||
|
&allowed_minimum_age,
|
||||||
&ruuid));
|
&ruuid));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -32,9 +32,12 @@ TEH_PG_do_withdraw (
|
|||||||
const struct TALER_CsNonce *nonce,
|
const struct TALER_CsNonce *nonce,
|
||||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||||
struct GNUNET_TIME_Timestamp now,
|
struct GNUNET_TIME_Timestamp now,
|
||||||
|
bool do_age_check,
|
||||||
bool *found,
|
bool *found,
|
||||||
bool *balance_ok,
|
bool *balance_ok,
|
||||||
bool *nonce_ok,
|
bool *nonce_ok,
|
||||||
|
bool *age_ok,
|
||||||
|
uint16_t *allowed_maximum_age,
|
||||||
uint64_t *ruuid)
|
uint64_t *ruuid)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
@ -51,6 +54,7 @@ TEH_PG_do_withdraw (
|
|||||||
TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
|
TALER_PQ_query_param_blinded_denom_sig (&collectable->sig),
|
||||||
GNUNET_PQ_query_param_timestamp (&now),
|
GNUNET_PQ_query_param_timestamp (&now),
|
||||||
GNUNET_PQ_query_param_timestamp (&gc),
|
GNUNET_PQ_query_param_timestamp (&gc),
|
||||||
|
GNUNET_PQ_query_param_bool (do_age_check),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
@ -60,6 +64,10 @@ TEH_PG_do_withdraw (
|
|||||||
balance_ok),
|
balance_ok),
|
||||||
GNUNET_PQ_result_spec_bool ("nonce_ok",
|
GNUNET_PQ_result_spec_bool ("nonce_ok",
|
||||||
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",
|
GNUNET_PQ_result_spec_uint64 ("ruuid",
|
||||||
ruuid),
|
ruuid),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
@ -71,9 +79,11 @@ TEH_PG_do_withdraw (
|
|||||||
" reserve_found"
|
" reserve_found"
|
||||||
",balance_ok"
|
",balance_ok"
|
||||||
",nonce_ok"
|
",nonce_ok"
|
||||||
|
",age_ok"
|
||||||
|
",allowed_maximum_age"
|
||||||
",ruuid"
|
",ruuid"
|
||||||
" FROM exchange_do_withdraw"
|
" 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 (
|
gc = GNUNET_TIME_absolute_to_timestamp (
|
||||||
GNUNET_TIME_absolute_add (now.abs_time,
|
GNUNET_TIME_absolute_add (now.abs_time,
|
||||||
pg->legal_reserve_expiration_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 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[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 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] found set to true if the reserve was found
|
||||||
* @param[out] balance_ok set to true if the balance was sufficient
|
* @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] 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)
|
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||||
* @return query execution status
|
* @return query execution status
|
||||||
*/
|
*/
|
||||||
@ -45,9 +48,12 @@ TEH_PG_do_withdraw (
|
|||||||
const struct TALER_CsNonce *nonce,
|
const struct TALER_CsNonce *nonce,
|
||||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||||
struct GNUNET_TIME_Timestamp now,
|
struct GNUNET_TIME_Timestamp now,
|
||||||
|
bool do_age_check,
|
||||||
bool *found,
|
bool *found,
|
||||||
bool *balance_ok,
|
bool *balance_ok,
|
||||||
bool *nonce_ok,
|
bool *nonce_ok,
|
||||||
|
bool *age_ok,
|
||||||
|
uint16_t *allowed_maximum_age,
|
||||||
uint64_t *ruuid);
|
uint64_t *ruuid);
|
||||||
|
|
||||||
#endif
|
#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 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 collectable corresponding collectable coin (blind signature)
|
||||||
* @param now current time (rounded)
|
* @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] found set to true if the reserve was found
|
||||||
* @param[out] balance_ok set to true if the balance was sufficient
|
* @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] 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)
|
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||||
* @return query execution status
|
* @return query execution status
|
||||||
*/
|
*/
|
||||||
@ -3757,9 +3760,12 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
const struct TALER_CsNonce *nonce,
|
const struct TALER_CsNonce *nonce,
|
||||||
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
const struct TALER_EXCHANGEDB_CollectableBlindcoin *collectable,
|
||||||
struct GNUNET_TIME_Timestamp now,
|
struct GNUNET_TIME_Timestamp now,
|
||||||
|
bool do_age_check,
|
||||||
bool *found,
|
bool *found,
|
||||||
bool *balance_ok,
|
bool *balance_ok,
|
||||||
bool *nonce_ok,
|
bool *nonce_ok,
|
||||||
|
bool *age_ok,
|
||||||
|
uint16_t *allowed_maximum_age,
|
||||||
uint64_t *ruuid);
|
uint64_t *ruuid);
|
||||||
|
|
||||||
|
|
||||||
|
@ -922,9 +922,6 @@ data2attributes (const struct TALER_KYCLOGIC_ProviderDetails *pd,
|
|||||||
JSON_INDENT (2));
|
JSON_INDENT (2));
|
||||||
return NULL;
|
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,
|
ret = json_loadb (attr_data,
|
||||||
attr_size,
|
attr_size,
|
||||||
JSON_REJECT_DUPLICATES,
|
JSON_REJECT_DUPLICATES,
|
||||||
|
@ -285,6 +285,12 @@ handle_reserve_withdraw_finished (void *cls,
|
|||||||
w2r.hr.hint = TALER_JSON_get_error_hint (j);
|
w2r.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_CONFLICT:
|
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;
|
/* The exchange says that the reserve has insufficient funds;
|
||||||
check the signatures in the history... */
|
check the signatures in the history... */
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
@ -295,11 +301,6 @@ handle_reserve_withdraw_finished (void *cls,
|
|||||||
w2r.hr.http_status = 0;
|
w2r.hr.http_status = 0;
|
||||||
w2r.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
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;
|
break;
|
||||||
case MHD_HTTP_GONE:
|
case MHD_HTTP_GONE:
|
||||||
/* could happen if denomination was revoked */
|
/* could happen if denomination was revoked */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# This file is in the public domain.
|
# This file is in the public domain.
|
||||||
#
|
#
|
||||||
|
@INLINE@ test_exchange_api_age_restriction.conf
|
||||||
@INLINE@ coins-cs.conf
|
@INLINE@ coins-cs.conf
|
||||||
@INLINE@ test_exchange_api.conf
|
|
||||||
|
@ -302,7 +302,7 @@ run (void *cls,
|
|||||||
true,
|
true,
|
||||||
true),
|
true),
|
||||||
TALER_TESTING_cmd_oauth_with_birthdate ("oauth-service-with-birthdate",
|
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),
|
6666),
|
||||||
TALER_TESTING_cmd_batch ("withdraw-age",
|
TALER_TESTING_cmd_batch ("withdraw-age",
|
||||||
withdraw_age),
|
withdraw_age),
|
||||||
|
Loading…
Reference in New Issue
Block a user