diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 0199802c1..033245f5a 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -114,6 +114,11 @@ struct DepositContext */ const struct TALER_EXCHANGEDB_Deposit *deposit; + /** + * Extension handler for policy, maybe NULL. + */ + const struct TALER_Extension *policy_extension; + /** * Our timestamp (when we received the request). * Possibly updated by the transaction if the @@ -156,6 +161,7 @@ deposit_transaction (void *cls, enum GNUNET_DB_QueryStatus qs; bool balance_ok; bool in_conflict; + bool blocked_by_policy = false; qs = TEH_make_coin_known (&dc->deposit->coin, connection, @@ -163,11 +169,30 @@ deposit_transaction (void *cls, mhd_ret); if (qs < 0) return qs; + + /* Check and apply policies, if applicable */ + if (NULL != dc->policy_extension) + { + const struct TALER_Extension *ext = dc->policy_extension; + struct TALER_ExtensionsPolicySerialID serialID; + struct GNUNET_TIME_Timestamp deadline; + GNUNET_assert (ext->parse_policy_details); + + qs = ext->parse_policy_details (dc->deposit->policy_details, + &serialID, + &deadline); + + if (qs < 0) + return qs; + + blocked_by_policy = true; + } + qs = TEH_plugin->do_deposit (TEH_plugin->cls, dc->deposit, dc->known_coin_id, &dc->h_payto, - false, /* FIXME-OEC: extension blocked #7270 */ + blocked_by_policy, &dc->exchange_timestamp, &balance_ok, &in_conflict); @@ -208,86 +233,6 @@ deposit_transaction (void *cls, } -/** - * @brief check the provided policy - * - * @param[in] policy_details JSON object provided by the client with prolicy - * @param[out] hc On success, will contain the hash of the normalized policy_details object - * @param[out] handler_out On success, the handler might provide an output - * @param[out] error_hint On failure, might contain a hint of the error from the extension - * @return GNUNET_OK on success. - */ -enum GNUNET_GenericReturnValue -check_policy_details ( - json_t *policy_details, - struct TALER_ExtensionPolicyHashP *hc, - json_t **handler_out, - char **error_hint) -{ - const char *type = NULL; - const struct TALER_Extension *extension; - enum GNUNET_GenericReturnValue ret; - - *error_hint = NULL; - - if ((NULL == policy_details) || - (! json_is_object (policy_details))) - { - *error_hint = "invalid policy object"; - return GNUNET_SYSERR; - } - - // parse and evaluate the object - { - json_t *jtype = json_object_get ( - policy_details, - "type"); - if (NULL == jtype) - { - *error_hint = "no type in policy object"; - return GNUNET_SYSERR; - } - - type = json_string_value (jtype); - if (NULL == type) - { - *error_hint = "invalid type in policy object"; - return GNUNET_SYSERR; - } - - extension = TALER_extensions_get_by_name (type); - if ((NULL == extension) || - (NULL == extension->deposit_handler)) - { - GNUNET_break (0); - *error_hint = "no such policy"; - return GNUNET_SYSERR; - } - - ret = extension->deposit_handler (policy_details, - handler_out); - if (GNUNET_OK != ret) - { - GNUNET_break (0); - if (NULL != *handler_out) - { - *error_hint = json_dumps (*handler_out, JSON_INDENT (2)); - } - else - { - GNUNET_break (1); - *error_hint = "unknown error with the policy"; - } - return ret; - } - } - - TALER_deposit_policy_hash (policy_details, - hc); - return GNUNET_OK; -} - - MHD_RESULT TEH_handler_deposit (struct MHD_Connection *connection, const struct TALER_CoinSpendPublicKeyP *coin_pub, @@ -296,9 +241,7 @@ TEH_handler_deposit (struct MHD_Connection *connection, struct DepositContext dc; struct TALER_EXCHANGEDB_Deposit deposit; const char *payto_uri; - struct TALER_ExtensionPolicyHashP h_policy; struct TALER_ExtensionPolicyHashP *ph_policy = NULL; - bool no_policy; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("merchant_payto_uri", &payto_uri), @@ -311,8 +254,6 @@ TEH_handler_deposit (struct MHD_Connection *connection, &deposit.coin.denom_pub_hash), TALER_JSON_spec_denom_sig ("ub_sig", &deposit.coin.denom_sig), - GNUNET_JSON_spec_fixed_auto ("merchant_pub", - &deposit.merchant_pub), GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &deposit.h_contract_terms), GNUNET_JSON_spec_mark_optional ( @@ -324,17 +265,20 @@ TEH_handler_deposit (struct MHD_Connection *connection, GNUNET_JSON_spec_timestamp ("timestamp", &deposit.timestamp), - /* TODO: this will move to an extension for refunds */ + /* TODO: refund_deadline and merchant_pub will move into the + * extension policy_merchant_refunds */ GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("refund_deadline", &deposit.refund_deadline), NULL), + GNUNET_JSON_spec_fixed_auto ("merchant_pub", + &deposit.merchant_pub), + GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", + &deposit.wire_deadline), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("policy", &deposit.policy_details), - &no_policy), - GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", - &deposit.wire_deadline), + &deposit.no_policy_details), GNUNET_JSON_spec_end () }; struct TALER_MerchantWireHashP h_wire; @@ -472,29 +416,11 @@ TEH_handler_deposit (struct MHD_Connection *connection, NULL); } - /* TODO: check policy_details */ - if (! no_policy) + if (! deposit.no_policy_details) { - char *hint; - json_t *out; - MHD_RESULT res; - - if (GNUNET_OK != - check_policy_details (dc.deposit->policy_details, - &h_policy, - &out, - &hint)) - { - res = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - /* TODO: new error type needed */ - TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, - hint); - GNUNET_free (hint); - return res; - } - - ph_policy = &h_policy; + TALER_deposit_policy_hash (deposit.policy_details, + &deposit.h_policy); + ph_policy = &deposit.h_policy; } deposit.deposit_fee = dk->meta.fees.deposit; diff --git a/src/extensions/age_restriction/age_restriction.c b/src/extensions/age_restriction/age_restriction.c index a1a11e4f7..576a60391 100644 --- a/src/extensions/age_restriction/age_restriction.c +++ b/src/extensions/age_restriction/age_restriction.c @@ -151,7 +151,9 @@ struct TALER_Extension TE_age_restriction = { .disable = &age_restriction_disable, .load_config = &age_restriction_load_config, .manifest = &age_restriction_manifest, - .deposit_handler = NULL, + + /* This extension is not a policy extension */ + .parse_policy_details = NULL, .http_get_handler = NULL, .http_post_handler = NULL, }; diff --git a/src/extensions/extensions.c b/src/extensions/extensions.c index 02137d31d..64574fc22 100644 --- a/src/extensions/extensions.c +++ b/src/extensions/extensions.c @@ -353,4 +353,53 @@ TALER_extensions_load_manifests ( } +enum GNUNET_GenericReturnValue +TALER_extensions_from_policy_details ( + const json_t *policy_details, + const struct TALER_Extension **extension, + char **error_hint) +{ + const json_t *jtype; + const char *type; + + *extension = NULL; + *error_hint = NULL; + + if ((NULL == policy_details) || + (! json_is_object (policy_details))) + { + *error_hint = "invalid policy object"; + return GNUNET_SYSERR; + } + + jtype = json_object_get (policy_details, "type"); + if (NULL == jtype) + { + *error_hint = "no type in policy object"; + return GNUNET_SYSERR; + } + + type = json_string_value (jtype); + if (NULL == type) + { + *error_hint = "invalid type in policy object"; + return GNUNET_SYSERR; + } + + *extension = TALER_extensions_get_by_name (type); + if ((NULL == *extension) || + (NULL == (*extension)->parse_policy_details)) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unsupported extension policy '%s' requested\n", + type); + *extension = NULL; + return GNUNET_NO; + } + + return GNUNET_OK; +} + + /* end of extensions.c */ diff --git a/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c b/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c index 160250a66..e00f810f0 100644 --- a/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c +++ b/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021-2022 Taler Systems SA + Copyright (C) 2022 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 @@ -32,7 +32,7 @@ /* (public) configuration of this extension */ /* TODO: these fields need to be set in the init handler */ -static struct TALER_BrandtVickreyAuction BV_config = { +static struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig BV_config = { .max_bidders = 10, .max_prices = 10, .auction_fee = { @@ -497,7 +497,7 @@ static json_t * auction_manifest ( const struct TALER_Extension *ext) { - struct TALER_BrandtVickreyAuction *conf = ext->config; + struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig *conf = ext->config; GNUNET_assert (conf); json_t *config = GNUNET_JSON_PACK ( @@ -589,20 +589,66 @@ auction_http_post_handler ( /** - * @brief implements the TALER_Extensions.deposit_handler interface. + * @brief implements the TALER_Extensions.parse_policy_details interface. * - * @param[in] input JSON input from the client during a deposit request - * @param[out] output by this extension + * @param[in] input The policy_details for this handler during deposit + * @param[out] serial On success will contain the serial-ID under which the + * @param[out] deadline On success will contain a deadline, might be "forever" + * exchange should store the policy_details in the policy_details table. * @return GNUNET_OK if the request was OK */ enum GNUNET_GenericReturnValue -auction_deposit_handler ( - json_t *input, - json_t **output) +auction_parse_policy_details ( + const json_t *input, + struct TALER_ExtensionsPolicySerialID *serial, + struct GNUNET_TIME_Timestamp *deadline) { - /* TODO */ - *output = NULL; - return GNUNET_OK; + enum GNUNET_GenericReturnValue ret = GNUNET_NO; + struct GNUNET_CRYPTO_EddsaPublicKey pub; + struct GNUNET_HashCode hc; + struct GNUNET_JSON_Specification spec[] = { + /* We ignore the "type" field as it must have been parsed already upstream + * - or this handler wouldn't have been called in first place. */ + GNUNET_JSON_spec_fixed_auto ("bidder_pub", &pub), + GNUNET_JSON_spec_fixed_auto ("h_auction", &hc), + GNUNET_JSON_spec_timestamp ("deadline", deadline), + GNUNET_JSON_spec_end (), + }; + + GNUNET_assert (serial); + GNUNET_assert (deadline); + + do { + ret = GNUNET_JSON_parse (input, + spec, + NULL, + NULL); + + if (GNUNET_OK != ret) + break; + + /* FIXME: check the deadline */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + LOG_PREFIX "check of deadline %ld not implemented!\n", + deadline->abs_time.abs_value_us); + + /* Create serial as H(bidder_pub, h_auction) */ + { + struct GNUNET_HashContext *hc = GNUNET_CRYPTO_hash_context_start (); + GNUNET_CRYPTO_hash_context_read (hc, + &pub, + sizeof(pub)); + GNUNET_CRYPTO_hash_context_read (hc, + &hc, + sizeof(hc)); + GNUNET_CRYPTO_hash_context_finish (hc, + &serial->hash); + } + + ret = GNUNET_OK; + } while(0); + + return ret; } @@ -617,7 +663,7 @@ struct TALER_Extension TE_auction_brandt = { .disable = &auction_disable, .load_config = &auction_load_config, .manifest = &auction_manifest, - .deposit_handler = &auction_deposit_handler, + .parse_policy_details = &auction_parse_policy_details, .http_get_handler = &auction_http_get_handler, .http_post_handler = &auction_http_post_handler, }; diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h index 646d5a848..d76de25ed 100644 --- a/src/include/taler_extensions.h +++ b/src/include/taler_extensions.h @@ -25,17 +25,21 @@ #include "taler_crypto_lib.h" #include "taler_json_lib.h" #include "taler_mhd_lib.h" +#include "taler_exchangedb_plugin.h" #define TALER_EXTENSION_SECTION_PREFIX "exchange-extension-" enum TALER_Extension_Type { - TALER_Extension_AgeRestriction = 0, - TALER_Extension_PolicyMerchantRefund = 1, - TALER_Extension_PolicyBrandtVickeryAuction = 2, - TALER_Extension_PolicyEscrowedPayment = 3, - TALER_Extension_MaxPredefined = 4 // Must be last of the predefined + TALER_Extension_PolicyNull = 0, + + TALER_Extension_AgeRestriction = 1, + TALER_Extension_PolicyMerchantRefund = 2, + TALER_Extension_PolicyBrandtVickeryAuction = 3, + TALER_Extension_PolicyEscrowedPayment = 4, + + TALER_Extension_MaxPredefined = 5 // Must be last of the predefined }; @@ -49,6 +53,15 @@ struct TALER_Extensions const struct TALER_Extension *extension; }; +/* + * @brief Serial ID under which the policy details to an deposit are stored in + * the policy_details table. + */ +struct TALER_ExtensionsPolicySerialID +{ + struct GNUNET_HashCode hash; +}; + /* * @brief Represents the implementation of an extension. * @@ -151,22 +164,30 @@ struct TALER_Extension json_t *(*manifest)( const struct TALER_Extension *ext); + /* ========================= + * Policy related handlers + * ========================= + */ /** - * @brief Handler for /{batch}-deposit requests. Can be NULL. + * @brief Handler to check a policy. Can be NULL; * * When a deposit request refers to this extension in its policy * (see https://docs.taler.net/core/api-exchange.html#deposit), this handler - * will be called during the deposit operation. + * will be called before the deposit transaction. * - * @param[in] input The policy/extension specific data, provided by the client in - * the policy-field of the deposit request. - * @param[out] output Potential output by the extension to be provided to the - * client as part of the response to the deposit request. Can be NULL. + * @param[in] policy_details Details about the policy, provided by the client + * during a deposit request. + * @param[out] serial On success, will contain the serial-ID under which the + * exchange should save the policy_details in the deposit table. + * @param[out] deadline On sucess, set to the deadline until the policy must + * be fulfilled. Might be "forever". This value is used by an external + * mechanism to detect timeouts. * @return GNUNET_OK if the data was accepted by the extension. */ - enum GNUNET_GenericReturnValue (*deposit_handler)( - json_t *input, - json_t **output); + enum GNUNET_GenericReturnValue (*parse_policy_details)( + const json_t *policy_details, + struct TALER_ExtensionsPolicySerialID *serial, + struct GNUNET_TIME_Timestamp *deadline); /** * @brief Handler for POST-requests to the /policy/$name endpoint. Can be NULL. @@ -366,19 +387,65 @@ struct TALER_AgeMask TALER_extensions_get_age_restriction_mask (); +/* + * =================================== + * Policy extensions related functions + * =================================== + */ + +/* + * @brief Finds the extension for a given policy + * + * @param[in] policy_details JSON of the policy detail from a deposit request + * @param[out] extension On GNUNET_OK, the exentions handling the given policy + * @param[out] error_hint On GNUNET_SYSERR, will contain a hint for the reason why it failed + * @return GNUNET_OK on success, with *extension set to the correct extension. + * GNUNET_NO, when no extension was found. GNUNET_SYSERR when the JSON was + * invalid, with *error_hint maybe non-NULL. + */ +enum GNUNET_GenericReturnValue +TALER_extensions_from_policy_details ( + const json_t *policy_details, + const struct TALER_Extension **extension, + char **error_hint); + /* * ================================ - * Brandt-Vickrey Auctions + * Merchant refund policy + * ================================ + */ +struct TALER_ExtensionPolicyMerchantRefundPolicyConfig +{ + struct GNUNET_TIME_Relative max_timeout; +}; + +/* + * ================================ + * Brandt-Vickrey Auctions policy * ================================ */ /* - * @brief Configuration for Brandt-Vickrey auctions + * @brief Configuration for Brandt-Vickrey auctions policy */ -struct TALER_BrandtVickreyAuction +struct TALER_ExtensionPolicyBrandtVickreyAuctionConfig { uint16_t max_bidders; uint16_t max_prices; struct TALER_Amount auction_fee; }; + +/* + * ================================ + * Escrowed Payments policy + * ================================ + */ +/* + * @brief Configuration for escrowed payments policy + */ +struct TALER_ExtensionPolicyEscrowedPaymentsConfig +{ + struct GNUNET_TIME_Relative max_timeout; +}; + #endif