summaryrefslogtreecommitdiff
path: root/src/exchange
diff options
context:
space:
mode:
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
commit752f10273860d2496fc3eb1e03de6ad4451e7c0f (patch)
tree53d51969f58611dbf8afacdcd40a769f5c847dd8 /src/exchange
parentc89bfa9026d7180eb24ae9480f225b93db22c53a (diff)
downloadexchange-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.c14
-rw-r--r--src/exchange/taler-exchange-httpd.h10
-rw-r--r--src/exchange/taler-exchange-httpd_batch-deposit.c121
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c102
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.c266
-rw-r--r--src/exchange/taler-exchange-httpd_extensions.h15
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c85
-rw-r--r--src/exchange/taler-exchange-httpd_management_extensions.c48
-rw-r--r--src/exchange/taler-exchange-httpd_metrics.h3
-rw-r--r--src/exchange/taler-exchange-httpd_refreshes_reveal.c5
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c2
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,