diff options
author | Özgür Kesim <oec-taler@kesim.org> | 2022-11-04 12:18:16 +0100 |
---|---|---|
committer | Özgür Kesim <oec-taler@kesim.org> | 2022-11-04 12:18:16 +0100 |
commit | 752f10273860d2496fc3eb1e03de6ad4451e7c0f (patch) | |
tree | 53d51969f58611dbf8afacdcd40a769f5c847dd8 /src/exchange | |
parent | c89bfa9026d7180eb24ae9480f225b93db22c53a (diff) | |
download | exchange-752f10273860d2496fc3eb1e03de6ad4451e7c0f.tar.gz exchange-752f10273860d2496fc3eb1e03de6ad4451e7c0f.tar.bz2 exchange-752f10273860d2496fc3eb1e03de6ad4451e7c0f.zip |
policy extensions and age restriction refactoring
- refactoring of extension-plugin-mechanism
- refactoring of age restriction extension
- added policy extensions plugin plumbing
- added DB schema and api
- policy_details
- policy_fulfillments
Diffstat (limited to 'src/exchange')
-rw-r--r-- | src/exchange/taler-exchange-httpd.c | 14 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd.h | 10 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_batch-deposit.c | 121 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_deposit.c | 102 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_extensions.c | 266 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_extensions.h | 15 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_keys.c | 85 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_management_extensions.c | 48 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_metrics.h | 3 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_refreshes_reveal.c | 5 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_responses.c | 2 |
11 files changed, 508 insertions, 163 deletions
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 34f93879a..691e1ef7d 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -102,6 +102,14 @@ static int allow_address_reuse; const struct GNUNET_CONFIGURATION_Handle *TEH_cfg; /** + * Configuration of age restriction + * + * Set after loading the library, enabled in database event handler. + */ +bool TEH_age_restriction_enabled = false; +struct TALER_AgeRestrictionConfig TEH_age_restriction_config = {0}; + +/** * Handle to the HTTP server. */ static struct MHD_Daemon *mhd; @@ -143,11 +151,6 @@ char *TEH_currency; char *TEH_base_url; /** - * Age restriction flags and mask - */ -bool TEH_age_restriction_enabled = true; - -/** * Default timeout in seconds for HTTP requests. */ static unsigned int connection_timeout = 30; @@ -174,6 +177,7 @@ bool TEH_suicide; * TALER_SIGNATURE_MASTER_EXTENSION. */ struct TALER_MasterSignatureP TEH_extensions_sig; +bool TEH_extensions_signed = false; /** * Value to return from main() diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index 0fda5ed8d..4d3fb4901 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -197,11 +197,6 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin; */ extern char *TEH_currency; -/* - * Age restriction extension state - */ -extern bool TEH_age_restriction_enabled; - /** * Our (externally visible) base URL. */ @@ -221,6 +216,7 @@ extern struct GNUNET_CURL_Context *TEH_curl_ctx; * Signature of the offline master key of all enabled extensions' configuration */ extern struct TALER_MasterSignatureP TEH_extensions_sig; +extern bool TEH_extensions_signed; /** * @brief Struct describing an URL and the handler for it. @@ -366,4 +362,8 @@ struct TEH_RequestHandler }; +/* Age restriction configuration */ +extern bool TEH_age_restriction_enabled; +extern struct TALER_AgeRestrictionConfig TEH_age_restriction_config; + #endif diff --git a/src/exchange/taler-exchange-httpd_batch-deposit.c b/src/exchange/taler-exchange-httpd_batch-deposit.c index c2a9cbd54..4d4197ab6 100644 --- a/src/exchange/taler-exchange-httpd_batch-deposit.c +++ b/src/exchange/taler-exchange-httpd_batch-deposit.c @@ -87,15 +87,27 @@ struct BatchDepositContext const char *payto_uri; /** - * Additional details for extensions relevant for this + * Additional details for policy extension relevant for this * deposit operation, possibly NULL! */ - json_t *extension_details; + json_t *policy_json; /** - * Hash over @e extension_details. + * Will be true if policy_json were provided */ - struct TALER_ExtensionContractHashP h_extensions; + bool has_policy; + + /** + * If @e policy_json was present, the corresponding policy extension + * calculates these details. These will be persisted in the policy_details + * table. + */ + struct TALER_PolicyDetails policy_details; + + /** + * Hash over @e policy_details. + */ + struct TALER_ExtensionPolicyHashP h_policy; /** * Time when this request was generated. Used, for example, to @@ -173,7 +185,7 @@ again: &TEH_keys_exchange_sign_, &bdc->h_contract_terms, &bdc->h_wire, - &bdc->h_extensions, + bdc->has_policy ? &bdc->h_policy: NULL, bdc->exchange_timestamp, bdc->wire_deadline, bdc->refund_deadline, @@ -242,7 +254,7 @@ batch_deposit_transaction (void *cls, MHD_RESULT *mhd_ret) { struct BatchDepositContext *dc = cls; - enum GNUNET_DB_QueryStatus qs; + enum GNUNET_DB_QueryStatus qs = GNUNET_SYSERR; bool balance_ok; bool in_conflict; @@ -469,18 +481,19 @@ parse_coin (struct MHD_Connection *connection, TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; if (GNUNET_OK != - TALER_wallet_deposit_verify (&deposit->amount_with_fee, - &deposit->deposit_fee, - &dc->h_wire, - &dc->h_contract_terms, - &deposit->coin.h_age_commitment, - &dc->h_extensions, - &deposit->coin.denom_pub_hash, - dc->timestamp, - &dc->merchant_pub, - dc->refund_deadline, - &deposit->coin.coin_pub, - &deposit->csig)) + TALER_wallet_deposit_verify ( + &deposit->amount_with_fee, + &deposit->deposit_fee, + &dc->h_wire, + &dc->h_contract_terms, + &deposit->coin.h_age_commitment, + dc->has_policy ? &dc->h_policy : NULL, + &deposit->coin.denom_pub_hash, + dc->timestamp, + &dc->merchant_pub, + dc->refund_deadline, + &deposit->coin.coin_pub, + &deposit->csig)) { TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n"); GNUNET_JSON_parse_free (spec); @@ -496,11 +509,6 @@ parse_coin (struct MHD_Connection *connection, deposit->h_contract_terms = dc->h_contract_terms; deposit->wire_salt = dc->wire_salt; deposit->receiver_wire_account = (char *) dc->payto_uri; - /* FIXME-OEC: #7270 should NOT insert the extension details N times, - but rather insert them ONCE and then per-coin only use - the resulting extension UUID/serial; so the data structure - here should be changed once we look at extensions in earnest. */ - deposit->extension_details = dc->extension_details; deposit->timestamp = dc->timestamp; deposit->refund_deadline = dc->refund_deadline; deposit->wire_deadline = dc->wire_deadline; @@ -517,7 +525,7 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, struct BatchDepositContext dc; json_t *coins; bool no_refund_deadline = true; - bool no_extensions = true; + bool no_policy_json = true; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("merchant_payto_uri", &dc.payto_uri), @@ -530,9 +538,9 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, GNUNET_JSON_spec_json ("coins", &coins), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("extension_details", - &dc.extension_details), - &no_extensions), + GNUNET_JSON_spec_json ("policy", + &dc.policy_json), + &no_policy_json), GNUNET_JSON_spec_timestamp ("timestamp", &dc.timestamp), GNUNET_JSON_spec_mark_optional ( @@ -563,6 +571,8 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, return MHD_YES; /* failure */ } + dc.has_policy = ! no_policy_json; + /* validate merchant's wire details (as far as we can) */ { char *emsg; @@ -607,11 +617,26 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, TALER_merchant_wire_signature_hash (dc.payto_uri, &dc.wire_salt, &dc.h_wire); - /* FIXME-OEC: #7270 hash actual extension JSON object here */ - // if (! no_extensions) - memset (&dc.h_extensions, - 0, - sizeof (dc.h_extensions)); + + /* handle policy, if present */ + if (dc.has_policy) + { + const char *error_hint = NULL; + + if (GNUNET_OK != + TALER_extensions_create_policy_details ( + dc.policy_json, + &dc.policy_details, + &error_hint)) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED, + error_hint); + + TALER_deposit_policy_hash (dc.policy_json, + &dc.h_policy); + } + dc.num_coins = json_array_size (coins); if (0 == dc.num_coins) { @@ -635,12 +660,32 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, struct TALER_EXCHANGEDB_Deposit); for (unsigned int i = 0; i<dc.num_coins; i++) { - if (GNUNET_OK != - (res = parse_coin (connection, - json_array_get (coins, - i), - &dc, - &dc.deposits[i]))) + do { + res = parse_coin (connection, + json_array_get (coins, i), + &dc, + &dc.deposits[i]); + if (GNUNET_OK != res) + break; + + /* If applicable, accumulate all contributions into the policy_details */ + if (dc.has_policy) + { + /* FIXME: how do deposit-fee and policy-fee interact? */ + struct TALER_Amount amount_without_fee; + + res = TALER_amount_subtract (&amount_without_fee, + &dc.deposits[i].amount_with_fee, + &dc.deposits[i].deposit_fee + ); + res = TALER_amount_add ( + &dc.policy_details.accumulated_total, + &dc.policy_details.accumulated_total, + &amount_without_fee); + } + } while(0); + + if (GNUNET_OK != res) { for (unsigned int j = 0; j<i; j++) TALER_denom_sig_free (&dc.deposits[j].coin.denom_sig); diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 0484ab071..455888a89 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -21,6 +21,7 @@ * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff + * @author Özgür Kesim */ #include "platform.h" #include <gnunet/gnunet_util_lib.h> @@ -47,7 +48,7 @@ * @param connection connection to the client * @param coin_pub public key of the coin * @param h_wire hash of wire details - * @param h_extensions hash of applicable extensions + * @param h_policy hash of applicable policy extension * @param h_contract_terms hash of contract details * @param exchange_timestamp exchange's timestamp * @param refund_deadline until when this deposit be refunded @@ -61,7 +62,7 @@ reply_deposit_success ( struct MHD_Connection *connection, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_MerchantWireHashP *h_wire, - const struct TALER_ExtensionContractHashP *h_extensions, + const struct TALER_ExtensionPolicyHashP *h_policy, const struct TALER_PrivateContractHashP *h_contract_terms, struct GNUNET_TIME_Timestamp exchange_timestamp, struct GNUNET_TIME_Timestamp refund_deadline, @@ -78,7 +79,7 @@ reply_deposit_success ( &TEH_keys_exchange_sign_, h_contract_terms, h_wire, - h_extensions, + h_policy, exchange_timestamp, wire_deadline, refund_deadline, @@ -131,6 +132,29 @@ struct DepositContext */ uint64_t known_coin_id; + /* + * True if @e policy_json was provided + */ + bool has_policy; + + /** + * If @e has_policy is true, the corresponding policy extension calculates + * these details. These will be persisted in the policy_details table. + */ + struct TALER_PolicyDetails policy_details; + + /** + * Hash over the policy data for this deposit (remains unknown to the + * Exchange). Needed for the verification of the deposit's signature + */ + struct TALER_ExtensionPolicyHashP h_policy; + + /** + * When has_policy is true, and deposit->policy_details are + * persisted, this contains the id of the record in the policy_details table. + */ + uint64_t policy_details_serial_id; + }; @@ -163,14 +187,35 @@ deposit_transaction (void *cls, mhd_ret); if (qs < 0) return qs; - qs = TEH_plugin->do_deposit (TEH_plugin->cls, - dc->deposit, - dc->known_coin_id, - &dc->h_payto, - false, /* FIXME-OEC: extension blocked #7270 */ - &dc->exchange_timestamp, - &balance_ok, - &in_conflict); + + + /* If the deposit has a policy associated to it, persist it. This will + * insert or update the record. */ + if (dc->has_policy) + { + qs = TEH_plugin->persist_policy_details ( + TEH_plugin->cls, + &dc->policy_details, + &dc->policy_details_serial_id, + &dc->policy_details.accumulated_total, + &dc->policy_details.fulfillment_state); + + if (qs < 0) + return qs; + } + + + qs = TEH_plugin->do_deposit ( + TEH_plugin->cls, + dc->deposit, + dc->known_coin_id, + &dc->h_payto, + (dc->has_policy) + ? &dc->policy_details_serial_id + : NULL, + &dc->exchange_timestamp, + &balance_ok, + &in_conflict); if (qs < 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -216,6 +261,8 @@ TEH_handler_deposit (struct MHD_Connection *connection, struct DepositContext dc; struct TALER_EXCHANGEDB_Deposit deposit; const char *payto_uri; + struct TALER_ExtensionPolicyHashP *ph_policy = NULL; + bool no_policy_json; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("merchant_payto_uri", &payto_uri), @@ -240,12 +287,18 @@ TEH_handler_deposit (struct MHD_Connection *connection, &deposit.csig), GNUNET_JSON_spec_timestamp ("timestamp", &deposit.timestamp), + /* 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_timestamp ("wire_transfer_deadline", &deposit.wire_deadline), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("policy", + &dc.policy_details.policy_json), + &no_policy_json), GNUNET_JSON_spec_end () }; struct TALER_MerchantWireHashP h_wire; @@ -271,6 +324,9 @@ TEH_handler_deposit (struct MHD_Connection *connection, return MHD_YES; /* failure */ } } + + dc.has_policy = ! no_policy_json; + /* validate merchant's wire details (as far as we can) */ { char *emsg; @@ -419,6 +475,26 @@ TEH_handler_deposit (struct MHD_Connection *connection, NULL); } + /* Check policy input and create policy details */ + if (dc.has_policy) + { + const char *error_hint = NULL; + + if (GNUNET_OK != + TALER_extensions_create_policy_details ( + dc.policy_details.policy_json, + &dc.policy_details, + &error_hint)) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED, + error_hint); + + TALER_deposit_policy_hash (dc.policy_details.policy_json, + &dc.h_policy); + ph_policy = &dc.h_policy; + } + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; if (GNUNET_OK != TALER_wallet_deposit_verify (&deposit.amount_with_fee, @@ -426,7 +502,7 @@ TEH_handler_deposit (struct MHD_Connection *connection, &h_wire, &deposit.h_contract_terms, &deposit.coin.h_age_commitment, - NULL /* FIXME: h_extensions! */, + ph_policy, &deposit.coin.denom_pub_hash, deposit.timestamp, &deposit.merchant_pub, @@ -481,7 +557,7 @@ TEH_handler_deposit (struct MHD_Connection *connection, res = reply_deposit_success (connection, &deposit.coin.coin_pub, &h_wire, - NULL /* FIXME: h_extensions! */, + ph_policy, &deposit.h_contract_terms, dc.exchange_timestamp, deposit.refund_deadline, diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c index d6c26f6f4..30d1c5ac9 100644 --- a/src/exchange/taler-exchange-httpd_extensions.c +++ b/src/exchange/taler-exchange-httpd_extensions.c @@ -14,7 +14,7 @@ */ /** * @file taler-exchange-httpd_extensions.c - * @brief Handle extensions (age-restriction, peer2peer) + * @brief Handle extensions (age-restriction, policy extensions) * @author Özgür Kesim */ #include "platform.h" @@ -77,65 +77,84 @@ extension_update_event_cb (void *cls, return; } - // Get the config from the database as string + // Get the manifest from the database as string { - char *config_str = NULL; + char *manifest_str = NULL; enum GNUNET_DB_QueryStatus qs; json_error_t err; - json_t *config; + json_t *manifest_js; enum GNUNET_GenericReturnValue ret; - qs = TEH_plugin->get_extension_config (TEH_plugin->cls, - extension->name, - &config_str); + qs = TEH_plugin->get_extension_manifest (TEH_plugin->cls, + extension->name, + &manifest_str); if (qs < 0) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Couldn't get extension config\n"); + "Couldn't get extension manifest\n"); GNUNET_break (0); return; } // No config found -> disable extension - if (NULL == config_str) + if (NULL == manifest_str) { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No manifest found for extension %s, disabling it\n", + extension->name); extension->disable ((struct TALER_Extension *) extension); return; } // Parse the string as JSON - config = json_loads (config_str, JSON_DECODE_ANY, &err); - if (NULL == config) + manifest_js = json_loads (manifest_str, JSON_DECODE_ANY, &err); + if (NULL == manifest_js) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse config for extension `%s' as JSON: %s (%s)\n", + "Failed to parse manifest for extension `%s' as JSON: %s (%s)\n", extension->name, err.text, err.source); GNUNET_break (0); + free (manifest_js); return; } // Call the parser for the extension - ret = extension->load_json_config ( + ret = extension->load_config ( (struct TALER_Extension *) extension, - config); + json_object_get (manifest_js, "config")); if (GNUNET_OK != ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Couldn't parse configuration for extension %s from the database", - extension->name); + "Couldn't parse configuration for extension %s from the manifest in the database: %s\n", + extension->name, + manifest_str); GNUNET_break (0); } + + free (manifest_str); + json_decref (manifest_js); } /* Special case age restriction: Update global flag and mask */ if (TALER_Extension_AgeRestriction == type) { - TEH_age_restriction_enabled = - TALER_extensions_age_restriction_is_enabled (); + const struct TALER_AgeRestrictionConfig *conf = + TALER_extensions_get_age_restriction_config (); + TEH_age_restriction_enabled = false; + if (NULL != conf) + { + TEH_age_restriction_enabled = true; + TEH_age_restriction_config = *conf; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "[age restriction] DB event has changed the config to %s with mask: %s\n", + TEH_age_restriction_enabled ? "enabled": "DISABLED", + TALER_age_mask_to_string (&conf->mask)); + } } @@ -143,14 +162,30 @@ extension_update_event_cb (void *cls, enum GNUNET_GenericReturnValue TEH_extensions_init () { - GNUNET_assert (GNUNET_OK == - TALER_extension_age_restriction_register ()); - /* Set the event handler for updates */ struct GNUNET_DB_EventHeaderP ev = { .size = htons (sizeof (ev)), .type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED), }; + + /* Load the shared libraries first */ + if (GNUNET_OK != + TALER_extensions_init (TEH_cfg)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "failed to load extensions"); + return GNUNET_SYSERR; + } + + /* Check for age restriction */ + { + const struct TALER_AgeRestrictionConfig *arc; + + if (NULL != + (arc = TALER_extensions_get_age_restriction_config ())) + TEH_age_restriction_config = *arc; + } + extensions_eh = TEH_plugin->event_listen (TEH_plugin->cls, GNUNET_TIME_UNIT_FOREVER_REL, &ev, @@ -162,17 +197,24 @@ TEH_extensions_init () return GNUNET_SYSERR; } - /* FIXME #7270: shall we load the extensions from the config right away? - * We do have to for now, as otherwise denominations with age restriction - * will not have the age mask set right upon initial generation. - */ - TALER_extensions_load_taler_config (TEH_cfg); - /* Trigger the initial load of configuration from the db */ - for (const struct TALER_Extension *it = TALER_extensions_get_head (); - NULL != it->next; + for (const struct TALER_Extensions *it = TALER_extensions_get_head (); + NULL != it && NULL != it->extension; it = it->next) - extension_update_event_cb (NULL, &it->type, sizeof(it->type)); + { + const struct TALER_Extension *ext = it->extension; + uint32_t typ = htonl (ext->type); + char *manifest = json_dumps (ext->manifest (ext), JSON_COMPACT); + + TEH_plugin->set_extension_manifest (TEH_plugin->cls, + ext->name, + manifest); + + extension_update_event_cb (NULL, + &typ, + sizeof(typ)); + free (manifest); + } return GNUNET_OK; } @@ -190,4 +232,168 @@ TEH_extensions_done () } +/* + * @brief Execute database transactions for /extensions/policy_* POST requests. + * + * @param cls a `struct TALER_PolicyFulfillmentOutcome` + * @param connection MHD request context + * @param[out] mhd_ret set to MHD status on error + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +policy_fulfillment_transaction ( + void *cls, + struct MHD_Connection *connection, + MHD_RESULT *mhd_ret) +{ + struct TALER_PolicyFulfillmentTransactionData *fulfillment = cls; + + return TEH_plugin->add_policy_fulfillment_proof (TEH_plugin->cls, + fulfillment); +} + + +MHD_RESULT +TEH_extensions_post_handler ( + struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[]) +{ + const struct TALER_Extension *ext = NULL; + json_t *output; + struct TALER_PolicyDetails *policy_details = NULL; + size_t policy_details_count = 0; + + + if (NULL == args[0]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + "/extensions/$EXTENSION"); + } + + ext = TALER_extensions_get_by_name (args[0]); + if (NULL == ext) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + "/extensions/$EXTENSION unknown"); + } + + if (NULL == ext->policy_post_handler) + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_IMPLEMENTED, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + "POST /extensions/$EXTENSION not supported"); + + /* Extract hash_codes and retrieve related policy_details from the DB */ + { + enum GNUNET_GenericReturnValue ret; + enum GNUNET_DB_QueryStatus qs; + const char *error_msg; + struct GNUNET_HashCode *hcs; + size_t len; + json_t*val; + size_t idx; + json_t *jhash_codes = json_object_get (root, + "policy_hash_codes"); + if (! json_is_array (jhash_codes)) + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + "policy_hash_codes are missing"); + + len = json_array_size (jhash_codes); + hcs = GNUNET_new_array (len, + struct GNUNET_HashCode); + policy_details = GNUNET_new_array (len, + struct TALER_PolicyDetails); + + json_array_foreach (jhash_codes, idx, val) + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, &hcs[idx]), + GNUNET_JSON_spec_end () + }; + + ret = GNUNET_JSON_parse (val, + spec, + &error_msg, + NULL); + if (GNUNET_OK != ret) + break; + + qs = TEH_plugin->get_policy_details (TEH_plugin->cls, + &hcs[idx], + &policy_details[idx]); + if (qs < 0) + { + error_msg = "a policy_hash_code couldn't be found"; + break; + } + } + + GNUNET_free (hcs); + if (GNUNET_OK != ret) + { + GNUNET_free (policy_details); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + error_msg); + } + } + + + { + enum GNUNET_GenericReturnValue ret; + + ret = ext->policy_post_handler (root, + &args[1], + policy_details, + policy_details_count, + &output); + + if (GNUNET_OK != ret) + { + TALER_MHD_reply_json_steal ( + rc->connection, + output, + MHD_HTTP_BAD_REQUEST); + } + + /* execute fulfillment transaction */ + { + MHD_RESULT mhd_ret; + struct TALER_PolicyFulfillmentTransactionData fulfillment = { + .proof = root, + .timestamp = GNUNET_TIME_timestamp_get (), + .details = policy_details, + .details_count = policy_details_count + }; + + if (GNUNET_OK != + TEH_DB_run_transaction (rc->connection, + "execute policy fulfillment", + TEH_MT_REQUEST_POLICY_FULFILLMENT, + &mhd_ret, + &policy_fulfillment_transaction, + &fulfillment)) + { + json_decref (output); + return mhd_ret; + } + } + } + + return TALER_MHD_reply_json_steal (rc->connection, + output, + MHD_HTTP_OK); +} + + /* end of taler-exchange-httpd_extensions.c */ diff --git a/src/exchange/taler-exchange-httpd_extensions.h b/src/exchange/taler-exchange-httpd_extensions.h index 4659b653e..e435f8f03 100644 --- a/src/exchange/taler-exchange-httpd_extensions.h +++ b/src/exchange/taler-exchange-httpd_extensions.h @@ -40,4 +40,19 @@ TEH_extensions_init (void); void TEH_extensions_done (void); + +/** + * Handle POST "/extensions/..." requests. + * + * @param rc request context + * @param root uploaded JSON data + * @param args array of additional options + * @return MHD result code + */ +MHD_RESULT +TEH_extensions_post_handler ( + struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[]); + #endif diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 9b7f28bd0..30c336539 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -17,6 +17,7 @@ * @file taler-exchange-httpd_keys.c * @brief management of our various keys * @author Christian Grothoff + * @author Özgür Kesim */ #include "platform.h" #include "taler_json_lib.h" @@ -815,10 +816,7 @@ static struct TALER_AgeMask load_age_mask (const char*section_name) { static const struct TALER_AgeMask null_mask = {0}; - struct TALER_AgeMask age_mask = TALER_extensions_age_restriction_ageMask (); - - if (age_mask.bits == 0) - return null_mask; + enum GNUNET_GenericReturnValue ret; if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( TEH_cfg, @@ -826,22 +824,29 @@ load_age_mask (const char*section_name) "AGE_RESTRICTED"))) return null_mask; + if (GNUNET_SYSERR == + (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, + section_name, + "AGE_RESTRICTED"))) { - enum GNUNET_GenericReturnValue ret; + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + section_name, + "AGE_RESTRICTED", + "Value must be YES or NO\n"); + return null_mask; + } - if (GNUNET_SYSERR == - (ret = GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg, - section_name, - "AGE_RESTRICTED"))) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section_name, - "AGE_RESTRICTED", - "Value must be YES or NO\n"); - return null_mask; - } + if (GNUNET_OK == ret) + { + if (! TEH_age_restriction_enabled) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "age restriction set in section %s, yet, age restriction is not enabled\n", + section_name); + return TEH_age_restriction_config.mask; } - return age_mask; + + + return null_mask; } @@ -1898,41 +1903,29 @@ create_krd (struct TEH_KeyStateHandle *ksh, bool has_extensions = false; /* Fill in the configurations of the enabled extensions */ - for (const struct TALER_Extension *extension = TALER_extensions_get_head (); - NULL != extension; - extension = extension->next) + for (const struct TALER_Extensions *iter = TALER_extensions_get_head (); + NULL != iter && NULL != iter->extension; + iter = iter->next) { - json_t *ext; - json_t *config_json; + const struct TALER_Extension *extension = iter->extension; + json_t *manifest; int r; - /* skip if not configured == disabled */ - if (NULL == extension->config || - NULL == extension->config_json) + /* skip if not enabled */ + if (! extension->enabled) continue; /* flag our findings so far */ has_extensions = true; - GNUNET_assert (NULL != extension->config_json); - config_json = json_copy (extension->config_json); - GNUNET_assert (NULL != config_json); - - ext = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_bool ("critical", - extension->critical), - GNUNET_JSON_pack_string ("version", - extension->version), - GNUNET_JSON_pack_object_steal ("config", - config_json) - ); - GNUNET_assert (NULL != ext); + manifest = extension->manifest (extension); + GNUNET_assert (manifest); r = json_object_set_new ( extensions, extension->name, - ext); + manifest); GNUNET_assert (0 == r); } @@ -1948,12 +1941,16 @@ create_krd (struct TEH_KeyStateHandle *ksh, extensions); GNUNET_assert (0 == r); - sig = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("extensions_sig", - &TEH_extensions_sig)); + /* Add the signature of the extensions, if it is not zero */ + if (TEH_extensions_signed) + { + sig = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("extensions_sig", + &TEH_extensions_sig)); - r = json_object_update (keys, sig); - GNUNET_assert (0 == r); + r = json_object_update (keys, sig); + GNUNET_assert (0 == r); + } } else { diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c index a663b1b06..989b88fb6 100644 --- a/src/exchange/taler-exchange-httpd_management_extensions.c +++ b/src/exchange/taler-exchange-httpd_management_extensions.c @@ -38,7 +38,7 @@ struct Extension { enum TALER_Extension_Type type; - json_t *config; + json_t *manifest; }; /** @@ -52,7 +52,7 @@ struct SetExtensionsContext }; /** - * Function implementing database transaction to set the configuration of + * Function implementing database transaction to set the manifests of * extensions. It runs the transaction logic. * - IF it returns a non-error code, the transaction logic MUST NOT queue a * MHD response. @@ -74,13 +74,13 @@ set_extensions (void *cls, { struct SetExtensionsContext *sec = cls; - /* save the configurations of all extensions */ + /* save the manifests of all extensions */ for (uint32_t i = 0; i<sec->num_extensions; i++) { struct Extension *ext = &sec->extensions[i]; const struct TALER_Extension *taler_ext; enum GNUNET_DB_QueryStatus qs; - char *config; + char *manifest; taler_ext = TALER_extensions_get_by_type (ext->type); if (NULL == taler_ext) @@ -90,10 +90,8 @@ set_extensions (void *cls, return GNUNET_DB_STATUS_HARD_ERROR; } - GNUNET_assert (NULL != ext->config); - - config = json_dumps (ext->config, JSON_COMPACT | JSON_SORT_KEYS); - if (NULL == config) + manifest = json_dumps (ext->manifest, JSON_COMPACT | JSON_SORT_KEYS); + if (NULL == manifest) { GNUNET_break (0); *mhd_ret = TALER_MHD_reply_with_error (connection, @@ -103,10 +101,12 @@ set_extensions (void *cls, return GNUNET_DB_STATUS_HARD_ERROR; } - qs = TEH_plugin->set_extension_config ( + qs = TEH_plugin->set_extension_manifest ( TEH_plugin->cls, taler_ext->name, - config); + manifest); + + free (manifest); if (qs < 0) { @@ -137,6 +137,7 @@ set_extensions (void *cls, /* All extensions configured, update the signature */ TEH_extensions_sig = sec->extensions_sig; + TEH_extensions_signed = true; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, matters here */ } @@ -150,7 +151,7 @@ verify_extensions_from_json ( const char*name; const struct TALER_Extension *extension; size_t i = 0; - json_t *blob; + json_t *manifest; GNUNET_assert (NULL != extensions); GNUNET_assert (json_is_object (extensions)); @@ -159,7 +160,7 @@ verify_extensions_from_json ( sec->extensions = GNUNET_new_array (sec->num_extensions, struct Extension); - json_object_foreach (extensions, name, blob) + json_object_foreach (extensions, name, manifest) { int critical = 0; json_t *config; @@ -175,18 +176,18 @@ verify_extensions_from_json ( } if (GNUNET_OK != - TALER_extensions_is_json_config ( - blob, &critical, &version, &config)) + TALER_extensions_parse_manifest ( + manifest, &critical, &version, &config)) return GNUNET_SYSERR; if (critical != extension->critical || 0 != strcmp (version, extension->version) // FIXME-oec: libtool compare || NULL == config - || GNUNET_OK != extension->test_json_config (config)) + || GNUNET_OK != extension->load_config (NULL, config)) return GNUNET_SYSERR; sec->extensions[i].type = extension->type; - sec->extensions[i].config = config; + sec->extensions[i].manifest = json_copy (manifest); } return GNUNET_OK; @@ -223,7 +224,8 @@ TEH_handler_management_post_extensions ( } /* Ensure we have an object */ - if (! json_is_object (extensions)) + if ((! json_is_object (extensions)) && + (! json_is_null (extensions))) { GNUNET_JSON_parse_free (top_spec); return TALER_MHD_reply_with_error ( @@ -235,13 +237,13 @@ TEH_handler_management_post_extensions ( /* Verify the signature */ { - struct TALER_ExtensionConfigHashP h_config; + struct TALER_ExtensionManifestsHashP h_manifests; if (GNUNET_OK != - TALER_JSON_extensions_config_hash (extensions, &h_config) || + TALER_JSON_extensions_manifests_hash (extensions, &h_manifests) || GNUNET_OK != - TALER_exchange_offline_extension_config_hash_verify ( - &h_config, + TALER_exchange_offline_extension_manifests_hash_verify ( + &h_manifests, &TEH_master_public_key, &sec.extensions_sig)) { @@ -298,9 +300,9 @@ TEH_handler_management_post_extensions ( CLEANUP: for (unsigned int i = 0; i < sec.num_extensions; i++) { - if (NULL != sec.extensions[i].config) + if (NULL != sec.extensions[i].manifest) { - json_decref (sec.extensions[i].config); + json_decref (sec.extensions[i].manifest); } } GNUNET_free (sec.extensions); diff --git a/src/exchange/taler-exchange-httpd_metrics.h b/src/exchange/taler-exchange-httpd_metrics.h index c1b7326ff..cae371f7c 100644 --- a/src/exchange/taler-exchange-httpd_metrics.h +++ b/src/exchange/taler-exchange-httpd_metrics.h @@ -44,7 +44,8 @@ enum TEH_MetricTypeRequest TEH_MT_REQUEST_IDEMPOTENT_MELT = 10, TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 11, TEH_MT_REQUEST_BATCH_DEPOSIT = 12, - TEH_MT_REQUEST_COUNT = 13 /* MUST BE LAST! */ + TEH_MT_REQUEST_POLICY_FULFILLMENT = 13, + TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */ }; /** diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c index a25d6ff43..85090cedc 100644 --- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c +++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c @@ -623,8 +623,7 @@ resolve_refreshes_reveal_denominations ( bool failed = true; /* Has been checked in handle_refreshes_reveal_json() */ - GNUNET_assert (ng == - TALER_extensions_age_restriction_num_groups ()); + GNUNET_assert (ng == TEH_age_restriction_config.num_groups); rctx->old_age_commitment = GNUNET_new (struct TALER_AgeCommitment); oac = rctx->old_age_commitment; @@ -931,7 +930,7 @@ handle_refreshes_reveal_json (struct MHD_Connection *connection, /* Sanity check of age commitment: If it was provided, it _must_ be an array * of the size the # of age groups */ if (NULL != old_age_commitment_json - && TALER_extensions_age_restriction_num_groups () != + && TEH_age_restriction_config.num_groups != json_array_size (old_age_commitment_json)) { GNUNET_break_op (0); diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index a81d1b6e5..ca110ad45 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -76,7 +76,7 @@ TEH_RESPONSE_compile_transaction_history ( &h_wire, &deposit->h_contract_terms, &deposit->h_age_commitment, - NULL /* h_extensions! */, + &deposit->h_policy, &deposit->h_denom_pub, deposit->timestamp, &deposit->merchant_pub, |